Visual Basic Callbacks
SHBrowseForFolder: Browse for Folders Callback Overview
     
Posted:   Saturday March 4, 1998
Updated:   Monday November 28, 2011
     
Applies to:   VB5, VB6
Developed with:   VB5, Windows 95
OS restrictions:   None
Author:   Brad Martinez
     

Related:  

SHBrowseForFolder: Pre-selecting Folders using a Browse Callback
SHBrowseForFolder: Browse for Folders Overview
SHBrowseForFolder: Browse for Folders Dialog
SHBrowseForFolder: Browse for Folders New UI Features
SHBrowseForFolder: Browse to Obtain Network Machines or Shares

CSIDL / Folders:
SHGetFolderPath: Overview of Shell and ComCtrl Versions, CSIDL Values
SHGetFolderPath: Retrieve Windows Shell Folders (Best Practice)

SHGetSpecialFolderLocation: Retrieve Windows Shell Folder
Pure VB: Using the Shell Application Object to Retrieve Windows Shell Folders

       
 Prerequisites
  
VB5/6 for the AddressOf method.

Windows' Browse Folders Dialog provides the means to retrieve from a user their selection of the Shell's file system and special folders. The following and related code page discuss adding callback functionality to a VB5 application to provide the ability to pre-select a folder on the dialog's display.

