Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Interop / April 2006

Tip: Looking for answers? Try searching our database.

Help with Marshaling an Array of Pointers from COM to Managed Objects

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Joshua Taylor - 14 Apr 2006 19:50 GMT
Ok so I've been beating my brains out over this for two days now pouring
through the SDK, MSDN Documentation, and the Internet at large.
I'm not new to programming or .NET, but definately new to Interop and shell
programming, and am getting tripped up by all the marshaling, importing, and

pointers.

I'm trying to get the contents of the Recycle Bin, and specifically the file
names and deleted dates.  I found a good C++ example at

http://www.codeproject.com/shell/recyclebin.asp, and have been sucessfull in
getting a managed IShellFolder object for the Recycle Bin itself.  I can get

it's display name etc.  I have even managed to convert the IEnumIDList and
IShellFolder2 interfaces over successfully (I think).

The problem comes when I call IEnumIDList.Next, and then try and marshal the
resulting pointers to managed (IShellFolder2) objects.  I'm sure that I'm
not

doing something right with rgelt (the "address of an array of ITEMIDLIST
pointers") returned by IEnumIDList.Next.  I get a
System.ExecutionEngineException on

the following line:

Dim objRecycledItem As Object = Marshal.GetTypedObjectForIUnknown(pidl(0),
System.Type.GetType("IShellFolder2"))

I've included what I think are the relevant portions of code below.  If more
information is required let me know and I'd be happy to post it.
Any help, advice, thoughts, or links to infomration would be greatly
appreciated.

Thanks in advance.

Here is the function that is throwing the exception:

Sub Test()
       Dim pDesktop As IntPtr
       Dim pidlRecyclebin As IntPtr
       Dim pRecyclebin As IntPtr

        'Get a PIDL for the desktop
       Shell32.SHGetDesktopFolder(pDesktop)
       Shell32.SHGetSpecialFolderLocation(Me.Handle,
Shell32.Enums.CSIDL.CSIDL_BITBUCKET, pidlRecyclebin)

       'Marshal pDesktop to a managed IShellFolder object
       Dim objDesktop As Object =
Marshal.GetTypedObjectForIUnknown(pDesktop,
System.Type.GetType("IShellFolder"))
       Dim desktop As IShellFolder = DirectCast(objDesktop, IShellFolder)

       'Bind the pRecyclebin PIDL
       desktop.BindToObject(pidlRecyclebin, IntPtr.Zero,
Shell32.ShellGUIDs.IID_IShellFolder, pRecyclebin)

       'Get a STRRET structure with the recycle bin name (for testing just
to know our PIDL really is working)
       Dim strret As Shell32.Structures.STRRET
       Dim name As String
       desktop.GetDisplayNameOf(pidlRecyclebin,
Convert.ToUInt32(Shell32.Enums.SHGNO.SHGDN_NORMAL), strret)
       Shell32.StrRetToBSTR(strret, pidlRecyclebin, name)
       Debug.WriteLine("Recyle Bin Name: " & name)

       'Marshal the pRecyclebin PIDL to a managed IShellFolder object
       Dim objRB As Object = Marshal.GetTypedObjectForIUnknown(pRecyclebin,
System.Type.GetType("IShellFolder"))
       Dim Recyclebin As IShellFolder = DirectCast(objRB, IShellFolder)

       'Use the RecycleBin IShellFolder object to enumerate it's contents
       Dim ppEnumIDList As IntPtr
       Recyclebin.EnumObjects(Me.Handle,
Shell32.Enums.SHCONTF.SHCONTF_FOLDERS Or
Shell32.Enums.SHCONTF.SHCONTF_NONFOLDERS _
         Or Shell32.Enums.SHCONTF.SHCONTF_INCLUDEHIDDEN, ppEnumIDList)

       Dim objEnumIDList As Object =
Marshal.GetTypedObjectForIUnknown(ppEnumIDList,
System.Type.GetType("IEnumIDList"))
       Dim EnumIDList As IEnumIDList = DirectCast(objEnumIDList,
IEnumIDList)

       Dim pidl As IntPtr()
       While Not EnumIDList.Next(Convert.ToUInt32(1), pidl,
IntPtr.Zero).Equals(Convert.ToUInt32(Shell32.Constants.S_FALSE))
ERR HERE-> Dim objRecycledItem As Object =
Marshal.GetTypedObjectForIUnknown(pidl(0),
System.Type.GetType("IShellFolder2"))
                       Dim RecycledItem As IShellFolder2 =
DirectCast(objRecycledItem, IShellFolder2)

                        '...

                      Marshal.ReleaseComObject(RecycledItem)
       End While

       'Release our com objects
       Marshal.ReleaseComObject(desktop)
       Marshal.ReleaseComObject(Recyclebin)
       Marshal.ReleaseComObject(EnumIDList)
End Sub

Here is my Import of IEnumIDList:

<ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F2-0000-0000-C000-000000000046")> _
Public Interface IEnumIDList
   'Retrieves the specified number of item identifiers in the
   'enumeration sequence and advances the current position by
   'the number of items retrieved.
   '[in] celt Number of elements in the array pointed to by the rgelt
parameter.
   '[out] rgelt Address of an array of ITEMIDLIST pointers that receives
the item
   '      identifiers. The implementation must allocate these item
identifiers
   '      using the Shell's allocator (retrieved by the SHGetMalloc
function).
   '      The calling application is responsible for freeing the item
   '      identifiers using the Shell's allocator.
   '[out] pceltFetched Address of a value that receives a count of the item
identifiers
   '      actually returned in rgelt. The count can be smaller than the
value
   '      specified in the celt parameter. This parameter can be NULL only
   '      if celt is one.
   <PreserveSig()> _
   Function [Next](ByVal celt As UInt32, <MarshalAs(UnmanagedType.LPArray)>
ByRef rgelt As IntPtr(), ByRef pceltFetched As IntPtr) As UInt32

   'Skips over the specified number of elements in the enumeration
sequence.
   '[in] Number of item identifiers to skipt.
   <PreserveSig()> _
   Function Skip(ByVal celt As UInt32) As UInt32

   'Returns to the beginning of the enumeration sequence.
   <PreserveSig()> _
   Function Reset() As UInt32

   'Creates a new item enumeration object with the same contents and state
as the current one.
   '[out] ppenum Address of a pointer to the new enumeration object. The
calling
   '      application must eventually free the new object by calling its
Release member function.
   <PreserveSig()> _
   Function Clone(ByRef ppenum As IEnumIDList) As UInt32
End Interface

And my import of ISHellFolder2:

<ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("93F2F68C-1D1B-11D3-A30E-00C04F79ABD1")> _
Public Interface IShellFolder2
   Inherits IShellFolder

   'Requests a pointer to an interface that allows a client to enumerate
the available search objects.
   'ppEnum [out] Address of a pointer to an enumerator object's
IEnumExtraSearch interface.
   <PreserveSig()> _
   Function EnumSearches(ByRef ppenum As IntPtr) As UInt32

   'Retreves the default sorting and display columns.
   'dwReserved [in] Reserved. Set to zero.
   'pSort [out] Pointer to a value that receives the index of the default
sorted column.
   'pDisplay [out] Pointer to a value that receives the index of the
default display column.
   'Return Value: Returns NOERROR if successful, or a COM error value
otherwise.
   <PreserveSig()> _
   Function GetDefaultColumn(ByVal dwReserved As UInt32, ByRef pSort As
IntPtr, ByRef pDisplay As IntPtr) As UInt32

   'Retrieves the default state for a specified column.
   'iColumn [in] Integer that specifies the column number.
   'pcsFlags [out] Pointer to a value that contains flags that indicate
   '               the default column state. This parameter can include
   '               a combination of the following flags.
   '               SHCOLSTATE_TYPE_STR A string.
   '               SHCOLSTATE_TYPE_INT An integer.
   '               SHCOLSTATE_TYPE_DATE A date.
   '               SHCOLSTATE_ONBYDEFAULT Should be shown by default in the
   '                                      Microsoft Windows Explorer
Details view.
   '               SHCOLSTATE_SLOW Extracting information about the column
can be
   '                               time consuming. This flag recommends
that the
   '                               folder view extract column information
   '                               asynchronously, on a background thread.
   '               SHCOLSTATE_EXTENDED Provided by a handler, not the
folder object.
   '               SHCOLSTATE_SECONDARYUI Not displayed in the shortcut
menu,
   '                                      but listed in the More dialog
box.
   '               SHCOLSTATE_HIDDEN Not displayed in the user interface.
   '               SHCOLSTATE_PREFER_VARCMP Uses default sorting rather
than
   '                                        IShellFolder::CompareIDs to get
the
   '                                        sort order.
   'Return VAlue: Returns S_OK if successful, or an error value otherwise.
   <PreserveSig()> _
   Function GetDefaultColumnState(ByVal iColumn As UInt32, ByRef pcsFlags
As IntPtr) As UInt32

   'Returns the globally unique identifier (GUID) of the default search
object for the folder.
   'pguid [out] GUID of the default search object.
   'Return Value: Returns NOERROR if successful, or an OLE error value
otherwise.
   <PreserveSig()> _
   Function GetDefaultSearchGUID(ByRef GUID As IntPtr) As UInt32

   'Retrieves detailed information, identified by a property set identifier
(FMTID) and a property
   'identifier (PID), on an item in a Shell folder.
   'pidl [in] PIDL of the item, relative to the parent folder. This method
accepts only single-level
   '          PIDLs. The structure must contain exactly one SHITEMID
structure followed by a
   '          terminating zero.
   'pscid [in] Pointer to an SHCOLUMNID structure that identifies the
column.
   'pv [out] Pointer to a VARIANT with the requested information. The value
