在C#中创建非托管的C++对象

15

我有一个未托管的dll,其中有一个类"MyClass"。现在是否有一种方法可以在C#代码中创建这个类的实例?调用它的构造函数?我尝试了,但是Visual Studio报告了一个错误,错误信息为该内存区域已损坏或类似。

提前致谢


静态无返回值主函数(string[] args) { IntPtr p = new IntPtr(); Program.CreateObserv(ref p); } [DllImport(@"C:\mm_2008\liba.dll", EntryPoint = "??0CRls@fld@@QAE@ABV01@@Z", SetLastError = true, CallingConvention = CallingConvention.ThisCall)] 内部静态外部函数 CreateObserv(ref IntPtr p);这段代码抛出了一个 AccessViolationException 异常:accessviolationexception attempted to read or write protected memory... - Evgeny007
  1. static void Main(string[] args) { IntPtr p = Program.CreateObserv(); }
[DllImport(@"C:\mm_2008\liba.dll", EntryPoint = "??0CRls@fld@@QAE@ABV01@@Z", SetLastError = true, CallingConvention = CallingConvention.ThisCall)] internal static extern IntPtr CreateObserv();
  • 这段代码导致应用程序和应用程序主机崩溃。
- Evgeny007
为避免出现像“??0CRls@fld@@QAE@ABV01@@Z”这样的混乱入口点名称,请在非托管 DLL 中使用 extern "C"。声明为 extern "C" 的函数将被导出而不进行名称混淆。 - Alex F
1
@Evgeny007 不要把代码放在评论中,删除你的评论并将代码放在问题中。 - Scott Chamberlain
3个回答

22

C#无法创建从本地Dll导出的类实例。您有两个选择:

  1. 创建C++/CLI包装器。这是一个.NET类库,可以作为参考添加到任何其他.NET项目中。在内部,C++/CLI类使用未管理的类进行操作,按照标准的C++规则链接到本地Dll。对于.NET客户端,这个C++/CLI类看起来像.NET类。

  2. 为C++类编写C包装器,并使用PInvoke在.NET客户端中使用。例如,过度简化的C++类:

    class MyClass()
    {
    public:
        MyClass(int n){data=n;}
        ~MyClass(){}
        int GetData(){return data;}
    private:
        int data;
    };

此类的C API包装器:

    void* CreateInstance()
    {
        MyClass* p = new MyClass();
        return p;
    }
void ReleaseInstance(void* pInstance) { MyClass* p = (MyClass*)pInstance; delete p; }
int GetData(void* pInstance) { MyClass* p = (MyClass*)pInstance; return p->GetData(); }
// 为MyClass的每个公共方法编写包装器函数。 // 每个包装器函数的第一个参数应该是类实例。

CreateInstance、ReleaseInstance和GetData可以在C#客户端使用PInvoke声明并直接调用。void*参数应在PInvoke声明中声明为IntPtr。


1
你在包装函数中缺少 extern "C"。 - Danvil
Danvil - 这是我在问题笔记中写的。无论如何,谢谢,我现在感觉好多了。 - Alex F
@Alex 如果本地的 C++ dll 中存在类的层次结构,该怎么办...? - rsjethani
这是什么使它成为“C” API?newdelete不是C++的构造吗?看起来只是一个静态的C++ API。 - Rotem
@Rotem:我的意思是,这个包装器可用于C客户端,以及任何使用C风格API的客户端:.NET PInvoke、Assembly、Delphi等。包装器本身是用C++实现的。 - Alex F

3
解决方案是创建类似于以下的C++/CLI包装器:

#include "DllExportClass.h"

public ref class ManagedOperationHelper
{
    public:

    double Sum(double add1, double add2)
    {
        CDllExportClass obj;
        double ret=obj.Sum(add1, add2);
        return ret;
    }

    double Mult(double mult1, double mult2)
    {
        CDllExportClass obj;
        double ret=obj.Mult(mult1, mult2);
        return ret;
    }
};

CDllExportClass是从本地代码导出的类。以上是C++/CLI的.h文件。请注意让找到该dll的lib文件。将dll和lib放在同一个目录中,并编译C++/CLI代码。在托管代码目录中放置本机dll和C++/CLI dll。在托管项目中引用C++/CLI项目。在托管代码中实例化C++/CLI类,如下所示:

ManagedOperationHelper obj = new ManagedOperationHelper();
double ret=obj.Sum(10, 20);  

这是全部内容。

2
你不能直接在C#中使用未管理的C++代码。可通过使用PInvoke实现互操作性。在调用具有指针参数的函数时,会涉及到许多与此主题相关的问题
基本过程如下: C#部分
namespace MyNamespace {
  public class Test {
    [DllImport("TheNameOfThe.dll")]
    public static extern void CreateMyClassInstance();

    public void CallIt() {
        CreateMyClassInstance(); // calls the unmanged function via PInvoke
    }
  }
}

C++ 部分
class MyClass {
  public: MyClass() { /** Constructor */ }
};

MyClass* staticObject;

extern "C" void CreateMyObjectInstance() {
   staticObject = new MyClass(); // constructor is called
} 

谢谢大家,虽然我不愿意承认,但似乎除了编写一个包装器之外别无他法。 - Evgeny007
另一个选项是使用编译器/crl标志编写托管的C++。然后,您可以在同一个dll中混合未管理和托管代码,然后在C#代码中简单地调用托管方法。它将生成与您在代码中包装本机方法时相同的IL代码。快速介绍http://www.codeproject.com/Articles/19354/Quick-C-CLI-Learn-C-CLI-in-less-than-minutes - Milan Jaric
1
链接已失效。 - Michael Santos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接