Monday, March 10, 2008

Browse for computers, folders, files and printers


Use Delphi (and API) to display the directory structure of a computer and allow a user to select a folder without using the Common Dialog control. Plus: select a printer or network computer.

In Windows, there are several ways to display directory structure of a computer. Most Delphi programmers know how to use the TOpenDialog component to allow the user to browse for folders and files to open.

By using the SHBrowseForFolder Windows API function and Delphi we can invoke a Windows system dialog used to browse for files and folders on users hard drive as well as network computers and printers.

Browse dialog

First, let's look at what SHBrowseForFolder needs. Here's the function declaration:

function SHBrowseForFolder
(var BrowseInfo: TBrowseInfo): PItemIDList; stdcall;

We pass in a complicated record type BrowseInfo to initialize and customize the Browse For Folder dialog box. We get back an item ID list (let's say: location of the selected folder, not to confuse to much).
Now we'll see how to fill in a record structure with information that initializes the Browse for Folder dialog box, then call SHBrowseForFolder to display the dialog box.

BrowseInfo structure
Two of the main elements of BrowseInfo are the lpszTitle and ulFlags fields. The dialog box displays the contents of lpszTitle in a static text control above the treeview. The ulFlags element sets the value which determines what the dialog displays and allows the user to select.
We can specify zero or more of the flags in order to make the dialog box much more useful than just browsing for folders. Some of the flags that can be specified to enhance the Browse For Folders dialog box are:

Value Meaning
BIF_BROWSEFORCOMPUTER Only returns computers. If the user selects anything other than a computer, the OK button is grayed.
BIF_BROWSEFORPRINTER Only returns printers. If the user selects anything other than a printer, the OK button is grayed.
BIF_RETURNONLYFSDIRS Only returns file system directories. If the user selects folders that aren't part of the file system, the OK button is grayed.
BIF_BROWSEINCLUDEFILES The browse dialog will display files as well as folders

For example, to browse for both folders and files the ulFlags should be set to BIF_BROWSEINCLUDEFILES.
Browse Delphi Form

   Delphi code
When we put all this in a Delphi function that will create the structure, initialize it, and call SHBrowseForFolder() to display the dialog box, we get something like:

uses ShellAPI, ShlObj;
function BrowseDialog
(const Title: string; const Flag: integer): string;
lpItemID : PItemIDList;
BrowseInfo : TBrowseInfo;
DisplayName : array[0..MAX_PATH] of char;
TempPath : array[0..MAX_PATH] of char;
FillChar(BrowseInfo, sizeof(TBrowseInfo), #0);
with BrowseInfo do begin
hwndOwner := Application.Handle;
pszDisplayName := @DisplayName;
lpszTitle := PChar(Title);
ulFlags := Flag;
lpItemID := SHBrowseForFolder(BrowseInfo);
if lpItemId <> nil then begin
SHGetPathFromIDList(lpItemID, TempPath);
Result := TempPath;

The BrowseDialog function takes two parameters: Title and Flag. Title represents the text that appears above the treeview (lpszTitle field of the BrowseInfo record). Flag parameter is used to fill the ulFlags field.

The function can now be simply called (to display the folder selected by the user) like:

procedure TfrMain.btnBrowseClick(Sender: TObject);
var sTitle, sFolder: string;
iFlag : integer;
sTitle:='Choose a ' +
case rgBrowseFor.ItemIndex of
sFolder := BrowseDialog(sTitle, iFlag);
if sFolder <> '' then
edSelected.text := sFolder
edSelected.text := 'Nothing selected';

Note: On the form (Name: frMain) there is a RadioGroup component (Name: rgBrowseFor) with four items, each one representing a value for the Flag variable (see the picture above). The function extracts the path to the selected folder from the lpItemID and returns it (filling the edSelected text box component). If the function fails (or the user presses the Cancel button), the string it returns will be empty.

   More info / related

  • Often, you may prefer that your application start the browse dialog box at a folder that the user is likely to want, such as the current working directory. Another interesting task is to force the dialog to appear centered on the screen. To set the browse dialog box's initial selection, the BROWSEINFO structure must contain a special callback function.
  • TShBrowse component. Native Delphi component that encapsulates the SHBrowseForFolder interface.
  • The SHBrowseForFolder function and a BROWSEINFO structure detailed info.
  • The application can also restrict the range of folders that the user can select from by specifying a root folder. See how to set the SHBrowseForFolder dialog box, with the root folder set to Program Files (not Delphi code unfortunately, but should be easy to understand).

  • Looking for last minute shopping deals? Find them fast with Yahoo! Search.