A customer reported that they had been setting the lpstrInitialDir
member of the OPENFILENAME
structure to "::{645FF040-5081-101B-9F08-00AA002F954E}"
in order to have the dialog’s default directory be the Recycle Bin. (I am not making this up.) They reported that this stopped working in Windows Vista and wanted to know why, and what they can do about it.
The lpstrInitialDir
is supposed to be the name of a directory. Virtual folders are not directories. It so happened that virtual directories were accepted in Windows XP due to a bug: The file dialog uses the PathCombine
function¹ to combine the current directory with the lpstrInitialDir
. The PathCombine
function checks whether the second parameter (lpstrInitialDir
) is an absolute or relative path, and in Windows XP it had a bug that if the second parameter’s second character was a colon, then it assumed that the path was absolute. It did this without verifying that the first character was a letter.
Windows Vista fixed this bug in the PathCombine
function, which means that if you passed an lpstrInitialDir
of "::{...}"
, the function said, “Oh, you almost fooled me there, but I’m onto you. That is not an absolute path. So I will combine it with the first parameter (the current directory).” The file dialog then says, “Let’s go to that folder!”, but it can’t (because there is no folder called ::{...}
in the current directory), so it falls back to the Documents folder.
That explains why the undefined behavior changed. But what’s the supported way of setting the initial directory to a virtual directory?
You use the IFileDialog
interface, which gives you more control over the file open dialog than OPENFILENAME
does. In particular, you can use the IFileDialog::
SetDefaultFolder
method.
Here’s a Little Program to demonstrate. Remember that Little Programs do little to no error checking. Today’s smart pointer library is (rolls dice) nothing! We’re going to use raw pointers.
#define STRICT #define STRICT_TYPED_ITEMIDS #include <windows.h> #include <shlobj.h> #include <knownfolders.h> int WINAPI WinMain( HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nShowCmd) { CCoInitialize init; IFileDialog* pfd; CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd)); IShellItem* psi; SHGetKnownFolderItem(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS(&psi)); pfd->SetDefaultFolder(psi); psi->Release(); // ... other initialization goes here ... pfd->Show(nullptr); // ... process the results ... pfd->Release(); return 0; }
We create the file open dialog and set its default folder to the Recycle Bin. In a real program, there would be additional initialization of the dialog, but in this Little Program, we’ll accept all the other defaults. We then show the dialog with no owner (because our program doesn’t have a main window). Observe that the default initial directory is the Recycle Bin.
¹ The PathCombine
function has been superseded by the PathCchCombine
function and its friends, which support NT-style paths and extra-long paths.
0 comments