IShellFolder and IEnumIDList hell!

R

Robin Tucker

I can't get this damned IEnumIDList thing to work correctly. I've got the
desktop folder as an IShellObject (which I've verified by being able to get
the display name etc. from it). I can also get a pointer to the IEnumIDList
object via. GetIEnum. It even allows me to execute the "Fetch" method on
the interface (changed from "Next" because next is a VB keyword - in any
case, its just a function pointer in the VTable). But COM_pIDL always
returns zero in this case. I would expect at least a pointer to some child
pidls or something.

I know this is "VB", but I seem to be doing everything in the same was as
the C# and C++ demos I've seen. Maybe you can spot something dodgy here? :

(apologies for the cross post to the interop group but I'm not sure if you
guys check both)

Public Function GetDesktop() As ShellFolder

' Get the root folder from the shell

Dim COM_theRoot As ShellLib.IShellFolder

COM_theRoot = ShellLib.ShellFunctions.GetDesktopFolder()
If COM_theRoot Is Nothing Then
Exit Function
End If

' the above works, I've verified it returns the "Desktop" IShellFolder

Dim COM_theEnum As ShellLib.IEnumIDList =
ShellLib.ShellFunctions.GetIEnum(COM_theRoot)
Dim COM_pIDL As IntPtr

Try
Do
COM_theEnum.Fetch(1, COM_pIDL, 0)
Debug.WriteLine(COM_pIDL.ToString())
Loop While not COM_pIDL.Equals(IntPtr.Zero)

Catch Ex As Exception

End Try
End Function

' /////////////////////////////////////////////////////////////////////////
' //
' // IEnumIDList
' //
' /////////////////////////////////////////////////////////////////////////

