实现 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
。
我正在 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
。