Click here to Skip to main content
Click here to Skip to main content

How to Browse for a Folder

By , 16 Feb 2006
 

Introduction

This article presents a no-frills, cut-n-paste solution to the age-old problem of browsing for a folder. There are several classes out here that offer much more customization and a plethora of features, but I'm sure one more example won't hurt.

When to use this solution

The benefit of this approach over some of the others here is that this implementation is a direct wrapper function for SHBrowseForFolder. As such, you can use it in console applications that don't use MFC, such as unit-testing applications that could benefit from not hard-coding an output location for test results.

So, in short, if you're looking for eye candy, check out XFolderDialog or some of the other folder dialogs here. Otherwise, read on.

Using the code

To use this code, you need to include the following standard files:

#include "shlobj.h"
#include <string>

The GetFolder function

Params

  • folderpath - A reference to the string that will contain the folder path if successful.
  • szCaption - An optional message for the dialog.
  • hOwner - An optional handle to the parent window.

Returns

  • true if successful, false if cancelled or an error occurs.
bool GetFolder(std::string& folderpath, 
               const char* szCaption = NULL, 
               HWND hOwner = NULL)
{
   bool retVal = false;

   // The BROWSEINFO struct tells the shell 
   // how it should display the dialog.
   BROWSEINFO bi;
   memset(&bi, 0, sizeof(bi));

   bi.ulFlags   = BIF_USENEWUI;
   bi.hwndOwner = hOwner;
   bi.lpszTitle = szCaption;

   // must call this if using BIF_USENEWUI
   ::OleInitialize(NULL);

   // Show the dialog and get the itemIDList for the 
   // selected folder.
   LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);

   if(pIDL != NULL)
   {
      // Create a buffer to store the path, then 
      // get the path.
      char buffer[_MAX_PATH] = {'\0'};
      if(::SHGetPathFromIDList(pIDL, buffer) != 0)
      {
         // Set the string value.
         folderpath = buffer;
         retVal = true;
      }

      // free the item id list
      CoTaskMemFree(pIDL);
   }

   ::OleUninitialize();

   return retVal;
}

Conclusion

Well, that's really all there is to it. If this code saves time for even one person out here, then the few minutes spent writing this was well worth it. Enjoy!

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

About the Author

Nitron
Software Developer (Senior) Barbnet Investment Co.
United States United States
Member
Walter Storm is currently a principal software engineer doing quantitative research for a private hedge fund. Originally from Tunkhannock, PA., he has a B.S. in Aerospace Engineering from Embry-Riddle Aeronautical University[^], and an M.S. in Systems Engineering from SMU[^]. He has been professionally developing software in some form or another since January of 2001.
 
View Walter Storm's profile on LinkedIn.[^]

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionPossible to preset a specific folder?memberChristof Schardt16 Feb '06 - 11:38 
I couldn't yet find a way to preset the box to a specific folder. If you have deep nested folders and want to go ub/down just one or two levels, it is annoying to have to walk down rather than starting at a certain folder.
 
Does anyone know a possibility to achieve this?
 
Thanks
Christof
 


AnswerRe: Possible to preset a specific folder? PinmemberNitron16 Feb '06 - 11:44 
Christof Schardt wrote:
Does anyone know a possibility to achieve this?

 
sheesh! I think we have more comments than lines of code on this! Nevertheless, all the better. Big Grin | :-D I do believe there are flags/params for the BROWSEINFO struct that will specify this, I'll look into it and add an arg to the fcn. Check back tomorrow.
 
~Nitron.
ññòòïðïðB A
start

GeneralRe: Possible to preset a specific folder? PinmemberChristof Schardt16 Feb '06 - 11:50 

Nitron wrote:
I do believe there are flags/params for the BROWSEINFO struct that will specify this

 
I looked for it but didn't find.
pidlRoot cannot be used, because it doesn't show the tree above the specified folder, so you are not able to navigate upwards from the starting-point.
 
Christof

GeneralRe: Possible to preset a specific folder? PinstaffNishant Sivakumar16 Feb '06 - 14:18 
Christof Schardt wrote:
I looked for it but didn't find.
pidlRoot cannot be used, because it doesn't show the tree above the specified folder, so you are not able to navigate upwards from the starting-point.

 
The discussions today with Nitron got me interested in this stuff, and I just wrote a blog entry on how you can specify a default folder using SHBrowseForFolder.
 
Selecting a default folder with SHBrowseForFolder[^]
 
Regards,
Nish
Nish’s thoughts on MFC, C++/CLI and .NET (my blog)
The Ultimate Grid - The #1 MFC grid out there!

GeneralRe: Possible to preset a specific folder? PinmemberChristof Schardt16 Feb '06 - 19:56 
Nishant Sivakumar wrote:
The discussions today with Nitron got me interested in this stuff, and I just wrote a blog entry on how you can specify a default folder using SHBrowseForFolder.

 
Great, Thanks a lot! It worked for me (after I removed the L"..." from the string).
 
Christof
 


GeneralRe: Possible to preset a specific folder? PinstaffNishant Sivakumar17 Feb '06 - 1:23 
Christof Schardt wrote:
Great, Thanks a lot! It worked for me (after I removed the L"..." from the string).

 
Yeah, MSDN incorrectly says an Unicode string should be used, when they meant Unicode-compliant string. Better use _T("...") there, so your code works in the Unicode version too.
 
Regards,
Nish
Nish’s thoughts on MFC, C++/CLI and .NET (my blog)
The Ultimate Grid - The #1 MFC grid out there!

GeneralRe: Possible to preset a specific folder? PinmemberSBJ21 Feb '06 - 4:52 
Can this technique be used to select a special folder, say My Documents?
 
Thanks,
Steve
 

