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

"Browse For Folder" dialog alike with source

, 5 Sep 2001
Rate this:
Please Sign up or sign in to vote.
Shell interfaces in use. IShellFolder, IEnumIDList, etc.

Sample Image - BFF_new.jpg

Introduction

This code demonstrates the use of Windows shell interfaces.

Shell Interfaces and Functions Used

The interfaces are:

  • IShellFolder
  • IEnumIDList

The shell functions are:

  • SHGetMalloc
  • SHGetDesktopFolder
  • SHGetFileInfo
  • SHGetSpecialFolderLocation

The idea behind it

I've never worked with the Windows shell API before and this is my first try at it. I wanted to implement a common way of retrieving shell namespaces, using provided and documented shell interfaces.

This sample is a substitute for SHBrowseForFolder shell function. This code does not implement evrything SHBrowseForFolder does, but it provides basic functionality for navigating namespaces and selecting a desired path.

The source code is separated into a shell extension DLL BFF{D}.DLL and a sample app App{D}.exe. The DLL contains everything that you need. The sample app shows how to use the DLL's functionality.

How to use

When you need to call the "browse for folder" dialog put in the source code that will look like this:

  CBrowseForFolderDlg::BFFINFO info;
  memset(&info, 0, sizeof(info));
  info.m_pParent = this;
  strcpy(info.m_szTitle, "Browse For Folder");
  strcpy(info.m_szMsg, "Select the Voice Mail Folder");
  info.m_pidlRoot  = m_pidlMainPath; /* Line 1 */
  info.m_pidlSelected = m_pidlSelected;
  info.m_nFlags |= BFF_IDL_ROOT | BFF_IDL_SELECTED; /* Line 2 */
  info.m_nFlags |= (m_bIncFiles) ? BFF_INCLUDE_FILES : info.m_nFlags;
  info.m_nFlags |= (m_bSysOnly) ? BFF_SYSTEM_ONLY : info.m_nFlags;

  CBrowseForFolderDlg dlg(&info);
  if (dlg.DoModal() == IDCANCEL)
	return;

// release memory for the previous default
  SHFree(m_pidlSelected);
// allocate and copy newly selected
  m_pidlSelected = dlg.GetTreeSelectedFullIdl();

The above code assumes that you have preallocated and assigned the m_pidlMainPath and m_pidlSelected values. If you wish to work with null terminated strings, rather than with ITEMIDLIST you would change line 1 and 2 as follows:

  strcpy(info.m_szRoot, "c:\\windows\\system");
  info.m_nFlags |= BFF_CHAR_ROOT;

Note, that you can specify network paths as well.

  strcpy(info.m_szRoot, "\\\\ABERRESFORD\\views");
  info.m_nFlags |= BFF_CHAR_ROOT;

Or CSIDL values.

  info.m_pidlRoot = CSIDL_DESKTOP;
  info.m_nFlags |= BFF_CSIDL_ROOT;

Exactly the same rules apply to the m_pidlSelected and m_szSelected member variable of the CBrowseForFolderDlg::BFFINFO structure. Just make sure that selected path is a subpath of your base root, otherwise you will get a message that specified path does not exist.

I've added a button on the dialog, "Set as Root", and made it invisible for the debugging purposes. You can make it visible and set the roots dynamically in the demo application.

You also have to add the BFF{D}.LIB library to your link list. The letter "D" signifies that you're linking with debug version of DLL.

That's all. You should be able to run your app and use CBrowseForFolderDlg class.

Data and Visual separation

I've applied the "Visitor" design patter to this example. I've introduced an abstract ITreeNode interface and made all its member variables virtual. ITreeNode represents the "data". The meat of the functionality resides in the CShellTreeNode class. CShellTreeNode uses the shell API to get to shell objects. The "visual" is the MFC's CTreeCtrl class, and the "visitor" is the CTreeVisitor class, which has a derivable CUITreeVisitor. CUITreeVisitor knows what methods of CTreeCtrl to tackle to make it work and reflect the hierarchy built upon ITreeNode. CUITreeVisitor knows nothing about the concrete CShellTreeNode and only deals with the methods exposed by ITreeNode, so you can easily change the implementation of this interface.

The CShellHelper class is used by the CShellTreeNode class and is a keeper of all "static" data. That is, data that is not about to change during the lifetime of the application. This class attaches to the global system image list (on WinNT I believe, each process gets its own copy of the system image list) and lets others read/write from/to this list.

