Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare and implement a COM interface on C# that inherits from another COM interface?

Tags:

c#

interop

com

I'm trying to understand what is the correct why to implement COM interfaces from C# code. It is straightforward when the interface doesn't inherit from other base interface. Like this one:

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

However things start to become weired when I need to implement an interface that inherits from other COM interfaces. For example, if I implement the IPersistFolder2 interface which inherits from IPersistFolder which inherits from IPersist as I usually on C# code:

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

The operating system is not able to call the methods on my object implementation. When I'm debugging I can see the constructor of my IPersistFolder2 implementation is called many times, however the interface methods I've implemented aren't called. I'm implementing the IPersistFolder2 as follows:

[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) { ... }
}

What seems strange is when I declare the COM interface imports as follow, it works:

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

I don't know why it works when I declare the COM interfaces that way (hidding the base interface methods using new). Maybe it is related to the way IUnknown works. Does anyone know what is the correct way of implementing COM interfaces in C# that inherits from other COM interfaces and why?

like image 790
CARLOS LOTH Avatar asked Jun 01 '10 20:06

CARLOS LOTH


People also ask

How interface are created and implemented in C#?

Notes on Interfaces:On implementation of an interface, you must override all of its methods. Interfaces can contain properties and methods, but not fields/variables. Interface members are by default abstract and public. An interface cannot contain a constructor (as it cannot be used to create objects)

How do you declare an interface?

To declare an interface, use the interface keyword. It is used to provide total abstraction. That means all the methods in an interface are declared with an empty body and are public and all fields are public, static, and final by default.

What is a COM interface?

A COM interface refers to a predefined group of related functions that a COM class implements, but a specific interface does not necessarily represent all the functions that the class supports.


1 Answers

Neat trick with the new keyword, that would indeed fix the problem. The issue here is that COM doesn't support inheritance. It is merely notational convenience in IDL to make it look like it does. In reality, an interface v-table must aggregate all the "inherited" base interfaces. In other words, for the IPersistFolder interface it must replicate the v-table slots for the 3 IUnknown methods and the IPersist::GetClassID method. The CLR takes care of IUnknown btw.

The v-table that .NET builds is not compatible with this layout, it doesn't replicate the base class method slots.

like image 182
Hans Passant Avatar answered Oct 15 '22 11:10

Hans Passant