Как объявить и реализовать интерфейс COM на C #, который наследуется от другого интерфейса COM? - PullRequest
5 голосов
/ 02 июня 2010

Я пытаюсь понять, как правильно реализовывать COM-интерфейсы из кода C #. Это просто, когда интерфейс не наследуется от другого базового интерфейса. Как этот:

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

Однако вещи начинают уставать, когда мне нужно реализовать интерфейс, который наследуется от других интерфейсов COM. Например, если я реализую интерфейс IPersistFolder2, который наследуется от IPersistFolder, который наследуется от IPersist, как я обычно использую код C #:

[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. Кто-нибудь знает, как правильно реализовать COM-интерфейсы в C #, который наследуется от других COM-интерфейсов и почему?

1 Ответ

3 голосов
/ 02 июня 2010

Аккуратный трюк с новым ключевым словом, который действительно решит проблему. Проблема в том, что COM не поддерживает наследование. Это просто нотационное удобство в IDL, чтобы он выглядел так, как он. На самом деле интерфейсная таблица должна объединять все «унаследованные» базовые интерфейсы. Другими словами, для интерфейса IPersistFolder он должен реплицировать слоты v-таблицы для 3 методов IUnknown и метода IPersist :: GetClassID. CLR заботится о IUnknown кстати.

V-таблица, создаваемая .NET, не совместима с этим макетом, она не реплицирует слоты методов базового класса.

...