GeneralRe: Possible to preset a specific folder? PinstaffNishant Sivakumar21 Feb '06 - 13:15 
SBJ wrote:
Can this technique be used to select a special folder, say My Documents?

 
Yes, set WPARAM to FALSE, and set LPARAM to the PIDL for My Documents.
 
Regards,
Nish
Nish’s thoughts on MFC, C++/CLI and .NET (my blog)
The Ultimate Grid - The #1 MFC grid out there!

GeneralRe: Possible to preset a specific folder? PinmemberSBJ21 Feb '06 - 13:27 
Nish,
Thanks much!
Steve
AnswerRe: Possible to preset a specific folder? PinmemberNitron17 Feb '06 - 3:32 
Here is a version that allows you to specify a starting location, but you cannot go up from it, only down:
 
bool GetFolder(std::string& folderpath, LPCTSTR szCaption = NULL, LPCWSTR szStartPath = NULL, HWND hOwner = NULL)
{
	bool retVal = false;
 
	// get a ITEMIDLIST for the starting path
	LPITEMIDLIST  pIDLRoot  = NULL;
 
	if (szStartPath != NULL)
	{
		LPSHELLFOLDER pShellFolder = NULL;
		HRESULT       hResult      = NULL;
		ULONG         chUsed       = 0L;
		BSTR          bstrPath     = NULL;
 
		bstrPath = ::SysAllocString ( szStartPath );
 
		if ( NULL != bstrPath )
		{
			// Get desktop IShellFolder interface
			if (::SHGetDesktopFolder (&pShellFolder) == NOERROR)
			{
				// convert the path to an ITEMIDLIST
				hResult = 
					pShellFolder->ParseDisplayName 
						(
							NULL,			// owner window
							NULL,			// reserved (must be NULL)
							bstrPath,       	// folder name
							&chUsed,		// number of chars parsed
							&pIDLRoot,      // ITEMIDLIST
							NULL            // attributes (can be NULL)
						);
 
				if (FAILED(hResult))
				{
					pIDLRoot = NULL;
				}
 
				pShellFolder->Release();
			}
		}
 
		::SysFreeString(bstrPath);
	}
 
	// The BROWSEINFO struct tells the shell 
	// how it should display the dialog.
	BROWSEINFO bi;
	memset(&bi, 0, sizeof(bi));
 
	bi.pidlRoot  = pIDLRoot;
	bi.ulFlags   = BIF_USENEWUI;
	bi.hwndOwner = hOwner;
	bi.lpszTitle = szCaption;
 
	// must call this if using BIF_USENEWUI
	::OleInitialize(NULL);
 
	// Show the dialog and get the itemIDList for the selected folder.
	LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);
 
	if(pIDL != NULL)
	{
		// Create a buffer to store the path, then get the path.
		char buffer[_MAX_PATH] = {'\0'};
		if(::SHGetPathFromIDList(pIDL, buffer) != 0)
		{
			// Set the string value.
			folderpath = buffer;
			retVal = true;
		}		
 
		// free the item id list
		::CoTaskMemFree(pIDL);
	}
 
	::OleUninitialize();
 
	return retVal;
}

 
~Nitron.
ññòòïðïðB A
start

AnswerRe: Possible to preset a specific folder? PinmemberStephan Poirier21 Feb '06 - 18:12 
I have something for what you are looking for. It's a wrapper class called CShellHelpers that execute all the commands needed to :
 
1 - Set the starting path of SHBrowseForFolder before opening of the dialog
2 - Then Retrieve the selected path when the dialog is closed
 
With this wrapper, you can go up from your starting location. It use the BFFM_INITIALIZED message in the BrowseCtrlCallback function to sets the startup folder location.
 
There are also some other functions like :
1 - Retrieve the icon belonging to the selected folder.
2 - Shell context menu for a given path.
3 - Retrieve volume serial number
4 - Shell About..
 
Whenever I need to browse for a folder, I use it!
I don't remember when I did this code. From what I can remember, I took some portion of code here and there and assembled it to make this wrapper. The class is still in constant devellopment and is not really ready for release to public but since I came across this discussion, I thought it would be kind from me to let you have my code. I included a small example that show how it work.
 
Email me if you want to have it. I can't find the damn button to "Add files" to this post! Big Grin | :-D
 
Have fun!
 
Stef

 
Programming looks like taking drugs...
I think I did an overdose. Poke tongue | ;-P
AnswerRe: Possible to preset a specific folder? Pinmemberbrightatlantis16 Oct '07 - 4:26 
You can get the pidl using SHSimpleIDListFromPath. Then you can assign a callback function to your BROWSEINFO struct to set the initial start path upon initialization of the window.
 
LPITEMIDLIST getPidl(const CString& str)
{
CStringW wStr(str);
return ::SHSimpleIDListFromPath(wStr);
}
 
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
switch(uMsg)
{
case BFFM_INITIALIZED:
return SendMessage(hwnd, BFFM_SETSELECTIONA, false, lpData);
}
 
return 0;
}
 
Then, just set the BROWSEINFO struct info as follows and you have the start path of your choosing. You can now navigate anywhere in the directory structure.
 
void ChooseDirectoryDialog(){
 
BROWSEINFO bi;
ZeroMemory(&bi, sizeof(bi))
bi.lpszTitle = "Choose a Directory";
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)getPidl("C:\MyPath");
 
LPITEMIDLIST idList = SHBrowseForFolder(&bi);
 
if (idList != NULL)
{
//get info from struct...
}
 
return 0;
}
 
Note: This isn't copied straight from source code so it may not compile right away.
 


 
Cheers,
 
Rich

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 16 Feb 2006
Article Copyright 2006 by Nitron
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid