实现 IParentAndItem COM 接口的正确方法

Proper way of implementing IParentAndItem COM Interface

我正在 C# 中实现基于 IShellItem/IShellItem2 COM 接口的对象模型,目前正在研究项目枚举。我想避免使用 BindToHandler 因为它被认为对性能有显着影响,这就是为什么我想使用 IParentAndItem 接口及其底层缓存机制。

根据 MSDN,IParentAndItem 接口的方法定义如下:

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
);

但是,以下转换为 C# COM Interop 接口会导致 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);
}

我正在使用接口从 IShellItem2 转换为使用其 GetParentAndItem 方法获得 IShellFolder 表示:

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
}

更新

Hans Passant 指出 IParentAndItem 的正确声明:

[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);
}

在实施 IParentAndItem 并将 BindToHandler 替换为 GetParentAndItem 之后,我只能说这条路线的性能非常值得。

确保在完成这些后调用 Marshal.FreeCoTaskMem 释放在 GetParentAndItem 中为两个 PIDL 分配的内存。

它应该会崩溃。不知道它是怎么发生的,但我能猜到。 MSDN 文档有点笨拙,它没有按照它们在界面中出现的顺序记录方法。使用 SDK 中的 ShObjIdl.idl 进行验证,首先是 Set 方法。所以现在你正在调用完全错误的方法:)只需在你的 C# 声明中交换它们。

您的 SetParentAndItem() 声明有错误,您必须从第二个参数中删除 ref