Brian Cryer

 
 

Cry about...
Delphi Programming


How to browse for a folder


The following working code sample shows how to browse for a folder using "SHBrowseForFolder" (i.e. how to invoke a selection folder dialog) and also how to specify the initially selected folder:

unit BrowseForFolderU;

interface

function BrowseForFolder(const browseTitle: String;
  const initialFolder: String = '';
  mayCreateNewFolder: Boolean = False): String;

implementation

uses
  Windows, Forms, shlobj;

var
  lg_StartFolder: String;

// With later versions of Delphi you may not need these constants.
const
  BIF_NEWDIALOGSTYLE=$40;
  BIF_NONEWFOLDERBUTTON=$200;

////////////////////////////////////////////////////////////////////////
// Call back function used to set the initial browse directory.
////////////////////////////////////////////////////////////////////////
function BrowseForFolderCallBack(Wnd: HWND; uMsg: UINT; lParam,
lpData: LPARAM): Integer stdcall;
begin
  if uMsg = BFFM_INITIALIZED then
    SendMessage(Wnd,BFFM_SETSELECTION, 1, Integer(@lg_StartFolder[1]));
  result := 0;
end;

////////////////////////////////////////////////////////////////////////
// This function allows the user to browse for a folder
//
// Arguments:-
//         browseTitle : The title to display on the browse dialog.
//       initialFolder : Optional argument. Use to specify the folder
//                       initially selected when the dialog opens.
//  mayCreateNewFolder : Flag indicating whether the user can create a
//                       new folder.
//
// Returns: The empty string if no folder was selected (i.e. if the user
//          clicked cancel), otherwise the full folder path.
////////////////////////////////////////////////////////////////////////
function BrowseForFolder(const browseTitle: String;
  const initialFolder: String ='';
  mayCreateNewFolder: Boolean = False): String;
var
  browse_info: TBrowseInfo;
  folder: array[0..MAX_PATH] of char;
  find_context: PItemIDList;
begin
  //--------------------------
  // Initialise the structure.
  //--------------------------
  FillChar(browse_info,SizeOf(browse_info),#0);
  lg_StartFolder := initialFolder;
  browse_info.pszDisplayName := @folder[0];
  browse_info.lpszTitle := PChar(browseTitle);
  browse_info.ulFlags := BIF_RETURNONLYFSDIRS or BIF_NEWDIALOGSTYLE;
  if not mayCreateNewFolder then
    browse_info.ulFlags := browse_info.ulFlags or BIF_NONEWFOLDERBUTTON;

  browse_info.hwndOwner := Application.Handle;
  if initialFolder <> '' then
    browse_info.lpfn := BrowseForFolderCallBack;
  find_context := SHBrowseForFolder(browse_info);
  if Assigned(find_context) then
  begin
    if SHGetPathFromIDList(find_context,folder) then
      result := folder
    else
      result := '';
    GlobalFreePtr(find_context);
  end
  else
    result := '';
end;

end.

The initial folder can be a local path, a mapped drive or even a UNC path.

In later versions of Delphi you may find the two constants for BIF_NEWDIALOGSTYLE and BIF_NONEWFOLDERBUTTON are defined in the unit shlobj, but these were missing in Delphi 7.

I gratefully acknowledge the help of Martin Birk for suggesting the addition of the line "browse_info.hwndOwner := Application.Handle;". Without that the browse dialog pops up in the corner of the screen, with the line the dialog pops up over the application.


These notes are believed to be correct for Delphi 6 and Delphi 7, and may apply to other versions as well.