如何在 DLL 之间传递对象指针?

Posted

技术标签:

【中文标题】如何在 DLL 之间传递对象指针?【英文标题】:How to pass an object pointer from and to a DLL? 【发布时间】:2012-02-15 21:09:14 【问题描述】:

我是一位经验丰富的软件工程师,但对 C# 有点陌生。我有一个 DLL,它由具有 C 接口的 C++ 对象组成,如图所示。我使用这个 C 接口是因为我不确定是否有办法从我的 C# 应用程序访问 DLL 中的 C++ 入口点。

   __declspec(dllexport) void *Constructor();
   __declspec(dllexport) void Destructor(void *pObj);
   __declspec(dllexport) int Method(void *pObj, char *pSrcDst, int len);

这是上面接口的C代码。

extern "C" void *Constructor()

   return (void *)new Object;

extern "C" void Destructor(void *pObj)

   delete (Object *)pObj;

extern "C" int Method(void *pObj, char *pSrcDst, int len)

   if (!pObj) return 0;
   return ((Object *)pObj)->Method(pSrcDst, len);

到目前为止一切顺利。现在,我需要能够从 C# 应用程序访问此接口。据我所知,我已经实现了以下 C# 接口。

   unsafe public class Wrapper
   
      [SuppressUnmanagedCodeSecurityAttribute()]
      [DllImport("MyDLL.dll")]
      public static extern
         IntPtr Constructor();
      [SuppressUnmanagedCodeSecurityAttribute()]
      [DllImport("MyDLL.dll")]
      public static extern
         void Destructor(IntPtr pObj);
      [SuppressUnmanagedCodeSecurityAttribute()]
      [DllImport("MyDLL.dll")]
      public static extern
         int Method(IntPtr pObj, [In,Out] byte[] pSrcDst, int len);

这是使用上述接口的 C# 代码。

  private IntPtr pObject;
  public object()
  
     pObject = Wrapper.Constructor();
  
  ~object()
  
     Wrapper.Destructor(pObject);
  
  public override byte[] method()
  
     byte[] bytes = new byte[100];
     int converted = Wrapper.Method(pObject, bytes, bytes.length);
     return bytes;
  

代码在调用 Wrapper.Method 和 Wrapper.Destructor 时都断言。我假设我没有正确处理指向对象的指针。有什么建议吗?

【问题讨论】:

“代码断言”是什么意思?你收到一些错误信息吗? 是的,“检测到 PInvokeStackImbalance”。 请记住,大多数编译器假定 extern "C" 函数不会引发异常(尤其是在 C 代码调用它们的情况下),因此您需要确保包装 new Object() 位在异常块中以防止任何异常(特别是std::bad_alloc)转义。 旁注:您不需要为每个函数重复 extern "C"。您可以将它们全部包装在 extern "C" /*functions here*/ 的范围内 【参考方案1】:

最可能的问题是您的代码没有使用正确的calling convention。在 C++ 中默认为__cdecl,但对于DllImport,默认为__stdcall

要解决这个问题,specify the calling convention explicitly:

[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]

【讨论】:

如果 OP 不知道,Visual Studio 中的默认调用约定是 compiler setting。可以改为__stdcall,以匹配默认的[DllImport()]约定。 @RobertKirnum,如果这个答案解决了你的问题,你应该accept it。【参考方案2】:

我的印象是,做这类事情的最佳方法是使用托管 c++/CLI 包装器。

Creating simple c++.net wrapper. Step-by-step

http://devmaster.net/forums/topic/7357-how-to-build-a-net-wrapper-for-a-c-library/

看起来很有希望。

【讨论】:

如果你只需要调用几个非托管函数,PInvoke 可能更容易。但是,是的,C++/CLI 肯定是一个替代方案。 没错,但那些少数非托管函数甚至存在的唯一原因是缺乏 C++/CLI 知识。如果有办法“做对”,为什么不使用它。你真的愿意使用一个名为“void * Constructor”的函数吗? 只要它没有公开暴露给其余的 C# 代码,我对此没有任何问题。如果您了解 C#,那么学习使用 PInvoke 比学习使用 C++/CLI 容易得多。

以上是关于如何在 DLL 之间传递对象指针?的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 和非托管 C++ 之间传递自定义对象

如何在pybind11中将共享指针列表传递给c++对象

Python 和 ctypes:如何正确地将“指针对指针”传递给 DLL?

如何将 Stucture 的地址传递给 C# 中的 Void 指针 [void*] 到 VC++ Dll

如何将结构从 C++ 应用程序传递到 C++ Win32 DLL?

C++成员函数指针指向全局函数指针