How to get Windows path using Qt/C++ - c++

I am trying to get the windows path using Qt and C++. The below code compiles, but not gettting the windows folder path in Qt. The same code works in Visual Studio 2010
wchar_t path[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_WINDOWS, NULL, 0, path);
The below code change seems working:
int const bufferSize = 512;
QScopedPointer<WCHAR> dirPath(new WCHAR[bufferSize]);
ZeroMemory( dirPath.operator ->(), bufferSize);
SHGetFolderPath(NULL, CSIDL_WINDOWS, NULL, 0, dirPath.operator ->());

There isn't a Qt function to do this, but what you are asking could be achieved by reading the environtment variable WINDIR:
QStringList env_list(QProcess::systemEnvironment());
int idx = env_list.indexOf(QRegExp("^WINDIR=.*", Qt::CaseInsensitive));
if (idx > -1)
{
QStringList windir = env_list[idx].split('=');
qDebug() << "Var : " << windir[0];
qDebug() << "Path: " << windir[1];
}
Outputs:
Var : "WINDIR"
Path: "C:\WINDOWS"

QString windowsInstallPath;
#ifdef Q_WS_WIN
QDir d;
if (d.cd("%windir%"))
windowsInstallPath = d.absolutePath();
#endif
if (!windowsInstallPath.isNull())
qDebug() << windowsInstallPath;
else
qDebug() << "Not compiled for Windows";
Should work.

I think another very reasonable way to get the Windows directory would be to get it from the environment passed to the program:
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
qDebug() << env.value("windir");
https://doc.qt.io/qt-5/qprocessenvironment.html

I don't think there is a specific Qt function to do this.
The nearest is QSysinfo which tells you the windows version. However SHGetFolderPath() shoudl work in Qt just as well as any other win API call.
ps In Windows vista-> this is replaced with SHGetKnownFolderPath

Here is a one line solution:
QString winPath = QString::fromUtf8(qgetenv("windir"));
This can also be used for any environment variable. I am not sure if qgetenv is available in Qt4 but it is in Qt5.

If your application is not Terminal Services aware, you may get a different directory under TS environment. Found this out myself today, not that I've ever been bit by %windir% or %SystemRoot% or using the ShGetKnownFolderPath or GetWindowsDirectory APIs.
I've opted for using GetSystemWindowsDirectory which exists Windows 2000 and upward. Microsoft's page for the function is here.
Further explanation by Raymond Chen is here.
Finally, the code...
It's written in Delphi 6. Sorry about that :) It's what I'm coding in at the moment, but if you have code for GetWindowsDirectory in your language, then just a few copy + renames are needed as the function signatures are identical. Note: this code is ANSI (...single byte chars in Delphi 6).
function GetSystemWindowsDirectoryA(lpBuffer: PAnsiChar; uSize: UINT): UINT; stdcall; external kernel32 name 'GetSystemWindowsDirectoryA';
function GetSystemWindowsDirectory: string;
var
buf: array[0..MAX_PATH] of Char;
resultLength: Cardinal;
begin
resultLength := GetSystemWindowsDirectoryA(#buf, SizeOf(buf));
if resultLength = 0 then
RaiseLastOSError;
SetLength(Result, resultLength);
Move(buf, PChar(Result)^, resultLength);
end;

Related

Is there a new replacement for SHGetSpecialFolderLocation?

My app is written in C++Builder for Win32. My code uses the SHGetSpecialFolderLocation() API to get the CSIDL_APPDATA and CSIDL_MYDOCUMENTS paths.
I noticed on Microsoft's website dated 12/04/2018 that it says:
[SHGetSpecialFolderLocation is not supported and may be altered or unavailable in the future. Instead, use SHGetFolderLocation.]
Then for SHGetFolderLocation it says:
Deprecated
What is the current way to get these two paths?
My current code is below.
LPITEMIDLIST List = NULL;
wchar_t wPath[MAX_PATH + 1];
UnicodeString S01, Fi;
if( !SHGetSpecialFolderLocation(0, CSIDL_APPDATA, &List) ){
if( SHGetPathFromIDListW(List, wPath ) ){
S01 = wPath;
Fi = (S01+"\\my_files\\");
Form1->MyRoamingPath_Mh = Fi;
}
}
SHGetSpecialFolderLocation() was first introduced in Windws 95/NT4. It was deprecated in Windows 2000/XP in favor of SHGetFolderLocation() (which returns a folder location as an IDLIST_ABSOLUTE) and SHGetFolderPath() (which returns a folder location as a path string).
So, in your example, you could have used SHGetFolderPath() instead:
#include <Shlobj.h>
#include <SysUtils.hpp>
wchar_t wPath[MAX_PATH + 1];
if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wPath) == S_OK)
{
Form1->MyRoamingPath_Mh = IncludeTrailingPathDelimiter(wPath) + L"my_files\\";
}
In Vista, use of CSIDL was deprecated in favor of KNOWNFOLDERID. The above functions have been deprecated in favor of SHGetKnownFolderIDList()/IKnownFolder::GetIDList() and SHGetKnownFolderPath()/IKnownFolder::GetPath(), respectively.
This is actually stated at the bottom of the SHGetFolderLocation() documentation 1:
1: I guess you did not scroll down far enough to see it.
Note As of Windows Vista, this function is merely a wrapper for SHGetKnownFolderIDList. The CSIDL value is translated to its associated KNOWNFOLDERID and SHGetKnownFolderIDList is called. New applications should use the known folder system rather than the older CSIDL system, which is supported only for backward compatibility.
So, in your example, you can now use SHGetKnownFolderPath() instead:
#include <Shlobj.h>
#include <SysUtils.hpp>
PWSTR pwPath;
if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, NULL, &pwPath) == S_OK)
{
try
{
Form1->MyRoamingPath_Mh = IncludeTrailingPathDelimiter(pwPath) + L"my_files\\";
}
__finally
{
CoTaskMemFree(pwPath);
}
}
For the "My Documents" folder, use FOLDERID_Documents.

