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.