0

I'm implementing an object model based on the IShellItem/IShellItem2 COM interfaces in C# and am currently working on item enumeration. I want to avoid using BindToHandler as it is considered to have a notable impact on performance, this is why i want to use the IParentAndItem interface and its underlying caching mechanisms.

According to MSDN the methods of the IParentAndItem interface are defined as follows:

HRESULT GetParentAndItem(
  [out, optional]  PIDLIST_ABSOLUTE *ppidlParent,
  [out, optional]  IShellFolder **ppsf,
  [out, optional]  PITEMID_CHILD *ppidlChild
);

HRESULT SetParentAndItem(
  [in]  PCIDLIST_ABSOLUTE pidlParent,
  [in]  IShellFolder *psf,
  [in]  PCUITEMID_CHILD pidlChild
);

However the following translation into a C# COM Interop interface results in an AccessViolationException:

[ComImport,
 Guid("B3A4B685-B685-4805-99D9-5DEAD2873236"), 
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IParentAndItem
{
    void GetParentAndItem(out IntPtr ppidlParent, out IShellFolder ppsf, out IntPtr ppidlChild);

    void SetParentAndItem(IntPtr pidlParent, ref IShellFolder psf, IntPtr pidlChild);
}

I'm using the interface to cast from an IShellItem2 to get the IShellFolder representation using its GetParentAndItem method:

IParentAndItem pni = si as IParentAndItem; // 'si' is the IShellItem2
if(pni != null)
{
   IntPtr ppidlParent, ppidlChild;
   IShellFolder ppsf;
   pni.GetParentAndItem(out ppidlParent, out ppsf, out ppidlChild); // <-- throws AccessViolationException
   // Work with ppsf, e.g. EnumObjects
}

UPDATE

The correct declaration of IParentAndItem as pointed out by Hans Passant:

[ComImport,
 Guid("B3A4B685-B685-4805-99D9-5DEAD2873236"), 
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IParentAndItem
{
    void SetParentAndItem(IntPtr pidlParent, IShellFolder psf, IntPtr pidlChild);

    void GetParentAndItem(out IntPtr ppidlParent, out IShellFolder ppsf, out IntPtr ppidlChild);
}

After implementing IParentAndItem and replacing BindToHandler with GetParentAndItem all i can say it is really worth going this route performance wise.

Be sure to free memory allocated in GetParentAndItem for the two PIDLs by calling Marshal.FreeCoTaskMem after you're done with those.

atomicode
  • 561
  • 4
  • 10

1 Answers1

1

It is supposed to crash. Not sure how it happened but I can guess. The MSDN docs are a bit clumsy, it does not document the methods in the order in which they appear in the interface. Verify with ShObjIdl.idl from the SDK, the Set method is first. So right now you are calling the completely wrong method :) Just swap them in your C# declaration.

Your SetParentAndItem() declaration has a bug, you must remove ref from the second argument.

Hans Passant
  • 873,011
  • 131
  • 1,552
  • 2,371
  • you're right, just checked and SetParentAndItem is declared first. Not sure where that ref came from as it is clearly noted as In param - thanks for pointing that out. I also missed to free the memory allocated for the 2 PIDLs. – atomicode Mar 31 '15 at 19:04