在未导出成员函数时,从C#调用C ++本机/非托管成员函数

时间:2015-04-21 06:29:55

标签: c# c++ interop

我有一个非托管DLL,它只导出一个C风格的工厂方法,它返回一个类的新实例(这里简化为简单)。

hello.h

#if defined(HWLIBRARY_EXPORT) // inside DLL
#   define HWAPI   __declspec(dllexport)
#else // outside DLL
#   define HWAPI   __declspec(dllimport)
#endif

struct HelloWorld{
   public:
    virtual void sayHello() = 0;
    virtual void release() = 0;
};
extern "C" HWAPI HelloWorld* GetHW();

HELLO.CPP

#include "hello.h"

struct HelloWorldImpl : HelloWorld
{
    void sayHello(){
    int triv;
    std::cout<<"Hello World!";
    std::cin>>triv;
};
void release(){
    this->HelloWorldImpl::~HelloWorldImpl();
};
HelloWorld* GetHW(){
HelloWorld* ptr = new HelloWorldImpl();
return ptr;
};

现在,我可以使用dllimport访问GetHW(),但有没有办法访问返回的'struct'的成员函数...即sayHello和release?

我也遇到了同样的问题。这个问题之前被问过一段时间。我评论它有任何更好的解决方案,但还没有得到任何答复。所以,重新发布它。

当我用谷歌搜索时,能够找到两种解决方案。

Solution1:为现有的dll公开C风格的所有成员函数。我不能这样做,因为它是第三方dll。

解决方案2:编写一个托管C ++ DLL,公开本机C ++ DLL的功能,以后可以在C#dll中使用。这里有许多类/函数。因此,创建将花费大部分时间。

我从以下链接获得了上述解决方案。 How To Marshall

除了上述两种解决方案外,请告诉我是否有更好的解决方案?

我有C ++解决方案的源代码。但我不是要触及C ++ DLL。如果有可能在C#中做到这一点,那就太好了。

如果没有其他选择,我需要遵循指定的两个解决方案中的任何一个。

1 个答案:

答案 0 :(得分:4)

C ++代码使用Visual C ++编译器实现抽象类的方式。 http://blogs.msdn.com/b/oldnewthing/archive/2004/02/05/68017.aspx。这种内存布局是“固定的”#34;因为它用于实现COM接口。内存中结构的第一个成员将是一个指向包含方法的函数指针的vtable的指针。所以对于一个

struct HelloWorldImpl : public HelloWorld
{
   public:
    int value1;
    int value2;
}
&#34;真实&#34;内存中的布局是:

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

其中vtbl将是:

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

为了做一个完整的回复,我为这个签名写了一个例子:

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

C#代码:

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

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

您的功能是void Func(void)int Func(int, int, int),但实际上他们有一个隐藏参数this,因此您可以将其编写为:

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

所以在C#代理是

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

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

然后你可以使用

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