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.