How can I overcome silent failure to change the PATH environment variable in VC++ 2013?

My goal is to write code that prepends a directory to the existing value of the PATH environment variable and sets the PATH environment variable to the new value for the duration of the program so that DLLs can be loaded from the new directory as well as from directories previously on the path. The code only needs to run on Windows and only needs to be compiled with Visual C++ 2013 and 2015. (Ideally, I'd like to be able to compile with MinGW as well, but the fact that there's no equivalent to /DELAYLOAD means that that may not be possible.)
I found, after experimentation, that SetEnvironmentVariable[W] silently fails to change the value of PATH, but _putenv, _putenv_s, _wputenv, and _wputenv_s work correctly --- but only on VC++ 2015. On VC++ 2013, they fail silently, and leave the value of PATH unchanged (the same way that SetEnvironmentVariable fails silently on VC++ 2013). I have not found any references to this behavior anywhere that I have searched on the Internet.
I can put up with using _putenv rather than SetEnvironmentVariable, but I need to figure out a way to change the value of PATH dynamically in VS2013, even if it's different from the way that works in VS2015.
UPDATE:
Here's prependDirToEnvVarW() (and getPathFromEnvVarW(), which it calls). prependDirToEnvVarW() works correctly on VC++ 2015 but silently fails to change the path on VC++ 2013.
inline std::wstring getPathFromEnvVarW()
{
DWORD bufferSize = GetEnvironmentVariableW(L"PATH", NULL, 0);
std::wstring ret(bufferSize, wchar_t(0));
DWORD getPathEnv = GetEnvironmentVariableW(L"PATH", &ret[0], bufferSize);
return (getPathEnv ? ret : L"");
}
inline bool prependDirToEnvVarW(const std::wstring & wstrDir)
{
const std::wstring PATH_VAR_NAME = L"PATH";
std::wstring wstrPrevPath = getPathFromEnvVarW();
std::wstring wstrValue = wstrDir;
if (!wstrValue.empty() && wstrValue[wstrValue.length() - 1] != L';')
{
wstrValue += L";";
}
wstrValue += wstrPrevPath;
std::replace(wstrValue.begin(), wstrValue.end(), L'/', L'\\');
errno_t retVal = _wputenv_s(PATH_VAR_NAME.c_str(), wstrValue.c_str());
std::cout << "Attempted to change PATH to:\n" <<
std::string(wstrValue.cbegin(), wstrValue.cend()) << std::endl;
if (retVal == 0)
{
return true;
}
else
{
std::cout << "retVal is nonzero: " << retVal
<< " (compare to EINVAL == " << EINVAL << ")" << std::endl;
return false;
}
}
The premise is incorrect. The PATH is the last thing searched for DLLs.
Instead, you should be calling SetDllDirectory() or AddDllDirectory() . This sets the DLL search path so that your added path is searched second, right after the executable directory.
If you need finer control than that, you will need to specify a full pathname to the LoadLibrary() function.

Run a command line with c++

