Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Win32 - Select Directory Dialog from C/C++

Tags:

c++

winapi

How to select an existing folder (or create new) from a native Win32 application?

Here is a similar question. It has a good answer for C#/.NET. But I want the same thing for native Win32.

Anybody knows a solution, free code, etc?

Update:

I tried the function from the answer. Everything worked as expected, except it is necessary to call the SHGetPathFromIDList function to retrieve the name of selected directory. Here is a sample screen shot:

Example of the directory pickup dialog.

like image 473
Kirill Kobelev Avatar asked Aug 20 '12 09:08

Kirill Kobelev


3 Answers

SHBrowseForFolder

Do your users a favor, and set at least the BIF_NEWDIALOGSTYLE flag.

To set the initial folder, add the following code:

static int CALLBACK BrowseFolderCallback(
                  HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
    if (uMsg == BFFM_INITIALIZED) {
        LPCTSTR path = reinterpret_cast<LPCTSTR>(lpData);
        ::SendMessage(hwnd, BFFM_SETSELECTION, true, (LPARAM) path);
    }
    return 0;
}

// ...
BROWSEINFO binf = { 0 };
...
binf.lParam = reinterpret_cast<LPARAM>(initial_path_as_lpctstr); 
binf.lpfn = BrowseFolderCallback;
...

and provide a suitable path (such as remembering the last selection, your applications data folder, or similar)

like image 61
peterchen Avatar answered Sep 16 '22 16:09

peterchen


Just as a go to for future users, this article helped me a lot with getting a directory dialog in C++

http://www.codeproject.com/Articles/2604/Browse-Folder-dialog-search-folder-and-all-sub-fol

Here is my code (heavily based/taken on the article)

NOTE: You should be able to copy/paste this into a file / compile it (g++, see VS in ninja edit below) and it'll work.

#include <windows.h>
#include <string>
#include <shlobj.h>
#include <iostream>
#include <sstream>

static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData)
{

    if(uMsg == BFFM_INITIALIZED)
    {
        std::string tmp = (const char *) lpData;
        std::cout << "path: " << tmp << std::endl;
        SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
    }

    return 0;
}

std::string BrowseFolder(std::string saved_path)
{
    TCHAR path[MAX_PATH];

    const char * path_param = saved_path.c_str();

    BROWSEINFO bi = { 0 };
    bi.lpszTitle  = ("Browse for folder...");
    bi.ulFlags    = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
    bi.lpfn       = BrowseCallbackProc;
    bi.lParam     = (LPARAM) path_param;

    LPITEMIDLIST pidl = SHBrowseForFolder ( &bi );

    if ( pidl != 0 )
    {
        //get the name of the folder and put it in path
        SHGetPathFromIDList ( pidl, path );

        //free memory used
        IMalloc * imalloc = 0;
        if ( SUCCEEDED( SHGetMalloc ( &imalloc )) )
        {
            imalloc->Free ( pidl );
            imalloc->Release ( );
        }

        return path;
    }

    return "";
}

int main(int argc, const char *argv[])
{
    std::string path = BrowseFolder(argv[1]);
    std::cout << path << std::endl;
    return 0;
}

EDIT: I've updated the code to show people how to remember the last selected path and use that.

Also, for VS, using Unicode character set. replace this line:

const char * path_param = saved_path.c_str();

With this:

std::wstring wsaved_path(saved_path.begin(),saved_path.end());
const wchar_t * path_param = wsaved_path.c_str();

My Test code above is compiled with g++, but doing this fixed it in VS for me.

like image 38
SparkyRobinson Avatar answered Sep 20 '22 16:09

SparkyRobinson


For Windows Vista and above, it's best to use IFileOpenDialog with the FOS_PICKFOLDERS option for a proper open dialog rather than this tree dialog. See Common Item Dialog on MSDN for more details.

like image 34
GrayFace Avatar answered Sep 20 '22 16:09

GrayFace