Once again, VBnet is pleased to present methods developed by Brad Martinez (http://www.mvps.org/btmtz/), author of the Browse for Folders routines presented on this site, as well as author of the Common Controls Replacement Project BrowseDialog control.


So what is a pidl anyway?
With the inception of Win95 and WinNT4 the shell began using a scheme referred to as an 'item identifier' (item ID) to identify every object in the namespace, including not only file system files and folders, but also 'virtual' objects implemented by the shell itself, such as the Desktop, Network Neighborhood, Control Panel, Recycle Bin and Dial-Up Networking folders, to name a few.

Item IDs can be thought of as something similar to a file system's path. Item IDs that are fully qualified (AKA absolute or complex) are refereed to as an 'item ID list', with the root item ID being the Desktop folder (similar to the root of a path). A single item ID (AKA simple item ID list) is only valid if referenced by its parent folder (similar to a relative path). Item IDs are always referenced by a memory pointer, and are consequently titled 'PIDLs' (piddle), or pointer to an item ID list. With the browse dialog, we'll only be dealing with absolute PIDLs.

One important point to note about PIDLs is that the shell allocates memory for each pidl returned from an API function, which must subsequently be freed by the shell's task memory allocator to prevent a memory leak. The API function CoTaskMemFree defined below was designed just for this purpose.
   

Converting a file-system path to a pidl
Aside from what SHGetSpecialFolderLocation returns in the way of pre-defined special shell folders (including virtual folders), there are a two ways that I'm aware of to covert any file system path to a pidl, one the hard and proper way, the other the easy and undocumented way. First the hard and proper way...

Encapsulate the shell's IShellFolder interface and call its ParseDisplayName member function. This entails defining the IShellFolder member functions in ODL or IDL (Object/Interface Description Language, see the SDK for more info) and compiling a type library. But you're in luck, the EnumDeskVB example on my site (http://www.mvps.org/btmtz/) contains a file named ISHF_Ex.tlb in which I've wrapped the IShellFolder interface into a type library. See the GetPIDLFromPath function in the EnumDeskVB project's IShellFolder.bas file to see how ParseDisplayName is called.

For the easy and undocumented way, Shell32.dll contains a rather neat little undocumented function that essentially duplicates what GetPIDLFromPath does (calls ParseDisplayName with the path passed to it converted to Unicode and returns the path's pidl) called SHSimpleIDListFromPath:

Declare Function SHSimpleIDListFromPath Lib _
    "shell32" Alias "#162" _
    (ByVal szPath As String) As Long

Some things to consider about this function: First, as far as I know, the function is exported at the specified ordinal in all current versions of Shell32.dll (including Win95 and WinNT's v4.00.*, IE3's v4.70, IE4 and WinNT 5.0's v4.71, and Win98's v4.72), but there is nothing that says it will be at this ordinal or will be available at all in any future versions of the library.

Secondly, szPath must be specified as an ANSI string in when this function is called from Win95, and as a Unicode string when called on WinNT (see the UndocShell example on my site to see how this can be done).

Finally the function's name is misleading. The function returns an absolute pidl relative to the desktop folder (as opposed to a "simple" pidl relative to its parent IShellFolder, see the top of this message).
   

How to specify a Browse dialog's 'root' folder
Easy enough, just specify the folder's pidl for the BROWSEINFO struct's pidlRoot member. Special shell folder PIDLs can be retrieved by calling the SHGetSpecialFolderLocation API function. To obtain a pidl for a file system path, see "How to convert a file system path to a pidl".

And don't forget, that the shell allocates memory for every pidl returned from an API function and must subsequently be freed via the shell's task allocator by calling the SHSimpleIDListFromPath API function. Again, see "So what is a pidl anyway?" for more information.
   

Implementing a Browse Callback
When calling SHBrowseForFolder, in order to for the browse dialog to open and have a specified folder pre-selected, you must implement the BrowseCallbackProc application-defined callback function in a BAS module. Windows defines the structure of the BrowseCallback procedure with the following syntax:

Public Function BrowseCallbackProc(ByVal hWnd As Long, _
                                   ByVal uMsg As Long, _
                                   ByVal lParam As Long, _
                                   ByVal lpData As Long) As Long

The value of the callback function's memory address (returned by the AddressOf operator) must be specified for the "lpfn" member of the BROWSEINFO struct. Since VB restricts use of the AddressOf operator to function parameters, the callback function's memory address can be obtained indirectly with the following method:

Dim bi As BROWSEINFO
bi.lpfn = FARPROC(AddressOf BrowseCallbackProc)

Public Function FARPROC(ByVal pfn As Long) As Long

  'A dummy procedure that receives and 
  'returns the return value of the 
  'AddressOf operator
   FARPROC = pfn

End Function

After SHBrowseForFolder has been called, and the browse dialog subsequently calls the callback function, the BrowseCallbackProc function's parameters contain the following values:

  • hWnd - Handle of the browse dialog window.
  • uMsg - Message received by the browse dialog, which one of the following Values:
'The browse dialog box has finished initializing. lParam is NULL.
Public Const BFFM_INITIALIZED = 1

'The selection has changed. lParam is a pointer to the item
'identifier list for the newly selected folder.

Public Const BFFM_SELECTIONCHANGED = 2

  • lParam - Message-specific value. See the description of uMsg above.
  • lpData - Application-defined value that was specified in the lParam member of the BROWSEINFO structure.

Once one of the above messages has been received in the callback function, any one of the following messages can be sent to the browse dialog's window (specified by the hWnd value):

Public Const WM_USER = &H400

'Sets the status text to the null-terminated string 
'specified by the lParam parameter, wParam is ignored
'and should be set to 0.
Public Const BFFM_SETSTATUSTEXTA = (WM_USER + 100)
Public Const BFFM_SETSTATUSTEXTW = (WM_USER + 104)

'If the lParam parameter is non-zero, enables the 
'OK button, or disables it if lParam is zero. 
'(docs erroneously said wParam!)
'wParam is ignored and should be set to 0.
Public Const BFFM_ENABLEOK = (WM_USER + 101)

'Selects the specified folder. If the wParam 
'parameter is FALSE, the lParam parameter is the 
'PIDL of the folder to select , or it is the path 
'of the folder if wParam is the C value TRUE (or 1). 
'Note that after this message is sent, the browse 
'dialog receives a subsequent BFFM_SELECTIONCHANGED message.
Public Const BFFM_SETSELECTIONA = (WM_USER + 102)
Public Const BFFM_SETSELECTIONW = (WM_USER + 103)

The associated code in SHBrowseForFolder: Pre-selecting Folders using a Browse Callback demonstrates using the Browse callback to select a predetermined folder using either of the folder's absolute path or file system PIDL.


 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

 

Hit Counter