I have read several posts about running a command line in c++, but none of them does what I want : no display of the console in an external application when I use the dllfile.
My problem : I want to use a process that picks files from a database. For example : to get a file XYZ.xyz and copy it to the directory MyDirXYZ, I would use the command line
"MyDataGenerator XYZ.xyz C:\MyDirXYZ".
I use Visual studio
Let 's take the following example to clarify the issue, I am trying to create a directory with "mkdir" in C++ , without using CreateDirectory :
First method :
std::string lDirectory("c:\\MyDummyDir")
std::string lCmd("mkdir " + lDirectory);
system((lCmd).c_str());
It will work , however it displays the console when I run it with a dllfile on Excel .
Second Method ( convertToRightFormat() convert a char to a TChar)
std::string lDirectory("c:\\MyDummyDir")
std::string lCmd("mkdir " + lDirectory);
BOOL _status = TRUE;
DWORD _reply, _code;
STARTUPINFO _si = { sizeof(_si) };
PROCESS_INFORMATION _pi;
TCHAR *_cmd = NULL;
_cmd=convertToRightFormat(lCmd.c_str())
_status &= CreateProcess(NULL, _cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &_si, &_pi);
This method does not work(not even creating the folder) , and obviously it does not work either for the process "MyDataGenerator".
I tried to use the executable MKDIR.EXE and put it in the commandline. In that case, it works , but still does not work for MyDataGenerator.
Is there a generic to make all command lines work, without showing the console?
Thanks you.
#include <Windows.h> // FreeConsole, system
int main(int argc, char* argv[])
{
system("mkdir newdirectoryname");
FreeConsole();
}
This will result in cmd popping up for a split second though. It closes after that.
If thats not acceptable just target the windows subsystem instead of the console one and make sure to not draw the window.

How can I open a file inside of an exe inside of my c++ app

Question is I have this button that when clicked I would like it open a file for me inside of a specific executable.
I am a tad rusty on c++ and this is a legacy application using c++ 6.0
built on windows xp.....So any help would be greatly appreciated!
Here is my code cpp
void CJunkView::OnCadkeyButton()
{
CString fileToOpen = "C:\\Documents and Settings\\Administrator\\Desktop\\x.prt";
CString exePath = "C:\\CK19\\Ckwin.exe";
system ("start (exePath), (fileToOpen)");
}
When I click this button it returns this Windows cannot find 'exePath,'.Make sure you typed the name correctly and then try again.
You need to build a string that contains the entire system call and the pass the buffer of that string to system()
Edit:
In response to the comment by IInspectable we could just use the implicit conversion operator operator LPCTSTR()
void CJunkView::OnCadkeyButton()
{
CString fileToOpen = "C:\\Documents and settings\\Administrator\\Desktop\\x.prt";
CString exePath = "C:\\CK19\\Ckwin.exe";
CString cmd = "start " + exePath + ", " + fileToOpen;
system (cmd);
}

Get path to My Documents

From Visual C++, how do I get the path to the current user's My Documents folder?
Edit:
I have this:
TCHAR my_documents[MAX_PATH];
HRESULT result = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, my_documents);
However, result is coming back with a value of E_INVALIDARG. Any thoughts as to why this might be?
It depends on how old of a system you need compatibility with. For old systems, there's SHGetSpecialFolderPath. For somewhat newer systems, there's SHGetFolderPath. Starting with Vista, there's SHGetKnownFolderPath.
Here's some demo code that works, at least on my machine:
#include <windows.h>
#include <iostream>
#include <shlobj.h>
#pragma comment(lib, "shell32.lib")
int main() {
CHAR my_documents[MAX_PATH];
HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, my_documents);
if (result != S_OK)
std::cout << "Error: " << result << "\n";
else
std::cout << "Path: " << my_documents << "\n";
return 0;
}
Use the SHGetFolderPath Windows API function and request CSIDL_MYDOCUMENTS.
Using Visual Studio 2017 with an MFC application under Windows 10 I am using the following code snippet with SHGetKnownFolderPath function to get the current user's Documents folder:
#include <string> // include file for C++ native strings
// . . . other code.
PWSTR ppszPath; // variable to receive the path memory block pointer.
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &ppszPath);
std::wstring myPath;
if (SUCCEEDED(hr)) {
myPath = ppszPath; // make a local copy of the path
}
CoTaskMemFree(ppszPath); // free up the path memory block
Note that the documentation has this to say about the path variable usage and the path returned:
ppszPath [out]
Type: PWSTR*
When this method returns, contains the address of a pointer to a
null-terminated Unicode string that specifies the path of the known
folder. The calling process is responsible for freeing this resource
once it is no longer needed by calling CoTaskMemFree. The returned
path does not include a trailing backslash. For example, "C:\Users" is
returned rather than "C:\Users\".
For a list of the FOLDERID_ arguments possible see the MSDN article KNOWN_FOLDER_FLAG enumeration.
Note that CSIDL_PERSONAL will not return the desired folder if the user has changed the default save folder in the Win7 Documents library. To get the right folder, you need to use SHLoadLibraryFromKnownFolder to obtain the IShellLibrary for the Documents library, use IShellLibrary::GetDefaultSaveFolder to get the IShellItem for the library's default save folder, and finally use IShellItem::GetDisplayName to get the folder name.
std::string GetMyDocumentsFolderPath()
{
wchar_t Folder[1024];
HRESULT hr = SHGetFolderPathW(0, CSIDL_MYDOCUMENTS, 0, 0, Folder);
if (SUCCEEDED(hr))
{
char str[1024];
wcstombs(str, Folder, 1023);
return str;
}
else return "";
}
cout<<GetMyDocumentsFolderPath()<<endl;
how about this solution? Its working fine for me.