Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29235 Discussions

SHILCreateFromPath and Shell32.dll

anthonyrichards
New Contributor III
3,158 Views
I want to use SHILCreateFromPath (with ShBrowseToFolder etc.), but the Shell32.dll on myWindows 2000 system does not have SHILCreateFromPath init, even though it has SHBrowseToFolder and other shell commands in it. However, the Shell32.dll on my home PC's Windows XP system does have SHILCreateFromPath in it. Can I copy over and use the Shell32.dll on the XP systemon tomy WIndows 2000 system with no ill-effects?
0 Kudos
14 Replies
ggveldkamp2
Novice
3,158 Views

According to the MSDN documentation SHILCreateFromPath is also available on Windows 2000. Maybe you need to install some service pack or a newer version of Internet Explorer. I would not copy over the dll.

0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,158 Views
Copying the Shell32 over is definitely a bad idea.

There's something fishy about MSDN description:

NoteThis function is available through Microsoft Windows XPService Pack 2 (SP2) and Windows Server 2003. It might be altered or unavailable in subsequent versions of Windows. It is not available in Windows Millennium Edition (Windows Me)....

Minimum operating systems Windows2000, Windows XP

There is a similar one, ILCreateFromPath, so you might try your luck with it instead.

But, what exactly you're doing with it? BROWSEINFO.pszDisplayName is an "intent(inout)" parameter -- it specifies the initial input folder. Setting up an arbitrary pidlRoot perhaps?
0 Kudos
anthonyrichards
New Contributor III
3,158 Views

Thanks, Jugoslav, for spotting the ILCreateFromPath function, which I had missed and I will look at it.

Yes, I want to open the browse tree at a particular folder which is not necessarily a 'special folder', for which CSIDL's are available. So I need to generate a pointer to a list (PIDL) for a folder I wish to specify. Maybe ILCreateFromPath will return what I want. I need to generate an interface block for it of course.

If this fails, perhaps I could extract the SHILCreateFromPath object code from the XP shell32.dll and add it to the 2000 shell32.dll using EDITBIN?

0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,158 Views
I didn't look too deeply into the problem (I knew it's non-trivial to set up a different pidlRoot), but some Googling reveals this interesting page, which in turn points to IShellFolder::ParseDisplayName as a more straightforward method. I'm reluctant to use *ILCreateFromPath as it looks to me as one of "come and go" borderline APIs.

On the other hand, ParseDisplayName is a COM object method, so it takes a bit of cludge to get hold of it (take the pointer returned by SHGetDesktopFolder, then walk a bit through it to get to the VTable, ParseDisplayName being the first method, see Shlobj.h). Do you need an example?
0 Kudos
anthonyrichards
New Contributor III
3,158 Views
Thanks for the offer, Jugoslav, but I want to get into COM as much as I want to jump into a nest of vipers...and anyway, ILCreateFromPath is not in SHELL32.DLL version 5.0 on Windows 2000 Pro either...
0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,158 Views
I don't want to dig into COM any more than you do... However, some shell APIs are COM-based (e.g. creating a shortcut), and there's no workaround but to dig there as deep as necessary.

I've got an (almost) working sample for you... The "almost" part is due to COM of course, as I got an error in return value, but I can't deduce what's wrong, and I gotta run now. Tomorrow...
0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,158 Views
OK, a good night sleep makes wonders... the "almost" part wasn't due to COM but by myself Smiley with tongue out [:-P].

There you go (attached). Once you get an address of a COM object (in this case, an IShellFolder through SHGetDesktopFolder; most often, through CoCreateInstance), you should call DEREFERENCE_PROC to get the address of the method you want. (See the comments in DEREFERENCE_PROC how a COM object looks like on a "low-level" ). The first argument of COM methods is always a "this" pointer (i.e. that's the additional one as compared to documentation, which I forgot last night). The "magic number" 3 is the method's ordinal in the object's VTable... to know it, you have to dig out the original source (MSDN usually lists them in alphabetical order), Google for it, or in despair, try the hit-and-miss method. (The first three are IUnknown::QueryInterface, IUnknown::AddRef, and IUnknown::Release for all objects).

(The example is derived from my original XFTFile::XBrowse routine, so there is some extra stuff which doesn't work due to trimming)
0 Kudos
anthonyrichards
New Contributor III
3,158 Views

Thanks, but as I said, COM gives me the horrors. I downloaded the MS platform SDK and found SHELL32.LIB, which I included in my project. I used the attached interface block for SHILCreateFromFolder. I am not sure what to use for the third argument attributeiflagson input (it is defined as in:out in the MSDN library reference for SHILCreateFromFolder), so just stuck in a value I found that seemed reasonable.

The program compiled and linked OK. When I run it on my WIN2000 system, I get the result

PIDL= 0, IRET= 80070002

whatever I use for the Start path. That is, the PIDL value for the desktop. When compiled and run on my XP system, it crashes out with an access error. Any ideas?

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
character*(*):: pszPath
end function SHILCreateFromPath
end interface

CHARACTER(256) STARTPATH
integer IRET, PIDL, FLAGS

integer, parameter :: CSIDL_PROGRAM_FILES = '0026'x
integer, parameter :: SFGAO_FILESYSTEM = '40000000'x

! 'Special' folder PIDL value
PIDL=CSIDL_PROGRAM_FILES
startpath='c:program files'
startpath=trim(adjustl(startpath))//char(0)
IRET=SHILCREATEFROMPATH(STARTPATH, PIDL, SFGAO_FILESYSTEM)
WRITE(*,1000) PIDL,IRET
1000 FORMAT(1H ,'PIDL= ',Z10,', IRET= ',Z10)

PIDL= 0, IRET= 80070002

0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,158 Views
HRESULT 80070002: 'The system cannot find the file specified. '

But, according to MSDN, the first argument is a LPCWSTR, thus you need to declare the first argument as an INTEGER(2) array and convert the input string to wide char using MultiByteToWideChar?

0 Kudos
anthonyrichards
New Contributor III
3,158 Views

Thanks, I didn't spot that. Do I understand correctly that I need to

1) Set a character string for the path that I want
2) determine how many characters it contains (including a terminating null?)
3) allocate an INTEGER(2) array of the correct size to contain the converted string (twice the number of characters?)
4) use MultiByteToWideChar to convert the null-terminated character string to the Unicode equivalent using, for example

