Browse for folder

Browse for folder

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.

31 posts / 0 new
Last post
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 interface

2. 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 none
TYPE T_BROWSEINFO
INTEGER 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 Paul-Curtis - the interface you provided works fine

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_BROWSEINFOEND MODULEmod_dialogWithout 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 returnend functionMODULE 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 ExtraWinTyMODULE 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 INTERFACEEND 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 = 0Here 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 noneINCLUDE '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_FOLDERLOGICAL 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) STARTPATH1INTEGER(2) UCPATH1(512)INTEGER IACP, IBYTESNEED, IBYTESWRITTEN,IWIDE!INTEGER ITEMIDLISTPTRSTARTPATH1="e:\\Data"//CHAR(0) ! Must be a null-terminated stringUCPATH1=0IACP=GetACP() ! get the code page in useIWIDE=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 zeroITEMIDLISTPTR=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 returnend functionMODULE 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 ExtraWinTyMODULE 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 INTERFACEend 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.

As it is told in IVF Help on STDCALL Attributes, "On IA-32 architecture, an underscore ( _ ) is placed at the beginning of
the external name of a subprogram." Also, I tried to compile without underscore and compiler reported error.

_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 - Intel Developer Support

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.

Leave a Comment

Please sign in to add a comment. Not a member? Join today