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

Posted

技术标签:

【中文标题】未导出成员函数时,从 C# 调用 C++ 本机/非托管成员函数【英文标题】:Call C++ native/unmanaged member functions from C# when member functions were not exported 【发布时间】:2015-04-21 06:29:55 【问题描述】:

我有一个非托管 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:以 C 风格为现有 dll 公开所有成员函数。我不能这样做,因为它是第 3 方 dll。

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

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

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

我有 C++ 解决方案的源代码。但我虽然不是触摸 C++ dll。如果有任何可能在 C# 中做到这一点,那就太好了。

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

【问题讨论】:

看看Using Unmanaged C++ Libraries (DLLs) in .NET Applications 您正在使用此代码重新发明 COM。但它与 COM 不兼容,因此不能直接从 C# 程序中使用。正确执行此操作需要 C++/CLI 包装器。 【参考方案1】:

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

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

【讨论】:

非常感谢您提供详细的答案。我将尝试实施相同的方法。 :) @chandrakanth 请注意,this-&gt;HelloWorldImpl::~HelloWorldImpl() 会泄漏内存:例如参见 ***.com/a/1036031/613130 。你应该delete this;

以上是关于未导出成员函数时,从 C# 调用 C++ 本机/非托管成员函数的主要内容,如果未能解决你的问题,请参考以下文章

如何添加对由 C# 项目调用的非托管 C++ 项目的引用?

从 c# 代码调用带有 void *parameter 的本机 c++ 函数

通过 Marshal.GetFunctionPointerForDelegate 从本机 (C++) 线程调用托管函数 (C#)

如何从 C# 调用 C++ 类

从非托管 c++ 调用 C# 函数(通过托管包装器)

C++ 到 C# 事件处理