C++ how to get folders and files names from virtual device? - c++

How to get all folders and files names in virtual device plugged to pc?
In C++;
I have problem because the virtual device not have a letter like local disc;
Path started like this "Computer\SUPRA_M727G\Internal storage"

You have to use Shell interfaces, namely IShellFolder and IEnumIDList.
To get an IShellFolder for the "Internal storage" folder, you can either:
Use SHGetDesktopFolder() to get an IShellFolder for the root of the Shell namespace, then pass the "Computer\SUPRA_M727G\Internal storage" string to its IShellFolder::ParseDisplayName() method to get an absolute ITEMIDLIST that you can pass to its IShellFolder::BindToObject() method.
Use SHParseDisplayName() or ILCreateFromPath() to convert the "Computer\SUPRA_M727G\Internal storage" string into an absolute ITEMIDLIST that you can pass to SHBindToObject() with its psf parameter set to NULL.
Either way, once you have an IShellFolder for the storage folder, you can use its IShellFolder::EnumObjects() method to get an IEnumIDList for enumerating its files and subfolders. The enumeration will give you a relative ITEMIDLIST for each item. To retrieve each item's name as a string, pass each ITEMIDLIST to its IShellFolder::GetDisplayNameOf() method, and then pass the returned STRRET to one of the StrRetTo...() functions (StrRetToBSTR(), StrRetToBuf(), or StrRetToStr()).
Refer to MSDN for more details:
Introduction to the Shell Namespace
Navigating the Shell Namespace

Related

Get the specific child of IShellItem assuming IShellItem is a directory?

Using IKnownFolderManager and IKnownFolder I managed to get the path to C:\Program Files and its corresponding IShellItem. After that I managed to create a directory in that location using IFileOperation. Now, I want to create a file in the new directory. Since IFileOperation needs IShellItem, how do I get the corresponding IShellItem of the new directory?
PS: I'm new to COM and Win32 API.
When using IFileOperation::NewItem(), you can use an IFileOperationProgressSink object, its PostNewItem() event will give you the IShellItem of the new item you create. You can either pass the sink object to NewItem() directly, or pass it to IFileOperation::Advise() before calling IFileOperation::PerformOperations().
Alternatively, you can use SHCreateItemFromRelativeName() instead, after PerformOperations() is successful.

How to rename a printer in C++ on Windows?

In WinSpool.h I can enumerate all the printers using the EnumPrinters function, but I cannot find out how to rename an existing printer?
You can use the SetPrinter function to rename a printer.
You would set the Level parameter to 2 and the pPrinter parameter would point to a PRINTER_INFO_2 structure. The PRINTER_INFO_2::pPrinterName field lets you change the printer name.
Note that the docs for SetPrinter say:
To modify the current printer settings, call the GetPrinter function
to retrieve the current settings into a PRINTER_INFO_2 structure,
modify the members of that structure as necessary, and then call
SetPrinter.
So you would need to call GetPrinter with a properly initialized structure first, before modifying the name and calling SetPrinter.

How to tell if a folder is a subfolder of a special Windows folder?

