c# - 未导出成员函数时从 C# 调用 C++ native /非托管成员函数

标签 c# c++ interop

我有一个非托管 DLL,它仅导出一个 C 样式的工厂方法,该方法返回一个类的新实例(此处进行了简化以使其看起来简单)。

你好.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();

你好.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(),但是有没有办法访问返回的“结构”的成员函数...即 sayHello 和 release?

我也遇到了同样的问题。不久前有人问过这个问题。我对此发表评论以寻求更好的解决方案,但尚未得到任何答复。所以,重新发布它。

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

解决方案1:将现有dll 的所有成员函数公开为C 风格。我不能这样做,因为它是第 3 方 dll。

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

我从下面的链接得到了上述解决方案。 How To Marshall

请问除了以上两种方案还有更好的方案吗?

我有 C++ 解决方案的源代码。但我虽然没有接触 C++ dll。如果有任何可能在 C# 中完成,那就太好了。

如果别无选择,我需要遵循指定的两种解决方案中的任何一种。

最佳答案

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

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

内存中的“真实”布局将是:

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# 中委托(delegate)是

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

关于c# - 未导出成员函数时从 C# 调用 C++ native /非托管成员函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29764548/

相关文章:

c# - SQLite一对多

c++ - C 和 C++ 预处理器之间有什么区别?

c++ - 来自 CreateWindowEx() 的 ERROR_INVALID_WINDOW_HANDLE

.net - 从 C# 验证 java SAML 签名

c# - 使用 ref 关键字保存对象引用以供将来使用

c# - 使用 yield return 的强类型方法接口(interface)

c++ - 混合 Ruby 和其他语言的最佳方式是什么? (尤其是 C++)

interop - 在Clojure中动态访问Java字段?

c# - 如何允许在 MessageBox 上复制消息

c++ - 如何在 Qt Designer 中创建可编辑的项目列表?