Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to show a dialog to let users browse for and select a folder

0.00/5 (No votes)
30 Aug 2002 1  
Shows how to use Windows shell functions to browse folders

Sample Image - screenshot.jpg

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() 
{
    // TODO: Add your control notification 

    // handler code here

    LPMALLOC pMalloc = NULL;
    LPITEMIDLIST pidl = NULL;
    BROWSEINFO bi;
    ZeroMemory(&bi, sizeof(BROWSEINFO));
    
    // set the bi's default values

    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);
        // free memory

        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:
        {
            // add your initialization code here

        }
        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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here