If I have a CSIDL (or its newer alternative KNOWNFOLDERID) for a special folder (for the sake of this example, let's assume My Documents folder) and a DOS folder path, is there any way to tell that the path refers to a subfolder within the special folder?
EDIT 1: I implemented the following method after #RemyLebeau's suggestion, but it always sets my nIsParent to 0, or not a parent. What am I missing there?
int nCSIDL = CSIDL_PERSONAL;
LPCTSTR pDosPath = L"C:\\Users\\UserName\\Documents\\Subfolder1\\File.txt";
int nIsParent = -1; //-1=error, 0=no, 1=yes
LPITEMIDLIST pidlDocuments = NULL;
if(SUCCEEDED(SHGetFolderLocation(NULL, nCSIDL, NULL, 0, &pidlDocuments)))
{
LPITEMIDLIST pidl = ILCreateFromPath(pDosPath);
if(pidl)
{
nIsParent = ILIsParent(pidlDocuments, pidl, FALSE) ? 1 : 0;
ILFree(pidl);
}
ILFree(pidlDocuments);
}
EDIT 2: As for his 2nd suggestion to use SHGetPathFromIDList and then PathRelativePathTo on both DOS paths, it won't work for the following: My Documents on my computer is redirected to "\\SRVR-A\Home\UserName\Documents", which is also the "R:\Documents" folder with drive R: mapped to that Home share. PathRelativePathTo fails on those paths.
EDIT 3: If I had a folder Test folder in My Documents I could do this using my mapped drive R::
subst S: "R:\Documents\Test folder"
Which will technically make folder "S:\Test folder" a parent of My Documents as well, which is "\\SRVR-A\Home\UserName\Documents\Test folder".
That is why I was looking for a Shell-only, or a single API solution.
Everything in the Shell is represented by the ITEMIDLIST structure, even file system paths. Retrieve the ITEMIDLIST of the special folder using SHGetFolderLocation() or SHGetKnownFolderIDList(), then retrieve the ITEMIDLIST of the DOS path using SHParseDisplayName() or ILCreateFromPath(), then use ILIsParent() to check if the special folder's ITEMIDLIST is a parent of the DOS path's ITEMIDLIST.
Alternatively, retrieve the special folder's path using SHGetFolderPath() or SHGetKnownFolderPath(), then use PathRelativePathTo to check if the DOS path can be represented as a relative subfolder of the special folder's path without using any ".." components.
Create a function that gets a full path, name of the special folder, and just call
strstr on the full path with the name of the special folder and if it does not return NULL then it is a subfolder.
As for an API for it, I'm not aware of something like that but it could be possible.

How to get the AppData path

SHGetSpecialFolderPathA(NULL,buffer, CSIDL_APPDATA,FALSE );
C:\Users\guest\AppData\Roaming
SHGetSpecialFolderPathA(NULL,buffer, CSIDL_LOCAL_APPDATA,FALSE );
C:\Users\guest\AppData\Local
Is there way to get the path C:\Users\guest\AppData using windows API's?
The roaming and local folders exist for a reason, sometimes you might need to put something in the root of the profile but you are not really supposed to do it. This is what MSDN says about CSIDL_PROFILE:
Applications should not create files or folders at this level; they
should put their data under the locations referred to by CSIDL_APPDATA
or CSIDL_LOCAL_APPDATA. However, if you are creating a new Known
Folder the profile root referred to by CSIDL_PROFILE is appropriate.
On NT5 they don't even have the same parent folder and "Roaming" is in the root of the profile:
C:\Documents and Settings\username\Application Data
C:\Documents and Settings\username\Local Settings\Application Data
The user and/or domain admin can move and/or redirect those folders to anywhere, to the root of a different drive or a network share.
The only documented way I can think of to find the parent is to use IKnownFolderManager::GetFolder and then call IKnownFolder::GetFolderDefinition and look at KNOWNFOLDER_DEFINITION.fidParent (Keep in mind that there does not have to be a parent, IKnownFolderManager::Redirect takes a string as the target so a redirected folder can be anywhere)
If you want to exclude files under a special shell folder you should compare the path with something like PathCommonPrefix or IKnownFolderManager::FindFolderFromPath.

How to properly use %USERPROFILE% inside code?

Is my code correct? It seems can compile but does not work properly..
CString testing = _T(" --url=") + cstring + _T(" --out=%USERPROFILE%\\snapshot.png");
I want to point it to user's folder..but still cannot work.
The answer is that you don't use environment variables at all. Rather, you use the shell functions specifically designed to retrieve the path of special folders.
On Windows Vista and later, that function is SHGetKnownFolderPath. It takes KNOWNFOLDERID values to identify the folder whose path you wish to retrieve. In your case, that would be FOLDERID_Profile.
If you need to target earlier versions of Windows (such as XP), you will need to use the SHGetSpecialFolderPath function, instead. It takes a CSIDL value identifying the folder whose path you wish to retrieve. Again, in your case, that would be CSIDL_PROFILE.
Of course, you should never store data directly in the user's profile folder. So hopefully the bit of code that you've shown is for demonstration purposes only. Applications should only create files in the specific locations under the user profile folder, designed for application data storage.
These locations are CSIDL_APPDATA or CSIDL_LOCAL_APPDATA. If you are creating data that the user should be able to modify and should treat as his/her own, then it would be appropriate to store that data in the user's documents folder (CSIDL_MYDOCUMENTS).
More usage information is available in my answer here.
Sample code:
TCHAR szFolderPath[MAX_PATH];
if (!SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_APPDATA, FALSE))
{
// Uh-oh! An error occurred; handle it.
}
Or, using MFC's CString class:
CString buffer;
BOOL bRet = SHGetSpecialFolderPath(NULL, buffer.GetBuffer(MAX_PATH), CSIDL_APPDATA, FALSE);
buffer.ReleaseBuffer();
if (!bRet)
{
// Uh-oh! An error occurred; handle it.
}
As Cody suggested, it's better to use the SHGetSpecialFolderPath function. However, you could use the GetEnvironmentVariable function to get that and other variables set in the system.
TCHAR szBuf[MAX_PATH] = {0};
::GetEnvironmentVariable(_T( "USERPROFILE" ), szBuf, MAX_PATH);