<ComImportAttribute(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList

<PreserveSig()> _
Function Fetch(ByVal cElt As Integer, ByVal rGelt As IntPtr, ByRef
pCeltFetched As Integer) As Integer

<PreserveSig()> _
Sub Skip(ByVal cElt As Integer)

<PreserveSig()> _
Sub Reset()

<PreserveSig()> _
Sub Clone(ByRef pEnum As IEnumIDList)

End Interface
 
J

Jay B. Harlow [MVP - Outlook]

Robin,
Try the following (untested):
<ComImportAttribute(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList

<PreserveSig()> _
Function [Next](ByVal cElt As Integer, ByRef rGelt As IntPtr, ByRef
pCeltFetched As Integer) As Integer

<PreserveSig()> _
Sub Skip(ByVal cElt As Integer)

<PreserveSig()> _
Sub Reset()

<PreserveSig()> _
Sub Clone(ByRef pEnum As IEnumIDList)

End Interface

By making rGelt ByVal you were effectively making it input only, you need to
make it ByRef to allow it to be "returned", remember its LPITEMIDLIST* (a
pointer to a long pointer to an ITEMIDLIST).

Also, notice that I named the Next method [Next], this allows a keyword to
be used as an identifier, when using next with an actual object, the [] are
not needed...

Note: I did not test the above...

Hope this helps
Jay
 
R

Robin Tucker

Yes, I noticed this and tried it byref too, but still the same result.
Perhaps the way I get the interface is incorrect? Here it is (for some
reason, my namespace ShellLib, contains IEnumIDList, but shellEnumType is
returning "nothing"??).

Public Shared Function GetIEnum(ByRef theFolder As IShellFolder) As
IEnumIDList

Dim ptrRet As IntPtr

theFolder.EnumObjects(IntPtr.Zero, 0, ptrRet)

Dim shellEnumType As System.Type =
System.Type.GetType("ShellLib.IEnumIDList")
Dim obj As [Object] = Marshal.GetTypedObjectForIUnknown(CType(ptrRet,
IntPtr), shellEnumType)
Dim iEnumFolder As IEnumIDList = CType(obj, IEnumIDList)

Return iEnumFolder

End Function

Jay B. Harlow said:
Robin,
Try the following (untested):
<ComImportAttribute(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList

<PreserveSig()> _
Function [Next](ByVal cElt As Integer, ByRef rGelt As IntPtr, ByRef
pCeltFetched As Integer) As Integer

<PreserveSig()> _
Sub Skip(ByVal cElt As Integer)

<PreserveSig()> _
Sub Reset()

<PreserveSig()> _
Sub Clone(ByRef pEnum As IEnumIDList)

End Interface

By making rGelt ByVal you were effectively making it input only, you need to
make it ByRef to allow it to be "returned", remember its LPITEMIDLIST* (a
pointer to a long pointer to an ITEMIDLIST).

Also, notice that I named the Next method [Next], this allows a keyword to
be used as an identifier, when using next with an actual object, the [] are
not needed...

Note: I did not test the above...

Hope this helps
Jay


Robin Tucker said:
I can't get this damned IEnumIDList thing to work correctly. I've got the
desktop folder as an IShellObject (which I've verified by being able to get
the display name etc. from it). I can also get a pointer to the IEnumIDList
object via. GetIEnum. It even allows me to execute the "Fetch" method on
the interface (changed from "Next" because next is a VB keyword - in any
case, its just a function pointer in the VTable). But COM_pIDL always
returns zero in this case. I would expect at least a pointer to some child
pidls or something.

I know this is "VB", but I seem to be doing everything in the same was as
the C# and C++ demos I've seen. Maybe you can spot something dodgy
here?
:

(apologies for the cross post to the interop group but I'm not sure if you
guys check both)

Public Function GetDesktop() As ShellFolder

' Get the root folder from the shell

Dim COM_theRoot As ShellLib.IShellFolder

COM_theRoot = ShellLib.ShellFunctions.GetDesktopFolder()
If COM_theRoot Is Nothing Then
Exit Function
End If

' the above works, I've verified it returns the "Desktop" IShellFolder

Dim COM_theEnum As ShellLib.IEnumIDList =
ShellLib.ShellFunctions.GetIEnum(COM_theRoot)
Dim COM_pIDL As IntPtr

Try
Do
COM_theEnum.Fetch(1, COM_pIDL, 0)
Debug.WriteLine(COM_pIDL.ToString())
Loop While not COM_pIDL.Equals(IntPtr.Zero)

Catch Ex As Exception

End Try
End Function

' /////////////////////////////////////////////////////////////////////////
' //
' // IEnumIDList
' //
' /////////////////////////////////////////////////////////////////////////
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList

<PreserveSig()> _
Function Fetch(ByVal cElt As Integer, ByVal rGelt As IntPtr, ByRef
pCeltFetched As Integer) As Integer

<PreserveSig()> _
Sub Skip(ByVal cElt As Integer)

<PreserveSig()> _
Sub Reset()

<PreserveSig()> _
Sub Clone(ByRef pEnum As IEnumIDList)

End Interface
 
R

Robin Tucker

My dumb mistake (apart from the byref faux pas earlier),

I was passing in 0 for flags, rather than SHCONTF_FOLDERS! It works now,
I've got more piddles than I know what to do with.


theFolder.EnumObjects(IntPtr.Zero, ShellAPI.SHCONTF.SHCONTF_FOLDERS, ptrRet)








Robin Tucker said:
Yes, I noticed this and tried it byref too, but still the same result.
Perhaps the way I get the interface is incorrect? Here it is (for some
reason, my namespace ShellLib, contains IEnumIDList, but shellEnumType is
returning "nothing"??).

Public Shared Function GetIEnum(ByRef theFolder As IShellFolder) As
IEnumIDList

Dim ptrRet As IntPtr

theFolder.EnumObjects(IntPtr.Zero, 0, ptrRet)

Dim shellEnumType As System.Type =
System.Type.GetType("ShellLib.IEnumIDList")
Dim obj As [Object] = Marshal.GetTypedObjectForIUnknown(CType(ptrRet,
IntPtr), shellEnumType)
Dim iEnumFolder As IEnumIDList = CType(obj, IEnumIDList)

Return iEnumFolder

End Function

Jay B. Harlow said:
Robin,
Try the following (untested):
<ComImportAttribute(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList

<PreserveSig()> _
Function [Next](ByVal cElt As Integer, ByRef rGelt As IntPtr, ByRef
pCeltFetched As Integer) As Integer

<PreserveSig()> _
Sub Skip(ByVal cElt As Integer)

<PreserveSig()> _
Sub Reset()

<PreserveSig()> _
Sub Clone(ByRef pEnum As IEnumIDList)

End Interface

By making rGelt ByVal you were effectively making it input only, you
need
to
make it ByRef to allow it to be "returned", remember its LPITEMIDLIST* (a
pointer to a long pointer to an ITEMIDLIST).

Also, notice that I named the Next method [Next], this allows a keyword to
be used as an identifier, when using next with an actual object, the [] are
not needed...

Note: I did not test the above...

Hope this helps
Jay


Robin Tucker said:
I can't get this damned IEnumIDList thing to work correctly. I've got the
desktop folder as an IShellObject (which I've verified by being able
to
get
the display name etc. from it). I can also get a pointer to the IEnumIDList
object via. GetIEnum. It even allows me to execute the "Fetch" method on
the interface (changed from "Next" because next is a VB keyword - in any
case, its just a function pointer in the VTable). But COM_pIDL always
returns zero in this case. I would expect at least a pointer to some child
pidls or something.

I know this is "VB", but I seem to be doing everything in the same was as
the C# and C++ demos I've seen. Maybe you can spot something dodgy
here?
:

(apologies for the cross post to the interop group but I'm not sure if you
guys check both)

Public Function GetDesktop() As ShellFolder

' Get the root folder from the shell

Dim COM_theRoot As ShellLib.IShellFolder

COM_theRoot = ShellLib.ShellFunctions.GetDesktopFolder()
If COM_theRoot Is Nothing Then
Exit Function
End If

' the above works, I've verified it returns the "Desktop" IShellFolder

Dim COM_theEnum As ShellLib.IEnumIDList =
ShellLib.ShellFunctions.GetIEnum(COM_theRoot)
Dim COM_pIDL As IntPtr

Try
Do
COM_theEnum.Fetch(1, COM_pIDL, 0)
Debug.WriteLine(COM_pIDL.ToString())
Loop While not COM_pIDL.Equals(IntPtr.Zero)

Catch Ex As Exception

End Try
End Function

'
/////////////////////////////////////////////////////////////////////////
' //
' // IEnumIDList
' //
'
/////////////////////////////////////////////////////////////////////////
<ComImportAttribute(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList

<PreserveSig()> _
Function Fetch(ByVal cElt As Integer, ByVal rGelt As IntPtr, ByRef
pCeltFetched As Integer) As Integer

<PreserveSig()> _
Sub Skip(ByVal cElt As Integer)

<PreserveSig()> _
Sub Reset()

<PreserveSig()> _
Sub Clone(ByRef pEnum As IEnumIDList)

End Interface
 
J

Jay B. Harlow [MVP - Outlook]

Robin,
I *hate* when that (forgetting a flag) happens. :)

Glad you got it to work.

Jay

Robin Tucker said:
My dumb mistake (apart from the byref faux pas earlier),

I was passing in 0 for flags, rather than SHCONTF_FOLDERS! It works now,
I've got more piddles than I know what to do with.


theFolder.EnumObjects(IntPtr.Zero, ShellAPI.SHCONTF.SHCONTF_FOLDERS, ptrRet)








Robin Tucker said:
Yes, I noticed this and tried it byref too, but still the same result.
Perhaps the way I get the interface is incorrect? Here it is (for some
reason, my namespace ShellLib, contains IEnumIDList, but shellEnumType is
returning "nothing"??).

Public Shared Function GetIEnum(ByRef theFolder As IShellFolder) As
IEnumIDList

Dim ptrRet As IntPtr

theFolder.EnumObjects(IntPtr.Zero, 0, ptrRet)

Dim shellEnumType As System.Type =
System.Type.GetType("ShellLib.IEnumIDList")
Dim obj As [Object] = Marshal.GetTypedObjectForIUnknown(CType(ptrRet,
IntPtr), shellEnumType)
Dim iEnumFolder As IEnumIDList = CType(obj, IEnumIDList)

Return iEnumFolder

End Function

Robin,
Try the following (untested):

<ComImportAttribute(),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList

<PreserveSig()> _
Function [Next](ByVal cElt As Integer, ByRef rGelt As IntPtr, ByRef
pCeltFetched As Integer) As Integer

<PreserveSig()> _
Sub Skip(ByVal cElt As Integer)

<PreserveSig()> _
Sub Reset()

<PreserveSig()> _
Sub Clone(ByRef pEnum As IEnumIDList)

End Interface

By making rGelt ByVal you were effectively making it input only, you
need
to
make it ByRef to allow it to be "returned", remember its LPITEMIDLIST* (a
pointer to a long pointer to an ITEMIDLIST).

Also, notice that I named the Next method [Next], this allows a
keyword
to
be used as an identifier, when using next with an actual object, the
[]
are
not needed...

Note: I did not test the above...

Hope this helps
Jay


message I can't get this damned IEnumIDList thing to work correctly. I've
got
the
desktop folder as an IShellObject (which I've verified by being able to
get
the display name etc. from it). I can also get a pointer to the
IEnumIDList
object via. GetIEnum. It even allows me to execute the "Fetch"
method
on
the interface (changed from "Next" because next is a VB keyword - in any
case, its just a function pointer in the VTable). But COM_pIDL always
returns zero in this case. I would expect at least a pointer to some
child
pidls or something.

I know this is "VB", but I seem to be doing everything in the same
was
as
the C# and C++ demos I've seen. Maybe you can spot something dodgy here?
:

(apologies for the cross post to the interop group but I'm not sure
if
you
guys check both)

Public Function GetDesktop() As ShellFolder

' Get the root folder from the shell

Dim COM_theRoot As ShellLib.IShellFolder

COM_theRoot = ShellLib.ShellFunctions.GetDesktopFolder()
If COM_theRoot Is Nothing Then
Exit Function
End If

' the above works, I've verified it returns the "Desktop" IShellFolder

Dim COM_theEnum As ShellLib.IEnumIDList =
ShellLib.ShellFunctions.GetIEnum(COM_theRoot)
Dim COM_pIDL As IntPtr

Try
Do
COM_theEnum.Fetch(1, COM_pIDL, 0)
Debug.WriteLine(COM_pIDL.ToString())
Loop While not COM_pIDL.Equals(IntPtr.Zero)

Catch Ex As Exception

End Try
End Function

'
/////////////////////////////////////////////////////////////////////////
' //
' // IEnumIDList
' //
'
/////////////////////////////////////////////////////////////////////////

<ComImportAttribute(),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList

<PreserveSig()> _
Function Fetch(ByVal cElt As Integer, ByVal rGelt As IntPtr, ByRef
pCeltFetched As Integer) As Integer

<PreserveSig()> _
Sub Skip(ByVal cElt As Integer)

<PreserveSig()> _
Sub Reset()

<PreserveSig()> _
Sub Clone(ByRef pEnum As IEnumIDList)

End Interface
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top