如何在C#中声明和实现一个继承自另一个COM接口的COM接口?

10

我试图理解在C#代码中正确实现COM接口的方式。当接口不继承其他基础接口时,这是很直截了当的,就像这样:

[ComImport, Guid("2047E320-F2A9-11CE-AE65-08002B2E1262"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IShellFolderViewCB
{
  long MessageSFVCB(uint uMsg, int wParam, int lParam);
}

然而,当我需要实现一个继承自其他COM接口的接口时,事情开始变得奇怪。例如,如果我像通常在C#代码中那样实现继承自IPersistIPersistFolder接口,以及继承自IPersistFolderIPersistFolder2接口:

[ComImport, Guid("0000010c-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersist
{
  void GetClassID([Out] out Guid classID);
}

[ComImport, Guid("000214EA-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersistFolder : IPersist
{
  void Initialize([In] IntPtr pidl);
}

[ComImport, Guid("1AC3D9F0-175C-11d1-95BE-00609797EA4F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersistFolder2 : IPersistFolder
{
  void GetCurFolder([Out] out IntPtr ppidl);
}

操作系统无法调用我的对象实现中的方法。在调试时,我可以看到多次调用了IPersistFolder2实现的构造函数,但我实现的接口方法没有被调用。我按照以下方式实现了IPersistFolder2
[Guid("A4603CDB-EC86-4E40-80FE-25D5F5FA467D")]
public class PersistFolder: IPersistFolder2
{
  void IPersistFolder2.GetClassID(ref Guid classID) { ... }
  void IPersistFolder2.Initialize(IntPtr pidl) { ... }
  void IPersistFolder2.GetCurFolder(out IntPtr ppidl) { ... }
}

奇怪的是,当我将COM接口导入声明如下时,它可以工作:

[ComImport, Guid("0000010c-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPersist
{
  void GetClassID([Out] out Guid classID);
}

[ComImport, Guid("000214EA-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPersistFolder : IPersist
{
  new void GetClassID([Out] out Guid classID);
  void Initialize([In] IntPtr pidl);
}

[ComImport, Guid("1AC3D9F0-175C-11d1-95BE-00609797EA4F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPersistFolder2 : IPersistFolder
{
  new void GetClassID([Out] out Guid classID);
  new void Initialize([In] IntPtr pidl);
  void GetCurFolder([Out] out IntPtr ppidl);
}

我不知道为什么以这种方式声明COM接口可以工作(使用new隐藏基础接口方法)。也许与IUnknown的工作方式有关。有人知道在C#中实现继承其他COM接口的正确方法及其原因吗?


1
投票支持你,因为你向我展示了如何“导入”IPersist。https://dev59.com/4nM_5IYBdhLWcg3wvFvI使用它,但没有提到如何添加对它的引用。 - Chry Cheng
我遇到了同样的问题,并且使用'new'关键字成功解决了它。同时感谢@Hans Passant :) - user2479356
1个回答

7
使用新关键字的巧妙技巧确实可以解决问题。问题在于COM不支持继承,它只是在IDL中提供了表示上的便利。实际上,接口v-table必须聚合所有“继承”的基础接口。换句话说,对于IPersistFolder接口,它必须复制3个IUnknown方法和IPersist::GetClassID方法的v-table插槽。顺便说一下,CLR会处理IUnknown。.NET构建的v-table与此布局不兼容,它不会复制基类方法插槽。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接