Hello,
I am new to IVF and I am attempting to display a BrowseForFolder dialog. I downloaded some code from the IVF forum (CmnDlgChooseFolder sample code posted by Paul-Curtis 02-24-2005) but I am having a couple of problems with it.
1. It calls CoTaskMemFree to free the memory allocated by the SHBrowseForFolder function. However CoTaskMemFree generates an unresolved exrernal error, even though I have the "use ole32" statement, in placeand I have added ole32.lib to the list of default libraries for the linker. I noticed that ole32.f90 does not include an interface for CoTaskMemFree, and so I have tried to create my own interface, but I can't get it right. Does anyone know how to use these functions correctly? Or is there another way to free the memory?
2. My BrowseForFolder dialog displays behind all other windows, instead of on top. Any idea why that would happen?
Help would begreatly appreciated - thanks very much.
Browse for folder
Browse for folder
For more complete information about compiler optimizations, see our Optimization Notice.
Here is the missing Interface:
Code:
interface subroutine CoTaskMemFree (pvoid) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_CoTaskMemFree@4' :: CoTaskMemFree !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'CoTaskMemFree' :: CoTaskMemFree !DEC$ ENDIF integer :: pvoid end subroutine CoTaskMemFree end interface2. My BrowseForFolder dialog displays behind all other windows, instead of on top. Any idea why that would happen?
You probably gave it an invalid or NULL parent window handle.
XeffortLite sample contains module XFTFile which contains XBrowse wrapper routine for ShBrowseForFolder. It also allows you to specify the initial directory for the dialog (and I find the software which always lets you start browsing from "My Computer" very annoying).
Jugoslav
Jugoslav
www.xeffort.com
Code:
module BrowseFolder interface integer(4) function SHBrowseForFolder (pBI) use mod_dialog !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHBrowseForFolderA@4' :: SHBrowseForFolder !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHBrowseForFolderA' :: SHBrowseForFolder !DEC$ ENDIF !DEC$ ATTRIBUTES REFERENCE :: pBI TYPE(T_BROWSEINFO) :: pBI end function SHBrowseForFolder end interface interface logical(4) function SHGetPathFromIDList (pidl, pszPath) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHGetPathFromIDListA@8' :: SHGetPathFromIDList !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHGetPathFromIDListA' :: SHGetPathFromIDList !DEC$ ENDIF !DEC$ ATTRIBUTES VALUE :: pidl !DEC$ ATTRIBUTES REFERENCE :: pszPath integer :: pidl character*(*) :: pszPath end function SHGetPathFromIDList end interface end module LOGICAL Function BrowseForFolder(dir) use browsefolder implicit noneTYPE T_BROWSEINFOINTEGER hwndOwner
INTEGER pidlRoot
INTEGER pszDisplayName ! Return display name of item selected.
INTEGER lpszTitle ! text to go in the banner over the tree.
INTEGER ulFlags ! Flags that control the return stuff
INTEGER lpfn
INTEGER lParam ! extra info that's passed back in callbacks
INTEGER iImage ! output var: where to return the Image index.
END TYPE T_BROWSEINFO character*(*) dir integer lpIDList TYPE(T_BROWSEINFO) BI bi.hwndOwner = 0 bi.pidlRoot = NULL !Starts with Workspace bi.pszDisplayName = LOC(Dir) bi.lpszTitle = LOC("Please select directory:"C) bi.ulFlags = 1 !For selecting only directories and hard drives and not network enviroment and so on bi.lpfn = 0 bi.lParam = 0 bi.iImage = 0 lpIDList = SHBrowseForFolder(bi) IF (lpIDList.ne.0) THEN BrowseForFolder = SHGetPathFromIDList (lpIDList, dir) else BrowseForFolder = .false. END IF return end function
I use the following module for browsing folders.
Note that dir has a char(0) at the end of the string.
Markus
Thanks Markus. I implemented this code and it works, but I get the same behaviour as before, i.e. that the Browse for Folder dialog is not visible.To seethec dialog I have to first minimize all windows, and then explicitly restore my application window. If I specify 0 in the bi.hwndOwner field the dialog opens in its own window. If I put the main window hwnd into this field, then the dialog displays as a child window of my main application window. However in both cases I have to first minimize all windows and then restore the application window in order to see it. Any ideas?
Thanks a lot
Lynn
Thanks Jugoslav. I downloaded the XFTLite sample and tried to use it. However, I seem to be missing some files that were not included in the download zip file: XFTApi, XFTCtrl, XFTTypes, and XFLOGM.
Is it possible to obtain these?
Also, with respect to my second problem, i.e. that the BrowseForFolder diolog does not display, pease see my reply to Markus.I think I am giving it a valid parent window handle (the hwnd of the main application window) but the dialog still does not display unless I fool around minimizing and restoring all other windows. If you have any further thoughts on this thatwould be great.
Lynn
The browse dialog will appear on top of its parent window. The handle of this window is passed in the browseinfo structure:
bi%hwndOwner = hwndParent
Your sample code shows bi%hwndOwner = 0, which probably explains why your dialog always appears beneath all other windows.
Thanks Jugoslav. I downloaded the XFTLite sample and tried to use it. However, I seem to be missing some files that were not included in the download zip file: XFTApi, XFTCtrl, XFTTypes, and XFLOGM.
Is it possible to obtain these?
Sorry, XFLOGM.f90 was indeed missing from the zip; I updated it now. (You don't need it for your project though -- only XFTFile and XFTStrings).
Either make sure that first !DEC$DEFINE XLITE line is uncommented, or specify XLITE in Project/Settings/Fortran/Preprocessor/Symbols. In this way, you "cut off" dependencies from other XFT* modules. (All library sources are in full Xeffort installation).
Jugoslav
Jugoslav
www.xeffort.com
I finally solved the problem in which my BrowseForFolder dialog was being displayed behind all other windows: I was responding to the WM_PAINT message in order to automatically display some graphical output, and this was interfering with all popup windows including message boxes. ObviouslyI was not doing it correctly. Anyway it all works now. Many thanks to all who replied to my questions and helped me along the way.
Lynn
Thanks. This is really very useful code. But I can not find module mod_dialog, that you use. What is in it? I use IVF 12. May be this module appears in other versions? I wrote a module with this name and inserted a definition ofTYPE T_BROWSEINFO in it: MODULEmod_dialog USE ifwinty TYPE T_BROWSEINFO SEQUENCE INTEGER(HANDLE) :: hwndOwner INTEGER :: pidlRoot INTEGER :: pszDisplayName INTEGER :: lpszTitle INTEGER :: ulFlags INTEGER :: lpfn INTEGER(fLPARAM) :: lParam INTEGER :: iImage END TYPE T_BROWSEINFO END MODULEmod_dialog Without this module compler reported about undefined structureT_BROWSEINFO. Now after compiling with your code it says: error #6633: The type of the actual argument differs from the type of the dummy argument. [BI] May be something else wrong? Thanks.
Did you also find the following list of useful flags for browsing?
integer, parameter :: BIF_DONTGOBELOWDOMAIN =Z'0002' ! For starting the Find Computer
integer, parameter :: BIF_STATUSTEXT =Z'0004' ! Top of the dialog has 2 lines of text for BROWSEINFO.lpszTitle and one line if
! this flag is set. Passing the message BFFM_SETSTATUSTEXTA to the hwnd can set the
! rest of the text. This is not used with BIF_USENEWUI and BROWSEINFO.lpszTitle gets
! all three lines of text.
integer, parameter :: BIF_RETURNFSANCESTORS =Z'0008'
integer, parameter :: BIF_EDITBOX =Z'0010' ! Add an editbox to the dialog
integer, parameter :: BIF_VALIDATE =Z'0020' ! insist on valid result (or CANCEL)
integer, parameter :: BIF_NEWDIALOGSTYLE =Z'0040' ! Use the new dialog layout with the ability to resize
! Caller needs to call OleInitialize() before using this API
integer, parameter :: BIF_USENEWUI = IOR(BIF_NEWDIALOGSTYLE , BIF_EDITBOX)
integer, parameter :: BIF_BROWSEINCLUDEURLS =Z'0080' ! Allow URLs to be displayed or entered. (Requires BIF_USENEWUI)
integer, parameter :: BIF_UAHINT =Z'0100' ! Add a UA hint to the dialog, in place of the edit box. May not be combined with BIF_EDITBOX
integer, parameter :: BIF_NONEWFOLDERBUTTON =Z'0200' ! Do not add the "New Folder" button to the dialog. Only applicable with BIF_NEWDIALOGSTYLE.
integer, parameter :: BIF_NOTRANSLATETARGETS=Z'0400' ! don't traverse target as shortcut
integer, parameter :: BIF_BROWSEFORCOMPUTER =Z'1000' ! Browsing for Computers.
integer, parameter :: BIF_BROWSEFORPRINTER =Z'2000' ! Browsing for Printers
integer, parameter :: BIF_BROWSEINCLUDEFILES=Z'4000' ! Browsing for Everything
integer, parameter :: BIF_SHAREABLE =Z'8000' ! sharable resources displayed (remote shares, requires BIF_USENEWUI)
integer, parameter :: BFFM_INITIALIZED = 1
I am not experienced programmist, I am scientist. I have found on another forum one more example of using function "Browse for folder", but it required more unknown modules as well. But after two days of dancing withtambourine around computer I made a compilation of these examples and made some modifications. Now It works as I wish, but with one issue, that is important. I could not control setting of the initial folder. Here is code I wrote. I tried to set as initial folder C:\Program Files, but it did not work. Debugger shows, that when I try to get PIDL of the initial forder, functionSHSimpleIDListFromPath always returns 0. Here is code; may be somebody will find in it some stupid errors. LOGICAL Function BROWSEFORFOLDER(dir) use browsefolder USE ifwinty use ExtraWinTy USE user32 implicit none character*(*) dir integer lpIDList, iret INTEGER(4) itemidlistptr, initdirptr CHARACTER(511) initdir TYPE(T_BROWSEINFO) BI initdirptr = LOC('C:\\Program Files'C) !Trying to set initial folder 'C:\Program Files' itemidlistptr = SHSimpleIDListFromPath (initdirptr) bi.hwndOwner = GetForegroundWindow() bi.pidlRoot = itemidlistptr !NULL !Starts with Workspace bi.pszDisplayName = LOC(Dir) bi.lpszTitle = LOC("Please select directory:"C) bi.ulFlags = BIF_RETURNONLYFSDIRS .XOR. BIF_NEWDIALOGSTYLE bi.lpfn = 0 bi.lParam = 0 bi.iImage = 0 iret = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED ) ! Needed for BIF_NEWDIALOGSTYLE flag lpIDList = SHBrowseForFolder(bi) IF (lpIDList.ne.0) THEN BrowseForFolder = SHGetPathFromIDList (lpIDList, dir) else BrowseForFolder = .false. END IF CALL CoUninitialize () ! Needed for CoInitializeEx return end function MODULE ExtraWinTy USE ifwinty integer, parameter :: BIF_RETURNONLYFSDIRS = #00000001 ! For finding a folder to start document searching integer, parameter :: BIF_DONTGOBELOWDOMAIN = #00000002 ! For starting the Find Computer integer, parameter :: BIF_STATUSTEXT = #00000004 integer, parameter :: BIF_RETURNFSANCESTORS = #00000008 integer, parameter :: BIF_EDITBOX = #00000010 integer, parameter :: BIF_BROWSEFORCOMPUTER = #00001000 ! Browsing for Computers. integer, parameter :: BIF_BROWSEFORPRINTER = #00002000 ! Browsing for Printers integer, parameter :: BIF_BROWSEINCLUDEFILES = #00004000 ! Browsing for Everything integer, parameter :: BIF_NONEWFOLDERBUTTON = #00000200 integer, parameter :: BIF_NEWDIALOGSTYLE = #00000040 TYPE T_BROWSEINFO SEQUENCE INTEGER(HANDLE) :: hwndOwner INTEGER :: pidlRoot INTEGER :: pszDisplayName INTEGER :: lpszTitle INTEGER :: ulFlags INTEGER :: lpfn INTEGER(fLPARAM) :: lParam INTEGER :: iImage END TYPE T_BROWSEINFO TYPE ****EMID SEQUENCE INTEGER(2) :: cb INTEGER(1) :: abID END TYPE ****EMID TYPE ITEMIDLIST SEQUENCE TYPE(****EMID) :: mkid END TYPE ITEMIDLIST END MODULE ExtraWinTy MODULE BrowseFolder interface integer(4) function SHBrowseForFolder (pBI) use ExtraWinTy !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHBrowseForFolderA@4' :: SHBrowseForFolder !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHBrowseForFolderA' :: SHBrowseForFolder !DEC$ ENDIF !DEC$ ATTRIBUTES REFERENCE :: pBI TYPE(T_BROWSEINFO) :: pBI end function SHBrowseForFolder end interface interface logical(4) function SHGetPathFromIDList (pidl, pszPath) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHGetPathFromIDListA@8' :: SHGetPathFromIDList !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHGetPathFromIDListA' :: SHGetPathFromIDList !DEC$ ENDIF !DEC$ ATTRIBUTES VALUE :: pidl !DEC$ ATTRIBUTES REFERENCE :: pszPath integer :: pidl character*(*) :: pszPath end function SHGetPathFromIDList end interface INTERFACE INTEGER(4) FUNCTION CoInitializeEx (pvReserved, dwCoInit) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_CoInitializeEx@8' :: CoInitializeEx !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'CoInitializeEx' :: CoInitializeEx !DEC$ ENDIF !DEC$ ATTRIBUTES VALUE :: pvReserved !DEC$ ATTRIBUTES REFERENCE :: dwCoInit INTEGER(4) :: pvReserved INTEGER(4) :: dwCoInit END FUNCTION END INTERFACE INTERFACE SUBROUTINE CoUninitialize () !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_CoUninitialize@0' :: CoUninitialize !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'CoUninitialize' :: CoUninitialize !DEC$ ENDIF END SUBROUTINE END INTERFACE INTERFACE INTEGER(4) FUNCTION SHSimpleIDListFromPath (pszPath) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHSimpleIDListFromPath@4' :: SHSimpleIDListFromPath !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHSimpleIDListFromPath' :: SHSimpleIDListFromPath !DEC$ ENDIF INTEGER(4) :: pszPath END FUNCTION END INTERFACE END MODULE
This works for me, and starts out in the supplied path:
! Shows a folder chooser shell window. The path to the chosen
! folder is placed in folderPath, which is a character array of
! the specified length. Returns true if the user chose a folder,
! or false if the operation was cancelled. The title caption
! is specified as a string table resource ID in the optional
! argument title.
!
LOGICAL FUNCTION CmnDlgChooseFolder (hwndParent, folderPath, title)
USE ExtraWinTy
USE ifcom, only: COMInitialize, COMUnInitialize
IMPLICIT NONE
INTEGER(HANDLE), INTENT(IN) :: hwndParent
CHARACTER(LEN=*), INTENT(INOUT) :: folderPath
INTEGER, INTENT(IN), OPTIONAL :: title ! stringtable id value
TYPE(T_BROWSEINFO) :: bi
CHARACTER(LEN=MAX_PATH) :: buffer
INTEGER :: pidl ! pointer to id list
INTEGER :: status, rval2
CHARACTER(LEN=200) :: titleBuffer
INTEGER :: titleId
TYPE(T_STRRET) :: rstring
INTEGER :: IShellFolder_desktop
CmnDlgChooseFolder = .FALSE.
IF (PRESENT(title)) THEN
titleId = title
ELSE
titleId = IDS_DEFAULT_CHOOSEFOLDER_TITLE
END IF
titleBuffer = STGet(titleId, 200)
CALL COMInitialize (status)
buffer = folderPath
! TYPE T_BROWSEINFO
! SEQUENCE
! INTEGER :: hwndOwner
! INTEGER :: pidlRoot
! INTEGER :: pszDisplayName
! INTEGER :: lpszTitle
! INTEGER :: ulFlags
! INTEGER :: lpfn
! INTEGER :: lParam
! INTEGER :: iImage
! END TYPE T_BROWSEINFO
bi%hwndOwner = hwndParent
bi%pidlRoot = NULL
bi%pszDisplayName = LOC(buffer)
bi%lpszTitle = LOC(titleBuffer)
bi%ulFlags = BIF_RETURNONLYFSDIRS
bi%lpfn = NULL
bi%lParam = 0
bi%iImage = 0
! SHBrowseForFolder returns an item identifier list.
pidl = SHBrowseForFolder (bi)
IF (pidl /= 0) THEN
SELECT CASE (windows_version)
! Win95 and Win98
CASE (VER_PLATFORM_WIN32_WINDOWS)
! We need to ask the shell to decode the item identifier
! list into a parseable name. First, retrieve an IShellFolder
! COM interface handle to the desktop folder.
rval2 = SHGetDesktopFolder(LOC(IShellFolder_desktop))
IF (rval2 == NOERROR) THEN
! Use the GetDisplayNameOf method to convert the item
! identifier list into a string. The SHGDN_FORPARSING flag
! indicates we want the parseable path, not the general-
! purpose display label.
rstring%uType = STRRET_CSTR
rval2 = IShellFolder_GetDisplayNameOf (IShellFolder_desktop,&
pidl, &
SHGDN_FORPARSING, &
LOC(rstring) )
IF (rval2 == NOERROR) THEN
folderPath = rstring%cStr
CmnDlgChooseFolder = .TRUE.
END IF
END IF
! WinNT, Win2K, WinXP
CASE (VER_PLATFORM_WIN32_NT)
CmnDlgChooseFolder = SHGetPathFromIDList (pidl, folderPath)
END SELECT
CALL CoTaskMemFree (pidl)
END IF
CALL COMUnInitialize ()
END FUNCTION CmnDlgChooseFolder
your call to
itemidlistptr = SHSimpleIDListFromPath (initdirptr)
fails because initdirptr must point to a WIDE character string, that is a string consisting of 2 bytes-per-character.
try this:
CHARACTER(256) STARTPATH1
INTEGER(2) UCPATH1(256)
INTEGER IACP, IBYTESNEED, IBYTESWRITTEN,IWIDE
INTEGER ITEMIDLISTPTR
....
....
STARTPATH1="C:\PROGRAM FILES"//CHAR(0) ! Must be a null-terminated string
UCPATH1=0
IACP=GetACP() ! get the code page in use
IWIDE=0
! find out how many bytes are needed...
IBYTESNEED=MultiByteToWideChar(IACP, 0, STARTPATH1,-1,UCPATH1, IWIDE)
!..then write them to UCPATH1..
IBYTESWRITTEN=MultiByteToWideChar(IACP, 0, STARTPATH1,-1,UCPATH1, IBYTESNEED)
UCPATH1(IBYTESWRITTEN+1)=0 ! terminate with an added zero
ITEMIDLISTPTR=SHSimpleIDListFromPath (LOC(UCPATH1))
and you should get a meaningful pointer value for ITEMIDLISTPTR
By the way, "C:\program files" has its own PIDL value = CSIDL_PROGRAM_FILES
Thanks, Paul.
I tried to compile it, but failed. Compiler output (some errors concernet to indefined names):
error #6404: This name does not have a type, and must have an explicit type. [STGET]error #6404: This name does not have a type, and must have an explicit type. [SHBROWSEFORFOLDER]error #6404: This name does not have a type, and must have an explicit type. [WINDOWS_VERSION]error #6404: This name does not have a type, and must have an explicit type. [SHGETDESKTOPFOLDER]error #6404: This name does not have a type, and must have an explicit type. [STRRET_CSTR]error #6404: This name does not have a type, and must have an explicit type. [SHGDN_FORPARSING]error #6404: This name does not have a type, and must have an explicit type. [ISHELLFOLDER_GETDISPLAYNAMEOF]error #6404: This name does not have a type, and must have an explicit type. [SHGETPATHFROMIDLIST]
I could not find definitions for these names in the IVF include files. May be they are somewhere else?
String 52:bi%pidlRoot=NULL
It looks like it starts from the Decktop, not from the supplied path.
Thanks, anthonyrichards.
After I added your code to my and added 'USE kernel32' stringcompiler built application without error reports. But when I run my program and called this function it showed wrong behavior. I tried to use as a starting folder E:\Data. First, a mesage appeared:
The folder E:\Data cannot be used. Please choose another folder.
After I pressed OK, browse for folder dialog appeared, but in wrong kind. It did not contain any folders except E:\Data, but it had improper view and had no subfolders. Screenshots attached. After I pressed Cancel and closed my application it crashed: An unhandled win32 exception occurred in Myprogram.exe [4576].
.
can you post the contents of your T_BROWSINFO structure?
And a screen shot of Windows explorer opened at E:\data?
anthonyrichards, thanks.
Here is the contents ofT_BROWSINFO:
bi.hwndOwner = GetForegroundWindow()
bi.pidlRoot = ITEMIDLISTPTR
bi.pszDisplayName = LOC(Dir)
bi.lpszTitle = LOC("Please select directory:"C)
bi.ulFlags = BIF_RETURNONLYFSDIRS .XOR. BIF_NEWDIALOGSTYLE
bi.lpfn = 0
bi.lParam = 0
bi.iImage = 0
Here is the screenshot:
try using different folders, say on the C: disk. Clearly the problem you found is not with creating the wide-character string it would appear, but some other combination of other events.
Try setting the flags to zero so the defaults are used.
I have tried your code on an XP Pro system and it works fine, except I do not have an e:\data disk so have to use different folder name.
Is there anything special about your E:\data disk?
Please also note (from http://msdn.microsoft.com/en-us/library/windows/desktop/bb762115%28v=vs....):
NoteIf COM is initialized using CoInitializeEx with the COINIT_MULTITHREADED flag, SHBrowseForFolder fails if the calling application uses the BIF_USENEWUI or BIF_NEWDIALOGSTYLE flag in the BROWSEINFO structure.
It is the responsibility of the calling application to call CoTaskMemFree to free the IDList returned by SHBrowseForFolder when it is no longer needed.
Perhaps remove the BIF_NEWDIALOGSTYLE flag?...
I have read this note and initializedCoInitializeExwith flagCOINIT_APARTMENTTHREADED. Also, if I useBIF_NEWDIALOGSTYLE, I get an option of creating new folder, that is important for me. By the way, code showed another spurious behavior. In order to work with it in separate project I created a simple dialog project (which IVF creates by default) and added in it this function (connecting to one of the buttons). And... it stopped compling. Compiler reported about errors: Error 3 error LNK2019: unresolved external symbol _SHSimpleIDListFromPath@4 referenced in function _BROWSEFORFOLDER Modules.obj Error 4 error LNK2019: unresolved external symbol _SHBrowseForFolderA@4 referenced in function _BROWSEFORFOLDER Modules.obj Error 5 error LNK2019: unresolved external symbol _SHGetPathFromIDListA@8 referenced in function _BROWSEFORFOLDER Modules.obj Here is all code. All functions are defined in it. SUBROUTINE OPEN_RES_FOLDER( dlg, id, callbacktype ) !DEC$ ATTRIBUTES DEFAULT :: OPEN_RES_FOLDER use iflogm use comdlg32 use user32 use Input_dialogGlobals implicit none INCLUDE 'RESOURCE.FD' type (dialog) dlg integer id, callbacktype LOGICAL status, BROWSEFORFOLDER CHARACTER dir*512 INTEGER ires status = BROWSEFORFOLDER(dir) !ires = MessageBox (GetForegroundWindow(), TRIM(dir) , ''c ,MB_ICONWARNING ) END SUBROUTINE OPEN_RES_FOLDER LOGICAL Function BROWSEFORFOLDER(dir) use browsefolder USE ifwinty use ExtraWinTy USE user32 USE kernel32 implicit none character*(*) dir integer lpIDList, iret INTEGER(4) itemidlistptr, initdirptr CHARACTER(511) initdir TYPE(T_BROWSEINFO) BI CHARACTER(256) STARTPATH1 INTEGER(2) UCPATH1(512) INTEGER IACP, IBYTESNEED, IBYTESWRITTEN,IWIDE !INTEGER ITEMIDLISTPTR STARTPATH1="e:\\Data"//CHAR(0) ! Must be a null-terminated string UCPATH1=0 IACP=GetACP() ! get the code page in use IWIDE=0 ! find out how many bytes are needed... IBYTESNEED=MultiByteToWideChar(IACP, 0, STARTPATH1,-1,UCPATH1, IWIDE) !..then write them to UCPATH1.. IBYTESWRITTEN=MultiByteToWideChar(IACP, 0, STARTPATH1,-1,UCPATH1, IBYTESNEED) UCPATH1(IBYTESWRITTEN+1)=0 ! terminate with an added zero ITEMIDLISTPTR=SHSimpleIDListFromPath (LOC(UCPATH1)) bi.hwndOwner = GetForegroundWindow() bi.pidlRoot = ITEMIDLISTPTR !NULL !Starts with Workspace bi.pszDisplayName = LOC(Dir) bi.lpszTitle = LOC("Please select directory:"C) bi.ulFlags = BIF_RETURNONLYFSDIRS .XOR. BIF_NEWDIALOGSTYLE bi.lpfn = 0 bi.lParam = 0 bi.iImage = 0 iret = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED ) ! Needed for BIF_NEWDIALOGSTYLE flag lpIDList = SHBrowseForFolder(bi) IF (lpIDList.ne.0) THEN BrowseForFolder = SHGetPathFromIDList (lpIDList, dir) else BrowseForFolder = .false. END IF CALL CoUninitialize () ! Needed for CoInitializeEx return end function MODULE ExtraWinTy USE ifwinty integer, parameter :: BIF_RETURNONLYFSDIRS = #00000001 ! For finding a folder to start document searching integer, parameter :: BIF_DONTGOBELOWDOMAIN = #00000002 ! For starting the Find Computer integer, parameter :: BIF_STATUSTEXT = #00000004 integer, parameter :: BIF_RETURNFSANCESTORS = #00000008 integer, parameter :: BIF_EDITBOX = #00000010 integer, parameter :: BIF_BROWSEFORCOMPUTER = #00001000 ! Browsing for Computers. integer, parameter :: BIF_BROWSEFORPRINTER = #00002000 ! Browsing for Printers integer, parameter :: BIF_BROWSEINCLUDEFILES = #00004000 ! Browsing for Everything integer, parameter :: BIF_NONEWFOLDERBUTTON = #00000200 integer, parameter :: BIF_NEWDIALOGSTYLE = #00000040 TYPE T_BROWSEINFO SEQUENCE INTEGER(HANDLE) :: hwndOwner INTEGER :: pidlRoot INTEGER :: pszDisplayName INTEGER :: lpszTitle INTEGER :: ulFlags INTEGER :: lpfn INTEGER(fLPARAM) :: lParam INTEGER :: iImage END TYPE T_BROWSEINFO TYPE T_STRRET SEQUENCE INTEGER(UINT) :: uType CHARACTER(LEN=MAX_PATH) :: cStr END TYPE T_STRRET TYPE ****EMID SEQUENCE INTEGER(2) :: cb INTEGER(1) :: abID END TYPE ****EMID TYPE ITEMIDLIST SEQUENCE TYPE(****EMID) :: mkid END TYPE ITEMIDLIST END MODULE ExtraWinTy MODULE BrowseFolder interface integer(4) function SHBrowseForFolder (pBI) use ExtraWinTy !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHBrowseForFolderA@4' :: SHBrowseForFolder !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHBrowseForFolderA' :: SHBrowseForFolder !DEC$ ENDIF !DEC$ ATTRIBUTES REFERENCE :: pBI TYPE(T_BROWSEINFO) :: pBI end function SHBrowseForFolder end interface interface logical(4) function SHGetPathFromIDList (pidl, pszPath) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHGetPathFromIDListA@8' :: SHGetPathFromIDList !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHGetPathFromIDListA' :: SHGetPathFromIDList !DEC$ ENDIF !DEC$ ATTRIBUTES VALUE :: pidl !DEC$ ATTRIBUTES REFERENCE :: pszPath integer :: pidl character*(*) :: pszPath end function SHGetPathFromIDList end interface INTERFACE INTEGER(4) FUNCTION CoInitializeEx (pvReserved, dwCoInit) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_CoInitializeEx@8' :: CoInitializeEx !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'CoInitializeEx' :: CoInitializeEx !DEC$ ENDIF !DEC$ ATTRIBUTES VALUE :: pvReserved !DEC$ ATTRIBUTES REFERENCE :: dwCoInit INTEGER(4) :: pvReserved INTEGER(4) :: dwCoInit END FUNCTION END INTERFACE INTERFACE SUBROUTINE CoUninitialize () !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_CoUninitialize@0' :: CoUninitialize !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'CoUninitialize' :: CoUninitialize !DEC$ ENDIF END SUBROUTINE END INTERFACE INTERFACE INTEGER(4) FUNCTION SHSimpleIDListFromPath (pszPath) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHSimpleIDListFromPath@4' :: SHSimpleIDListFromPath !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHSimpleIDListFromPath' :: SHSimpleIDListFromPath !DEC$ ENDIF INTEGER(4) :: pszPath END FUNCTION END INTERFACE end module
The version of Shell32.dll that I have in c:\windows\system32\ has symbols for the functions you need but lacking a leading underscore. This would explain why the symbols specified in the ALIAS directives with leading underscore are not found when the libraries are searched.
_SHSimpleIDListFromPath@4 is correct on IA-32. This is defined in shell32.lib. You need to add shell32.lib to the list of libraries you link against. You can also USE SHELL32, though this routine is not defined in it (using the module will pull in the library.)
Steve
Thanks. After I added USE SHELL32 compilation passes well. But when I launch program and press button, linked with this routine, a mistake message appears as it is described in post 16. This message appears only if I explicitly set a start folder other then default (E:\Data in this example).
LOGICAL Function BROWSEFORFOLDER(dir)
use browsefolder
USE ifwinty
use ExtraWinTy
USE user32
USE kernel32
implicit none
character*(*) dir
integer lpIDList, iret
INTEGER(4) itemidlistptr, initdirptr
CHARACTER(511) initdir
TYPE(T_BROWSEINFO) BI
CHARACTER(256) STARTPATH1
INTEGER(2) UCPATH1(512)
INTEGER IACP, IBYTESNEED, IBYTESWRITTEN,IWIDE
!INTEGER ITEMIDLISTPTR
STARTPATH1="E:Data"//CHAR(0) ! Must be a null-terminated string
UCPATH1=0
IACP=GetACP() ! get the code page in use
IWIDE=0
! find out how many bytes are needed...
IBYTESNEED=MultiByteToWideChar(IACP, 0, STARTPATH1,-1,UCPATH1, IWIDE)
!..then write them to UCPATH1..
IBYTESWRITTEN=MultiByteToWideChar(IACP, 0, STARTPATH1,-1,UCPATH1, IBYTESNEED)
UCPATH1(IBYTESWRITTEN+1)=0 ! terminate with an added zero
ITEMIDLISTPTR=SHSimpleIDListFromPath (LOC(UCPATH1))
bi.hwndOwner = GetForegroundWindow()
bi.pidlRoot = ITEMIDLISTPTR !NULL !Starts with Workspace
bi.pszDisplayName = LOC(Dir)
bi.lpszTitle = LOC("Please select directory:"C)
bi.ulFlags = BIF_RETURNONLYFSDIRS .XOR. BIF_NEWDIALOGSTYLE
bi.lpfn = 0
bi.lParam = 0
bi.iImage = 0
iret = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED ) ! Needed for BIF_NEWDIALOGSTYLE flag
lpIDList = SHBrowseForFolder(bi)
IF (lpIDList.ne.0) THEN
BrowseForFolder = SHGetPathFromIDList (lpIDList, dir)
else
BrowseForFolder = .false.
END IF
CALL CoUninitialize () ! Needed for CoInitializeEx
return
end function
MODULE ExtraWinTy
USE ifwinty
integer, parameter :: BIF_RETURNONLYFSDIRS = #00000001 ! For finding a folder to start document searching
integer, parameter :: BIF_DONTGOBELOWDOMAIN = #00000002 ! For starting the Find Computer
integer, parameter :: BIF_STATUSTEXT = #00000004
integer, parameter :: BIF_RETURNFSANCESTORS = #00000008
integer, parameter :: BIF_EDITBOX = #00000010
integer, parameter :: BIF_BROWSEFORCOMPUTER = #00001000 ! Browsing for Computers.
integer, parameter :: BIF_BROWSEFORPRINTER = #00002000 ! Browsing for Printers
integer, parameter :: BIF_BROWSEINCLUDEFILES = #00004000 ! Browsing for Everything
integer, parameter :: BIF_NONEWFOLDERBUTTON = #00000200
integer, parameter :: BIF_NEWDIALOGSTYLE = #00000040
TYPE T_BROWSEINFO
SEQUENCE
INTEGER(HANDLE) :: hwndOwner
INTEGER :: pidlRoot
INTEGER :: pszDisplayName
INTEGER :: lpszTitle
INTEGER :: ulFlags
INTEGER :: lpfn
INTEGER(fLPARAM) :: lParam
INTEGER :: iImage
END TYPE T_BROWSEINFO
TYPE T_STRRET
SEQUENCE
INTEGER(UINT) :: uType
CHARACTER(LEN=MAX_PATH) :: cStr
END TYPE T_STRRET
END MODULE ExtraWinTy
MODULE BrowseFolder
USE SHELL32
interface
integer(4) function SHBrowseForFolder (pBI)
use ExtraWinTy
!DEC$ IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHBrowseForFolderA@4' :: SHBrowseForFolder
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHBrowseForFolderA' :: SHBrowseForFolder
!DEC$ ENDIF
!DEC$ ATTRIBUTES REFERENCE :: pBI
TYPE(T_BROWSEINFO) :: pBI
end function SHBrowseForFolder
end interface
interface
logical(4) function SHGetPathFromIDList (pidl, pszPath)
!DEC$ IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHGetPathFromIDListA@8' :: SHGetPathFromIDList
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHGetPathFromIDListA' :: SHGetPathFromIDList
!DEC$ ENDIF
!DEC$ ATTRIBUTES VALUE :: pidl
!DEC$ ATTRIBUTES REFERENCE :: pszPath
integer :: pidl
character*(*) :: pszPath
end function SHGetPathFromIDList
end interface
INTERFACE
INTEGER(4) FUNCTION CoInitializeEx (pvReserved, dwCoInit)
!DEC$ IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS:'_CoInitializeEx@8' :: CoInitializeEx
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS: 'CoInitializeEx' :: CoInitializeEx
!DEC$ ENDIF
!DEC$ ATTRIBUTES VALUE :: pvReserved
!DEC$ ATTRIBUTES REFERENCE :: dwCoInit
INTEGER(4) :: pvReserved
INTEGER(4) :: dwCoInit
END FUNCTION
END INTERFACE
INTERFACE
SUBROUTINE CoUninitialize ()
!DEC$ IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS:'_CoUninitialize@0' :: CoUninitialize
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS: 'CoUninitialize' :: CoUninitialize
!DEC$ ENDIF
END SUBROUTINE
END INTERFACE
INTERFACE
INTEGER(4) FUNCTION SHSimpleIDListFromPath (pszPath)
!DEC$ IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHSimpleIDListFromPath@4' :: SHSimpleIDListFromPath
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHSimpleIDListFromPath' :: SHSimpleIDListFromPath
!DEC$ ENDIF
INTEGER(4) :: pszPath
END FUNCTION
END INTERFACE
end module
Please try removing the BIF_NEWDIALOGSTYLE flag and use defaults, just to see whether that makes any difference.
I know you say you need it to get an edit box but it may be the problem.
Also, is there anything special about your E:\ drive and its folders (acess, security-wise etc.)?
What Windows operating system are you using?
When I removeBIF_NEWDIALOGSTYLE flag and use E:\data as starting folder it shows the following window:
This starting folder could not be expanded by clicking the cross on the left. It is abnormal too.
When I use default starting folder (bi.pidlRoot = NULL) everything goes OK both with and withoutBIF_NEWDIALOGSTYLE flag.
E: drive is usual partition. I keep all data on it, including "My Documents". Folder "Data" on it was made by Windows Explorer, specially to test this function and has no additional attributes.
I use Windows XP SP3.
I predict that your problem(s) will go away after you have replaced
ITEMIDLISTPTR=SHSimpleIDListFromPath(LOC(UCPATH1))
with
INTEGER, PARAMETER :: CSIDL_FLAG_CREATE ='8000'X
integer, parameter :: CSIDL_PROGRAM_FILES = '0026'x
...
...
ITEMIDLISTPTR=0
IATTRIBUTES=CSIDL_FLAG_CREATE
IRET=SHILCREATEFROMPATH(UCPATH1, ITEMIDLISTPTR, IATTRIBUTES)
The problem appears to be linked to using the output from SHSimpleIDListFromPath instead of
SHILCreateFromPath
you will need the following interface block:
interface
logical(4) function SHILCreateFromPath(pszPath, pidl, iflags)
!DEC$ IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS:'_SHILCreateFromPath@12' :: SHILCreateFromPath
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS: 'SHILCreateFromPath' :: SHILCreateFromPath
!DEC$ ENDIF
!DEC$ ATTRIBUTES REFERENCE :: pidl
!DEC$ ATTRIBUTES REFERENCE :: iflags
!DEC$ ATTRIBUTES REFERENCE :: pszPath
integer :: pidl, iflags
integer(2) :: pszPath(*)
end function SHILCreateFromPath
end interface
Yes!YES!!! Thanks a lot.
It works.
The only problem is that I missed initial folder and root folder.
This is what I have now:
And this is my dream (I generated this dialog by AutoIt script):
I have not found anyBROWSEINFO flags which could set initial folder. May be not all of them are documented?
May be, there is a function, which after BrowseForForder dialog is evoked sets focus on initial folder?
You will probably have to play with the IATTRIBUTES value. Research MSDN information on SHilCreateFromPath for useful values.
The CSIDL_FLAG_CREATE value I gave as an example causes the requested folder to be created if it is not present.
I believe that BrowsForFolder will only display folders from the requested folder path downwards. It will not display folders at a higher level.



