Introduction
Many times, I met situations where I want the user to choose a folder in the computer. Sometimes,
even a special folder like Printer folder or My Computer. Some guys do it by sub-classing the common
File Dialog. That's a way to do it, but not much neat to me. Actually, there are several Windows shell functions
specific to do this job for us. After digging for some time, I'd like to share my experience
with other coders in case you don't know how to do it.
Details
After you read through my demo code, you will agree that this is a convenient and small
piece of code if you don't want too many special functionalities from it.
First, the function SHBrowseForFolder(LPBROWSEINFO lpbi)
is the core part for browsing folders.
It accepts one parameter which is a pointer to the structure BROWSEINFO
. There're eight
domains in this structure that you can use to customize your folder browser. However, we'll only use
a few of them. That's enough for most programmers.
This is the block of code I used to bring out the browser.
void CFolderBrowserDlg::OnButtonBrowse()
{
LPMALLOC pMalloc = NULL;
LPITEMIDLIST pidl = NULL;
BROWSEINFO bi;
ZeroMemory(&bi, sizeof(BROWSEINFO));
bi.hwndOwner = m_hWnd;
bi.lpszTitle = _T("Current folder is:");
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
bi.lpfn = BrowseCallbackProc;
pidl = SHBrowseForFolder(&bi);
if(pidl != NULL)
{
SHGetPathFromIDList(pidl,
m_strFolderPath.GetBuffer(
m_strFolderPath.GetLength()));
UpdateData(FALSE);
if(SUCCEEDED(SHGetMalloc(&pMalloc)) && pMalloc);
pMalloc->Free(pidl);
pMalloc->Release();
}
}
First, we have to set hwndOwner
to the handle of the main dialog. This is the only
value we have to set. Then, we can set lpszTitle
which is the
string appearing above the tree
view control of folders. ulFlags
is a very important domain of BROWSEINFO
. By
setting the corresponding flag bits, we can get a bunch of functionalities. Here, we set
it to BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT
. BIF_RETURNONLYFSDIRS
means the OK
button will be enabled only when the user choose a file system folder instead of a special folder
like "My Computer". Most of time, that's what we want. BIF_STATUSTEXT
lets the dialog to
have a label showing the folder you have chosen. You have to deal with this label in your callback function,
which is set in the lpfn
domain. It points to a callback function whose prototype is like
int CALLBACK BrowseCallbackProc(HWND hwnd,
UINT uMsg,
LPARAM lParam,
LPARAM dwData);
uMsg
is just the code of message it receives. lParam
is message dependent.
And dwData
is user-defined data and it may not be used for some messages. There're three kind of
messages it can receive: BFFM_INITIALIZED
, BFFM_SELCHANGED
and BFFM_VALIDATEFAILED
. Usually, we only process one of them BFFM_SELCHANGED
(if you like,
even this one is not necessary to be processed.). Here is my code for this function.
int CALLBACK BrowseCallbackProc(
HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
switch(uMsg)
{
case BFFM_INITIALIZED:
{
}
break;
case BFFM_SELCHANGED:
{
TCHAR szText[MAX_PATH] = {0};
SHGetPathFromIDList(
reinterpret_cast<LPITEMIDLIST>(lParam), szText);
SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0,
reinterpret_cast<LPARAM>(szText));
}
break;
}
return 0;
}
For message BFFM_SELCHANGED
which means the user selects another folder, lParam
is of type LPITEMIDLIST
. By using function SHGetPathFromIDList(...)
, you can get the
selected folder's path and save it to szText
. Then, I send the browser
a message BFFM_SETSTATUSTEXT
to let it set the text in the label. Note
that BFFM_SETSTATUSTEXT
is the message that the callback function can send, but
does not receive.
The return value of SHBrowseForFolder(...)
is of type LPITEMIDLIST
. If
the user pressed OK button, you can get the folder's path by the same method I mentioned above. In my code,
I save it to my member variable m_strFolderPath
and update my main demo dialog. Finally, don't forget
to release the memory your program has allocated. This is what LPMALLOC pMalloc
is here for.
Pretty short code, isn't it? You can easily embed it into your program. Furthermore, there're many
more functionalities you can find with SHBrowseForFolder(...)
. For example,
- When your callback function processes message
BFFM_INITIALIZED
,
you can add a 3-D edge to the text label. (But this function is not guaranteed in the future version of Windows.)
- You can add a text field in the browser so that the user can input a folder by typing.
Then, you can also check the validity of the user's input.
- With this function, you can even get the handle of icon of the selected folder.
- You can set the root folder of your browser so that users cannot browse folders outside that one.
- And so on...
There're many more interesting stuff you can dig into with this function. But in most cases, my code
should be enough to do the work. For further knowledge about it, I recommend you to
read "Visual C++ Windows Shell Programming" by Dino Esposito which is one of very few good books
on Windows Shell Programming.