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.