MultiByteToWideChar(CP_APC, MB_PRECOMPOSED, MYPATH, SIZEOF(MYPATH), MYUNICODEARRAY, SIZEOF(MYUNICODEARRAY) )

Can you tell me where to find values for CP_APC, MB_PRECOMPOSED?

Again, many thanks for your suggestions.

0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,158 Views
The example of MultiByteToWideChar is right above, in the file I attached and which you apparently didn't look at Surprised smiley [:-O]. Trust me, it's not more complicated than your approach. Both CP_ACP and MB_PRECOMPOSED are zero, I think (and I didn't bother to define them, just used zeros).

If MyPath is zero-terminated, you can set -1 for the length and you'll get the zero-terminated output array back.

If MyPath is not zero-terminated, set the length to LEN_TRIM(MyPath), and manually append 0 to the output array (like I did).


0 Kudos
anthonyrichards
New Contributor III
3,158 Views

I did look at your file (thanks again) but missed the MultiByte bit. I have added the code to convert the character string to unicode, changed the interface to SHILCreateFromPath as shown, added a call to CoInitialize (after getting an error message from SHILCreateFromPath that it hadn't been initialised) and repeated the program with the result shown below. I now get error code 8x0004005.

! For interfaces to GetACP and MultiByteToWideChar functions
USE kernel32

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


CHARACTER(256) STARTPATH
INTEGER(2) UCPATH(256)
integer IRET, PIDL, FLAGS, IACP, IWIDE, IBYTESNEED, IBYTESWRITTEN, I

integer, parameter :: CSIDL_PROGRAM_FILES = '0026'x
integer, parameter :: SFGAO_FILESYSTEM = '40000000'x

! had to add this because of previous error message from SHILCreateFromPath
iret=CoInitialize(0)

PIDL=CSIDL_PROGRAM_FILES
startpath='c:program fileszemax'
startpath=trim(adjustl(startpath))//char(0)

UCPATH=0
IACP=GetACP()
print *, 'IACP= ', IACP

IWIDE=0
IBYTESNEED=MultiByteToWideChar(IACP, 0, STARTPATH,-1,UCPATH, IWIDE)

PRINT *,'Number of wide characters needed= ', IBYTESNEED
IBYTESWRITTEN=MultiByteToWideChar(IACP, 0, STARTPATH,-1,UCPATH, IBYTESNEED)

PRINT *, 'IBYTESWRITTEN= ', IBYTESWRITTEN
PRINT *, 'UCPATH= ',(UCPATH(I),I=1,IBYTESWRITTEN)

IATTRIBUTES=0
IRET=SHILCREATEFROMPATH(UCPATH, PIDL, IATTRIBUTES)
WRITE(*,1000) PIDL,IRET, IATTRIBUTES
1000 FORMAT(1H 'PIDL= ',Z10,', IRET= ',Z10,', ATTRIBUTES= ',I8)

The output I get is:

IACP= 1252
Number of wide characters needed= 24
IBYTESWRITTEN= 24
UCPATH= 99 58 47 112 114 111 103 114 97 109
32 102 105 108 101 115 47 122 101 109 97
120 47 0
PIDL= 0, IRET= 80004005, ATTRIBUTES= 0

So something is still amiss..maybe it's because I have Win2000 and there are things missing from itthat I need?

0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,158 Views
Jolly well:

0x80004005: "Unspecified error" (there's Error Lookup program in your CVF folder)

For what it's worth, your code works for me on CVF under XP SP2. I only had to dynamically bind the shell32.dll because I used the CVF's default (oldish) shell32.lib. Maybe it's a safer bet -- if there's no SHILCreateFromPath in the system's Shell32.dll, you'll get a null pscfp right away:

end interface
pointer (pscfp, SHILCreateFromPath)
...
hShell = GetModuleHandle("shell32.dll"C)
pscfp = GetProcAddress(hShell, "SHILCreateFromPath"C)

IRET=SHILCREATEFROMPATH(UCPATH, PIDL, IATTRIBUTES)
If you still won't have anything with COM, it is suggested here that undocumented
SHSimpleIDListFromPath  works everywhere (but without promise as to the future).
0 Kudos
anthonyrichards
New Contributor III
3,158 Views

You may not believe this, but after trying everything else, when I changed

startpath='c:/Program Files/ZEMAX/'

to

startpath='c:Program FilesEMAX'

(i.e. forward slashes to back slashes in the folder name) it worked! Unbelievable!
I found that just one forward slash is enough to cause the function to reject the path as unacceptable!
However, I can't getSHILCreateFromPathto create a folder using the CSIDL_FLAG_CREATE attributes though, it returns with error 80070002

I Found 3 versions of ILCreateFromPath in the SHELL32LIB in the SDK and I got both the ASCII (ILCreateFromPathA) and Wide character (ILCreateFromPathW) versions to work as well.

Again, thanks for your help, it was much appreciated.

0 Kudos
Reply