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

前端 未结 1 1748
眼角桃花
眼角桃花 2020-12-15 02:07

I have an unmanaged DLL that exports only a C style factory method that returns a new instance of a class (simplified here to look simple).

hello.h

#         


        
相关标签:
1条回答
  • 2020-12-15 02:46

    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);
    
    0 讨论(0)
提交回复
热议问题