will be fully typed.
   'Return Value: Returns S_OK if successful, or an error value otherwise.
   <PreserveSig()> _
   Function GetDetailsEx(ByVal pidl As IntPtr, ByVal pscid As IntPtr, ByRef
pv As IntPtr) As UInt32

   'Retrieves detailed information, identified by a column index, on an
item in a Shell folder.
   'pidl [in] Pointer to an item identifier list (PIDL) of the item for
which you are requesting
   '          information. This method accepts only single-level PIDLs. The
structure must
   '          contain exactly one SHITEMID structure followed by a
terminating zero. If this
   '          parameter is set to NULL, the title of the information field
specified by iColumn
   '          is returned.
   'iColumn [in] Zero-based index of the desired information field. It is
identical to the
   '             column number of the information as it is displayed in a
Microsoft Windows
   '             Explorer Details view.
   'psd [out] Pointer to a SHELLDETAILS structure that contains the
information.
   'Return Value: Returns S_OK if successful, or an error value otherwise.
   <PreserveSig()> _
   Function GetDetailsOf(ByVal pidl As IntPtr, ByVal iColumn As UInt32,
ByRef psd As IntPtr) As UInt32

   'Converts a column to the appropriate property set ID (FMTID) and
property ID (PID).
   'iColumn [in] Column ID
   'pscid [out] Pointer to an SHCOLUMNID structure containing the FMTID and
PID.
   'Return Value: Returns NOERROR if successful, or a COM error value
otherwise.
   <PreserveSig()> _
   Function MapColumnToSCID(ByVal icolumn As UInt32, ByVal pscid As IntPtr)
As UInt32
End Interface
Dmytro Lapshyn [MVP] - 17 Apr 2006 12:58 GMT
Hi Joshua,

Why do you do a manual import of IShellFolder2? I'd suggest that you found
the unmanaged .DLL implementing this interface and applied tlbimp.exe to
that DLL. This is much more reliable.
Joshua Taylor - 19 Apr 2006 20:54 GMT
Honestly, because the whole COM Interop thing is new to me, and in reading
the documentation, I got the impression that tlbimp.exe only worked with
.TLBs, and I was also under the impression that IShellFolder2 was
implemented in shell32.dll.  But, under futher investigation, you are
correct (but you knew that <g>) in that tlbimp.exe works for .DLLs also.
However, after importing shell32.dll, I'm not seeing IShellFolder2.  I must
be missing something.  Can you point me in the right direction for finding
which library implements this interface?

Thanks Again,
Josh
Dmytro Lapshyn [MVP] - 20 Apr 2006 11:34 GMT
There's a fellow MVP's site where he hosts some old VB6 stuff:

http://www.mvps.org/emorcillo/en/code/vb6/index.shtml

One of the samples is dedicated to IShellFolder. Hopefully there's a TLB in
the sample you can reuse. Anyway, you can probably ask the guy for the
assistance - he's an MVP and should be willing to help.

> Honestly, because the whole COM Interop thing is new to me, and in reading
> the documentation, I got the impression that tlbimp.exe only worked with
[quoted text clipped - 7 lines]
> Thanks Again,
> Josh
Dmytro Lapshyn [MVP] - 20 Apr 2006 11:40 GMT
See also this discussion. It's about C++, but the basic principles still
apply.

http://groups.google.com/group/microsoft.public.vc.atl/browse_thread/thread/ce1b
45bcb76fa2d2/48aa1ef81cf8dabe?lnk=st&q=IShellFolder2+tlb&rnum=2&hl=en#48aa1ef81c
f8dabe


That is, if you can't find the ready-made TLB, compile your own one from the
supplied IDL file (shobjidl.idl) with the MIDL tool. Or, indeed, resort to
the manual interface declaration which might be more accurate that the TLB
due to the reasons described in the referenced post.

IMPORTANT!!! If you do your own .NET interface declaration, use the IDL as
the source, but *NOT MSDN*.
This is because it is not guaranteed the method and property order and
declarations are correct in the MSDN docs, but they are practically 100%
accurate in the IDL.

> Honestly, because the whole COM Interop thing is new to me, and in reading
> the documentation, I got the impression that tlbimp.exe only worked with
[quoted text clipped - 7 lines]
> Thanks Again,
> Josh
Joshua Taylor - 20 Apr 2006 23:42 GMT
Thanks for both the replys.  I have actually used Eduardo Morcillo's OLELIB
type library for a rudimentary word add-in I had done quite a while back,
and had completely forgot that the IShellFolder interface was in there.  I
had never checked on IShellFolder2, but just downloaded it again and checked
and it is.

Also, on the note about not using MSDN.  I was not aware that the order in
which the methods are defined are important.  I was under the impression
that unless told otherwise Interop was matching the imported functions based
on name.  I will check out both the OLELIB type library and the IDL and
re-order accordingly.

Thanks for the pointers, and all the help.  I've at least got a new
direction to try,
Josh

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.