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.
I have had it suggested that if you pass in the application handle as a parameter then you no longer need to include "forms" in the uses clause. The function signature would then become:
function BrowseForFolder(const browseTitle: String;
const initialFolder: String = '';
const appHWND: THandle;
mayCreateNewFolder: Boolean = False): String;
and the line:
browse_info.hwndOwner := Application.Handle;
replaced with:
browse_info.hwndOwner := appHWND;
which you prefer is down to your individual preference.
These notes are believed to be correct for Delphi 6 and Delphi 7, and may apply to other versions as well.
About the author: Brian Cryer is a dedicated software developer and webmaster. For his day job he develops websites and desktop applications as well as providing IT services. He moonlights as a technical author and consultant.