Introduction
The Shell API function SHBrowseForFolder
displays a dialog box that enables the user to select a Shell folder. The dialog box allows the user to select any folder because its root folder is Desktop (which contains My Documents
, My Computer
and My Network Places
). Some applications need to restrict the user in the selection of folders. Restricting the selection for My Computer
or My Network Places
is easier. But restricting the user for a particular folder requires little manipulation.
Background
The function SHBrowseForFolder
takes a pointer to the structure BROWSEINFO
. This structure has eight members. One of the members, pidlRoot
, is a pointer to the structure ITEMIDLIST
. If pidlRoot
is NULL
, the root folder is Desktop. One can use pre-defined CSIDL
values such as CSIDL_MYDOCUMENTS
, CSIDL_NETWORK
, etc. If any other arbitrary folder is to be used as root folder, it should be appropriately converted to the structure ITEMIDLIST
.
The class CFoldersDialog
puts the whole code for SHBrowseForFolder
dialog. The function ConvertPathToLpItemIdList
converts an arbitrary folder into ITEMIDLIST
. This class can be used in MFC as well as SDK without making any changes. It can also be used in VC++.NET.
The function ConvertPathToLpItemIdList
takes an arbitrary folder as string
type input parameter. It first converts the string
to OLECHAR
. Then SHGetDesktopFolder
function retrieves IShellFolder
interface and calls the method ParseDisplayName
. This method puts the OLECHAR
form of root folder into ITEMIDLIST
structure.
Using the Code
The function BrowseFolder
is called from any other class by creating an object of CFoldersDialog
. It first uses the function ConvertPathToLpItemIdList
for converting string
to ITEMIDLIST
and then use its callback function BrowseCallbackProc
.
LPITEMIDLIST ConvertPathToLpItemIdList(const char *pszPath)
{
LPITEMIDLIST pidl = NULL;
LPSHELLFOLDER pDesktopFolder = NULL;
OLECHAR olePath[MAX_PATH];
ULONG chEaten;
HRESULT hr;
if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
{
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszPath, -1,
olePath, MAX_PATH);
hr = pDesktopFolder->ParseDisplayName(NULL, NULL,
olePath, &chEaten, &pidl, NULL);
pDesktopFolder->Release();
return pidl;
}
return NULL;
}
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
{
switch(uMsg)
{
case BFFM_SELCHANGED:
{
if (SHGetPathFromIDList((LPITEMIDLIST) lp, szDir))
SendMessage(hwnd,BFFM_SETSTATUSTEXT, 0, (LPARAM)szDir);
break;
}
case BFFM_INITIALIZED:
{
SendMessage(hwnd, BFFM_SETSELECTION, 1,(LPARAM) szDir);
}
break;
default:
break;
}
return 0;
}
int CFoldersDialog::BrowseFolder(HWND hWnd, CString RootPath)
{
BROWSEINFO bi;
TCHAR szDir[MAX_PATH];
LPITEMIDLIST pidl;
LPMALLOC pMalloc;
int nRet = 0;
if (SUCCEEDED(SHGetMalloc(&pMalloc)))
{
ZeroMemory(&bi,sizeof(bi));
bi.hwndOwner = hWnd;
bi.pszDisplayName = 0;
bi.lpszTitle = 0;
char *pszRootPath = new char[RootPath.GetLength() + 1];
wcstombs(pszRootPath, RootPath, RootPath.GetLength());
bi.pidlRoot = ConvertPathToLpItemIdList(pszRootPath);
if (bi.pidlRoot == NULL)
nRet = 1;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
bi.lpfn = BrowseCallbackProc;
pidl = SHBrowseForFolder(&bi);
if (pidl)
SHGetPathFromIDList(pidl,szDir);
else
nRet = 2;
pMalloc->Free(pidl);
pMalloc->Release();
}
return nRet;
}
Points of Interest
The callback function displays the user selected subfolder at the top of the dialog. Once the user click the OK button, the folder value is transferred to the second text box in the first dialog box. The variable szDir
is accessed from both CFoldersDialog
and the calling class.
History
- First version 1.0: April 27, 2003
- Second version 2.0: February 22, 2010
- Fixed bugs
- Added Unicode support
- Compiled demo project with Visual Studio 6, 2005, 2008 and 2010