Call C++ native/unmanaged member functions from C# when member functions were not exported

本秂侑毒 提交于 2019-11-28 22:11:18

The C++ code is using the way abstract classes are implemented by the Visual C++ compiler. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/05/68017.aspx. This memory layout is "fixed" because it is used for implementing COM interfaces. The first member of the struct in memory will be a pointer to a vtable containing the function pointers of your methods. So for a

struct HelloWorldImpl : public HelloWorld
{
   public:
    int value1;
    int value2;
}

the "real" layout in memory would be:

struct HelloWorldImpl
{
    HelloWorldVtbl *vtbl;
    int value1;
    int value2;
}

where vtbl would be:

struct HelloWorldVtbl
{
    void *sayHello;
    void *release;
}

Just for the sake of doing a complete response, I'm writing the example for this signatures:

struct HelloWorld {
   public:
    virtual int sayHello(int v1, int v2, int v3) = 0;
    virtual void release() = 0;
};

C# code:

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetHW();

[StructLayout(LayoutKind.Sequential)]
struct HelloWorldVtbl
{
    public IntPtr sayHello;
    public IntPtr release;
}

Your functions are void Func(void) or int Func(int, int, int), but in truth they have a hidden parameter, this, so you can write them as:

int sayHello(HelloWorld*, int, int, int);
void release(HelloWorld*);

so in C# the delegate is

[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int Int32MethodInt32Int32Int32(IntPtr ptr, int v1, int v2, int v3);

[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void VoidMethodVoid(IntPtr ptr);

Then you can use

IntPtr ptr = GetHW();
IntPtr vtbl = Marshal.ReadIntPtr(ptr, 0);

HelloWorldVtblhw = (HelloWorldVtbl)Marshal.PtrToStructure(vtbl, typeof(HelloWorldVtbl));

Int32MethodInt32Int32Int32 sayHello = (Int32MethodInt32Int32Int32)Marshal.GetDelegateForFunctionPointer(hw.sayHello, typeof(Int32MethodInt32Int32Int32));
int res = sayHello(ptr, 1, 2, 3);
Console.WriteLine(res);

VoidMethodVoid release = (VoidMethodVoid)Marshal.GetDelegateForFunctionPointer(hw.release, typeof(VoidMethodVoid));
release(ptr);
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!