This article contains two methods. The first shows a "Browse For Folder" dialog. The second method shows how to let the users of your program activate Windows Explorer to investigate the contents of a folder. The two methods are combined in the sample program at the end, but they can be used independently. In fact, it is most likely that you will use them separately!
Browse For Folder Dialog
A Liberty BASIC FILEDIALOG allows the user to select a filename. Sometimes you want to give your users the chance to select a folder. You can do this with the SHBrowseForFolder API call.
The Browse For Folder Dialog looks like this:
This isn't a simple API call, but it isn't difficult to use if you follow the code given here. It is taken apart and explained in the body of the article, and the entire program is included at the bottom.
You must first build a struct to contain information sent to the function.
STRUCT BrowseInfo,_ hWndOwner As Long,_ pIDLRoot As Long,_ pszDisplayName As Long,_ lpszTitle$ As ptr,_ ulFlags As Long,_ lpfnCallback As Long,_ lParam As Long,_ iImage As Long
There is a struct member provided for the text that will display at the top of the Browse Dialog. Here it is set to contain two lines of text:
crlf$=chr$(13)+chr$(10) 'set up text to display on the dialog: txt1$="This is a Liberty BASIC 3+ capability!" + crlf$ txt1$=txt1$+"Please choose a folder!" 'fill the struct item with the text BrowseInfo.lpszTitle$.struct = txt1$
You also need to set a flag to tell the function to return directories:
BIF.RETURNONLYFSDIRS = 1 'Return only if the user selected a directory BrowseInfo.ulFlags.struct = BIF.RETURNONLYFSDIRS
Make the API call to Shell32 DLL like this:
'Show the 'Browse for folder' dialog calldll #shell32, "SHBrowseForFolder",_ BrowseInfo as struct,_ lpIDList as long
If the function is successful, the return is a pointer to an ID List. You can retrieve the complete path and name of the folder chosen by setting up a string buffer to hold the name. The buffer must be terminated by chr$(0), to tell Liberty BASIC to pass the address of the buffer so that it can be modified by the API function. Without the null termination, Liberty BASIC passes a copy of the string, which cannot be modified by the function. You then call the SHGetPathFromIDList function, which requires the pointer to the ID List that was returned by the SHBrowseForFolder function. When this function completes its task, the path/foldername chosen by the user is contained in the buffer you set up. It sounds complicated, but it is easy to do using the following code.
MAX.PATH = 260 'return > 0 = success, so continue If lpIDList>0 Then 'set up a string buffer: sPath$ = space$(MAX.PATH) + chr$(0) 'Get the path from the IDList calldll #shell32, "SHGetPathFromIDList",_ lpIDList as long,_ sPath$ as ptr,_ r as long End If 'the folder name is now in sPath$
You now must free the memory taken by the ID List. This is done with a simple API call that requires the ID of the list.
open "ole32" for dll as #ole 'free the block of memory calldll #ole, "CoTaskMemFree",_ lpIDList as long,_ r as long close #ole
The path/foldername chosen by the user is now in sPath$. The name will be terminated by a null character - chr$(0) - so you can check for the placement of a null character in the string to discover the actual length of the path/folder and truncate the string accordingly.
'check for null char, which 'signals end of folder name string iNull = InStr(sPath$, chr$(0)) 'truncate string at null character If iNull Then sPath$ = Left$(sPath$, iNull - 1) End If
Exploring a Folder
There have been articles in previous newsletters about the ShellExecute API function. Issue #63 dealt with using ShellExecute to open files of various types. Issue #74 discussed printing a text file with ShellExecute. Issue #103 discussed printing graphics with ShellExecute.
It is also possible to use the ShellExecuteA function to explore a specified folder on disk. To do it, the "operation" parameter is "explore" and the "filename" parameter is a null, or empty string. The "directory" parameter is the folder to explore with Windows Explorer. If the "directory" parameter is a null string, it is likely that the program's DefaultDir$ will be called up in Windows Explorer. The "window handle" parameter can be the handle of a program window, or it can be 0. The "showflag" parameter is set to "_SW_SHOWNA", which shows the Windows Explorer window at its normal size (not minimized, maximized or hidden). The following small demo causes Windows Explorer to open in the program's DefaultDir$.
hWnd = 0 'window handle, can be 0 lpszOp$ = "explore" 'operation to perform lpszFile$ = "" 'filename, null here lpszParams$="" 'extra info lpszDir$ = DefaultDir$ 'directory showflag = _SW_SHOWNA 'show normal CallDLL #shell32, "ShellExecuteA",_ hWnd As long,_ lpszOp$ As ptr,_ lpszFile$ As ptr,_ lpszParams$ As ptr,_ lpszDir$ As ptr,_ showflag As long,_ result As long end
The sample demo combines the two methods discussed in this article. It allows a user to select a folder with the SHBrowseForFolder function and it then calls Windows Explorer with the ShellExecuteA API call to explore the folder. Either method can be used separately as detailed above.
Sample Program
'set up constants for api calls BIF.RETURNONLYFSDIRS = 1 MAX.PATH = 260 crlf$=chr$(13)+chr$(10) 'create struct for api call STRUCT BrowseInfo,_ hWndOwner As Long,_ pIDLRoot As Long,_ pszDisplayName As Long,_ lpszTitle$ As ptr,_ ulFlags As Long,_ lpfnCallback As Long,_ lParam As Long,_ iImage As Long 'Set the owner window, optional BrowseInfo.hWndOwner.struct = 0 'set up text to display on the dialog: txt1$="This is a Liberty BASIC 3+ capability!" + crlf$ txt1$=txt1$+"Please choose a folder!" 'fill the struct item with the text BrowseInfo.lpszTitle$.struct = txt1$ 'Return only if the user selected a directory BrowseInfo.ulFlags.struct = BIF.RETURNONLYFSDIRS 'Show the 'Browse for folder' dialog calldll #shell32, "SHBrowseForFolder",_ BrowseInfo as struct,_ lpIDList as long 'return > 0 = success, so continue If lpIDList>0 Then 'set up a string buffer: sPath$ = space$(MAX.PATH) + chr$(0) 'Get the path from the IDList calldll #shell32, "SHGetPathFromIDList",_ lpIDList as long,_ sPath$ as ptr,_ r as long 'the folder name is now in sPath$ open "ole32" for dll as #ole 'free the block of memory calldll #ole, "CoTaskMemFree",_ lpIDList as long,_ r as long close #ole 'check for null char, which 'signals end of folder name string iNull = InStr(sPath$, chr$(0)) 'truncate string at null character If iNull Then sPath$ = Left$(sPath$, iNull - 1) End If End If 'if user cancelled, sPath$ is empty if sPath$="" then sPath$="Cancelled" print "Folder chosen is " print sPath$ if sPath$="Cancelled" then end 'now cause Explorer to open in that folder call ShellExecute 0, sPath$,_SW_SHOWNA, "explore" end Sub ShellExecute hWnd, file$, parameter, lpszOp$ ' _SW_SHOWNA show normal ' _SW_HIDE hide window ' _SW_MINIMIZE minimize window ' lpszOp$ = "open" or "print" or "explore" lpszFile$ = file$ lpszDir$ = DefaultDir$ lpszParams$="" CallDLL #shell32, "ShellExecuteA", hWnd As long,_ lpszOp$ As ptr,lpszFile$ As ptr,_ lpszParams$ As ptr,lpszDir$ As ptr,_ parameter As long, result As long End Sub