Instead of using API-function SHBrowseForFolder to select a directory, you can also use the COM-interface of the "Shell".
There is an example in article 18823 of the Progress Knowledgebase.
Julian Lyndon-Smith wrote the following example, different from the one in the Knowledge Base.
FUNCTION DotRGetFolder RETURNS CHARACTER
( INPUT ip_cTitle AS CHARACTER /* title for browse dialog */ ) :
/* constants for BrowseForFolder options */
&SCOPED BIF_RETURNONLYFSDIRS 1
&SCOPED BIF_DONTGOBELOWDOMAIN 2
DEFINE VARIABLE lv_chShell AS COM-HANDLE NO-UNDO. /* shell application */
DEFINE VARIABLE lv_chFolder AS COM-HANDLE NO-UNDO. /* holder for selected folder object */
DEFINE VARIABLE lv_cPathName AS CHARACTER NO-UNDO. /* folder pathame */
IF ip_cTitle EQ "":U OR ip_cTitle EQ ? THEN
ASSIGN ip_cTitle = "Select Folder".
/* create Shell Automation object */
CREATE "Shell.Application":U lv_chShell NO-ERROR.
IF NOT VALID-HANDLE(lv_chShell) THEN
RETURN "":u. /* automation object not present on system */
/* execute the browseForFolderMethod */
lv_chFolder = lv_chShell:BrowseForFolder(CURRENT-WINDOW:HWND,
ip_cTitle,
{&BIF_DONTGOBELOWDOMAIN}
+ {&BIF_RETURNONLYFSDIRS}).
/* see if user has selected a valid folder */
IF VALID-HANDLE(lv_chFolder) AND lv_chFolder:SELF:IsFolder THEN
ASSIGN lv_cPathName = lv_chFolder:SELF:Path.
ELSE
ASSIGN lv_cPathName = "":U.
/* always release com objects when done */
RELEASE OBJECT lv_chFolder NO-ERROR.
RELEASE OBJECT lv_chShell NO-ERROR.
RETURN lv_cPathName.
END FUNCTION.