Data retrieval

I only extract immediate children for a selected node if any. You can specify the depth of the search by using ITreeNode::SetFindDepth() method. the depth of <1> means just get immediate children.

Displaying Tree

In order to achieve high display speed of the tree I used a widely known display technique. The core idea behind it, is that you only provide information that is visible at a given moment in time. CTreeCtrl will be sending a message for each visible node when it needs text and an icon for it - CTreeCtrl::OnGetdispinfo. The TVITEM::lParam member points at an instance of ITreeNode. Only then I provide the tree control with the text and the icon id in the image list. According to the data retrieval approach I described above, whenever a user expands a node I will request this ITreeNode to go and fetch its immediate children. Then I will request CUITreeVisitor to go and visit (display) found nodes in the tree control. This is done through a method CTreeCtrl calls every time a node with children gets expanded CTreeCtrl::OnItemexpanding(). Therefore, whether a user expands just one level or the "*" button on the numpad was clicked the same algorithm does the job.

Sorting

The shell objects are displayed in a sorted descending order. The system folders take precedence over the files. This is done using standard Microsoft's approach. There is a callback function that you need to set up, which will be called when Microsoft algorithm needs to compare two items from the list being sorted. This is the static CFileTreeCtrl::CompFunc() method in our case.

Updates

I have updated the source code and the picture. Enjoy! If you find bugs or have any suggestions for improvements, let me know.

Updates again... (01/30/2001)

I've fixed a couple of bugs. One of them was pretty nasty. The list control didn't work on WinNT because of that. All fixed now. Now you can specify any node in the shell namespace as a base root for your shell browser. The CBrowseForFolderDlg::BFFINFO structure has been extended to support an arbitrary root. You can specify the base root in the form of an ITEMIDLIST, CSIDL or char*. The same goes to the default selection. You can type in a network path and the synchronize button should be working.

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

Share

About the Author


Comments and Discussions

 
Generalabout SHGDN_INCLUDE_NONFILESYS Pinmemberlazymiken20-Feb-06 5:14 
GeneralFolder comparing tool PinsussAnonymous9-Mar-05 8:41 
General'SHFree' : redefinition Pinmemberiruizg22-Jan-04 5:58 
GeneralRe: 'SHFree' : redefinition PinmemberBisyork30-Jul-04 7:03 
GeneralWideCharToMultiByte with OEMCP or ACP PinsussThomas from Germany4-Nov-02 21:56 
GeneralQuestion regarding Shell Version PinsussAnonymous17-Sep-02 11:24 
GeneralRe: Question regarding Shell Version Pinmemberprolabs30-Mar-05 1:06 
GeneralGetting info about each node PinmemberAnonymous2-Apr-01 14:49 
GeneralRe: Getting info about each node PinmemberMarat Bedretdinov8-Apr-01 14:17 
GeneralUpdate for the CShellTreeNode::operator==(const ITreeNode*) method implementation PinmemberMarat Bedretdinov23-Jan-01 7:08 
GeneralRe: problem with Windows 2000 PinmemberPat O'Neil30-Jun-01 6:33 
GeneralRe: problem with Windows 2000 PinmemberMarat Bedretdinov30-Jun-01 8:03 
GeneralRe: problem with Windows 2000 PinmemberMike Nordell7-Sep-01 10:21 
GeneralRe: problem with Windows 2000 PinmemberMarat Bedretdinov7-Sep-01 13:09 
GeneralRe: problem with Windows 2000 PinmemberMike Nordell9-Sep-01 9:21 
> Very clever (not) I don't think you get the point
> of all this.
 
I'm sorry. I read the introduction "This code demonstrates the use of Windows shell interfaces" and since what was displayed was only the filesystem branch of the shell namespace I assumed it was only about the filesystem namespace... Well, "assumption is the mother of all fsckups". I was wrong.

 
What got me writing the response in the first place was the amount of client code displayed for the task of browsing for a folder, it was never my intention to say that the code I displayed was in any way more clever or generic - just simpler to use for a single-threaded client for the case of getting an existing folder on a drive-letter mounted filesystem.

 
On a sidenote I might add that when using C++ you usually don't have to memset an "auto" (stack) POD struct, just

  structname instancename = { 0 };


and the compiler takes care of the initialization.



GeneralRe: problem with Windows 2000 PinmemberMarat Bedretdinov7-Sep-01 13:19 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 6 Sep 2001
Article Copyright 2001 by Marat Bedretdinov
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid