Get path to My Documents - c++

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.

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 to use CopyFile for copying a particular type of files from one folder to another

How to use CopyFile for copying specific type of files from one folder to a backup file (backup.bkp)
Example:
C:\HHH
abc.jpeg
def.txt
ghi.jpeg
I want to copy only jpeg files to the backup.bkp file
I tried the below syntax but it is not working,
CopyFile( _T("C:\\HHH\*.jpeg"),_T("C:\\Backup.bak", FALSE);
Can anyone suggest a solution for this?
CopyFile doesn't accept wild cards or copy multiple files. It can copy a single file from one fully specified place to another.
To achieve what you want you need to enumerate over the directory using FindFirstFile/FindNextFile and copy the files one by one using CopyFile. These functions take wildcards or you can ask for all files, and do the filtering yourself.
Assuming the destination directory already exists.
CopyFile simply copy one file to another one. A quick solution consists in using the standard C system function to run the command to copy the files (using copy or xcopy for example), but that gives you less control.
So sticking with CopyFile, you need to list all *.jpeg files from the HHH directory first, then copy each of them into the destination directory.
You will also often need to split/concatenate paths during the processing, and though you can do that manually by using strcpy, strcat, etc. You've better to use dedicated functions like the splitpath and makepath family of functions or equivalents.
That leads us to following sample code:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <tchar.h>
int main(void) {
WIN32_FIND_DATA File;
HANDLE hSearch;
TCHAR SourcePath[_MAX_PATH];
TCHAR SourceDrive[_MAX_DRIVE];
TCHAR SourceDir[_MAX_DIR];
TCHAR SourceFname[_MAX_FNAME];
TCHAR SourceExt[_MAX_EXT];
TCHAR DestPath[_MAX_PATH];
TCHAR DestDrive[_MAX_DRIVE];
TCHAR DestDir[_MAX_DIR];
LPCTSTR lpszFindPattern = TEXT("C:\\HHH\\*.jpeg");
_tsplitpath_s(_T("C:\\HHH\\"), SourceDrive, _MAX_DRIVE, SourceDir, _MAX_DIR, NULL, 0, NULL, 0);
_tsplitpath_s(_T("C:\\Backup.bak\\"), DestDrive, _MAX_DRIVE, DestDir, _MAX_DIR, NULL, 0, NULL, 0);
hSearch = FindFirstFile(lpszFindPattern, &File);
if (hSearch != INVALID_HANDLE_VALUE) {
do {
_tsplitpath_s(File.cFileName, NULL, 0, NULL, 0, SourceFname, _MAX_FNAME, SourceExt, _MAX_EXT);
_tmakepath_s(SourcePath, _MAX_PATH, SourceDrive, SourceDir, SourceFname, SourceExt);
_tmakepath_s(DestPath, _MAX_PATH, DestDrive, DestDir, SourceFname, SourceExt);
CopyFile(SourcePath, DestPath, FALSE);
} while (FindNextFile(hSearch, &File));
FindClose(hSearch);
}
return 0;
}
CopyFile() does not support what you are asking for. You would have to manually loop through the source directory locating the desired files and then copy them one at a time.
A better solution is to let the OS do the work for you. You can use the SHFileOperation() function for that:
SHFILEOPSTRUCT op = {0};
op.hwnd = hwnd;
op.wFunc = FO_COPY;
op.pFrom = TEXT("C:\\HHH\\*.jpeg\0"); // note the extra null terminator!
op.pTo = TEXT("C:\\Backup.bak\\\0"); // note the extra null terminator!
op.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NORECURSION | FOF_SILENT;
if (SHFileOperation(&op) != 0)
{
// error...
}
else
{
if (op.fAnyOperationsAborted)
{
// not all files copied...
}
}
On Vista and later, SHFileOperation() has been deprecated by IFileOperation, but unfortunately it does not support wildcards at all, so you would have to enumerate the files manually first, passing each file to IFileOperation::CopyItem(), or putting them in an array that you can then pass to IFileOperation::CopyItems(), and then finally call IFileOperation::PerformOperations() to perform the actual copy.

Creating folders in My Documents

I'm having trouble creating a directory structure in the Windows My Documents directory. I use
ExpandEnvironmentStrings(L"%USERPROFILE%\\Documents",dir,MAX_PATH);
to get the directory then I create a new Directory in there
CreateDirectoryW(dir,NULL)
then in there I want to create another directory so in essence i want Documents\foo\bar however when I go to the foo directory via the Library on explorer side bar 'bar' isn't found unless I go to C:\users\xxx\Documents\foo then its there. Also if I go to Libraries\Documents\foo and right click->New->Folder isn't an option.
I was wondering if there is a Security Option to CreateDirectory I'm supposed to use or what I'm doing wrong
If you want to create a directory tree, you can use SHCreateDirectoryEx. The following code works well on my computer.
#include <iostream>
#include <Windows.h>
#include <Shlobj.h>
#include <Shlwapi.h>
int main()
{
char path[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, path)))
{
PathAppend(path, "foo\\bar");
if (SHCreateDirectoryEx(NULL, path, NULL) != ERROR_SUCCESS)
{
std::cout << "Error: " << GetLastError();
}
}
else
{
std::cout << "Error: " << GetLastError();
}
}
Notice that this works only on Windows 2000 Professional or higher.

How to get Windows path using Qt/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;

C++ Windows - How to get process path from its PID

How can I retrieve a process's fully-qualified path from its PID using C++ on Windows?
Call OpenProcess to get a handle to the process associated with your PID. Once you have a handle to the process, call GetModuleFileNameEx to get its fully-qualified path. Don't forget to call CloseHandle when you're finished using the process handle.
Here's a sample program that performs the required calls (replace 1234 with your PID):
#include <windows.h>
#include <psapi.h> // For access to GetModuleFileNameEx
#include <tchar.h>
#include <iostream>
using namespace std;
#ifdef _UNICODE
#define tcout wcout
#define tcerr wcerr
#else
#define tcout cout
#define tcerr cerr
#endif
int _tmain(int argc, TCHAR * argv[])
{
HANDLE processHandle = NULL;
TCHAR filename[MAX_PATH];
processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, 1234);
if (processHandle != NULL) {
if (GetModuleFileNameEx(processHandle, NULL, filename, MAX_PATH) == 0) {
tcerr << "Failed to get module filename." << endl;
} else {
tcout << "Module filename is: " << filename << endl;
}
CloseHandle(processHandle);
} else {
tcerr << "Failed to open process." << endl;
}
return 0;
}
Some notes to Emerick Rogul's solution:
Don't forget to add 'psapi.lib' to linker (additional dependencies).
I also changed PROCESS_ALL_ACCESS to PROCESS_QUERY_INFORMATION | PROCESS_VM_READ because I got:
Failed to open process.
If it's compiled as a 32 bit application it will fail to get the name of 64 bit processes ("Failed to get module filename.")
I didn't have very much luck with GetModuleFileNameEx and QueryFullProcessImageName is only available on Vista or higher. I was however able to get the path for a process by using GetProcessImageFilename. It returns the windows kernel path but you can use QueryDosDevice to compare the device path returned by GetProcessImageFilename with its proper drive path.
This page shows how to normalize an windows kernel path returned by GetProcessImageFilename (see NormalizeNTPath function):
http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/c48bcfb3-5326-479b-8c95-81dc742292ab/
Have you tried QueryFullProcessImageName?
Sometimes GetModuleFileNameEx returns the 299 error code (I don't know why)
The only method that works for all versions of Windows, including XP is in Nathan Moinvaziri answer:
check the provided url:
Windows API to Get a Full Process Path