Working with C++ Interface Classes from C#

First of all I should warn that this method is not the ultimate. It is not garanteed to work with all the compilers. But for certain DLLs it can slightly simplify its usage.

Imagine that we have a native DLL with the following exported interface:

class ISomething
{
  public:
    virtual void Method1() = 0;
    virtual int Method2() = 0;
}

__declspec(dllexport) ISomething* CreateSomething();
__declspec(dllexport) void DeleteSomething(ISomething* instance);

We can import those methods to C# as:

[DllImport(LIB_NAME)]
private static extern IntPtr CreateSomething();

[DllImport(LIB_NAME)]
private static extern void DeleteSomething(IntPtr instance);

But what will we do with that interface class?

In C++ object the first member is pointer to vtable (I should remind that it is common for C++ compilers, but not the must). Thus, IntPtr to instance will actually point to pointer to vtable. vtable looks like:

[StructLayout(LayoutKind.Sequential)]
private struct SomethingVTable
{
  public IntPtr Method1;
  public IntPtr Method2;
}

(we can deduce its structure from .h file)

When we have instancePtr to native object we can get this structure with:

// IntPtr instancePtr = NativeApi.CreateSomething();

IntPtr vtablePtr = Marshal.ReadIntPtr(instancePtr, 0);
SomethingVTable vtable = Marshal.PtrToStructure<SomethingVTable>(vtablePtr);  

Now we have a struct with pointers to actual methods of the native interface class. Methods like:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void Method1Delegate(IntPtr thisPtr);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Method2Delegate(IntPtr thisPtr);

When we get a vtable, we can get delegates for those methods with:

Method1Delegate method1 = Marshal.GetDelegateForFunctionPointer<Method1Delegate>(vtable.Method1);
Method2Delegate method2 = Marshal.GetDelegateForFunctionPointer<Method2Delegate>(vtable.Method2);

All the ISomething instances share the same vtable, so we can do it once when we get first instance pointer.

Now we can call methods of the native interface class by giving them a pointer to a created instance of that class:

// create an instance with imported method and C++ memory manager
IntPtr instance = NativeApi.CreateSomething();

// this should work only if not yet initialized
InitializeMethodPointers();

try
{
  // call method1
  method1Delegate(instance);

  // call method2
  return method2Delegate(instance);
}
finally
{
  // don't forget to free the memory
  NativeApi.DeleteSomething(instance);
}

Or this can be wrapped with C# class which will call ISomething methods and provide them an instance pointer.


Added: