c# - 在 native dll 中创建 C++ 类以在 C# 中使用

标签 c# .net c++-cli pinvoke wrapper

我花了大约 3 天时间阅读有关这个​​主题的内容...

感谢许多教程和关于如何创建 native DLL 的回答问题,我现在完全迷失了。如果您有空闲时间,请稍微解释一下该主题并帮助我 - 如果您没有时间,那么只需转到我的问题的简单形式...


到目前为止,这是我对该主题的了解:

1) 我需要在类名之前使用定义为 __declspec(ddlexport)__declspec(ddlimport) 的宏来导出所有类方法和变量

2) 我需要在某处使用 extern "C" 但我不确定具体位置

3) 有很多方法可以做到这一点(将类作为参数传递给接受它的方法 c approch/导出类/使用接口(interface))


这就是我迷路的原因和方式:

1) 大多数教程都是针对导出方法的,我怀疑与类相比这很容易(在 C# 中你使用 [Dllimport,DLL 的名称] 然后你调用每个方法)

2) 我是否需要对类使用 extern "C"

3) 如果我使用带有接口(interface)的工厂方法,我是否需要分发包含该接口(interface)的 .h 文件?


这是我想做的:

1) 创建一个包含类的 C++ DLL 并导出该类以在 .NET 或 C++ 中使用(我想保护我的代码,因为我看到使用存储的 IL 可以轻松地反转托管代码。 )

2) 我想要 2 个 DLL,一个 C++ native DLL,另一个将是包装器 DLL,这样如果有人想在 C++ 中使用我的类,他可以直接使用 native DLL,如果他想在 C#/VB.net 中使用它,他可以使用 C++/CLI 包装器 DLL...

3) 没有库,没有头文件,没有 def 文件,...等.....只有纯 DLL(将发布 2 个文件)


简单形式


假设我想从这个 C++ 类在 C# 中实例化一个对象

Class Human
{
 private:
    int Pee_Meter;
 public:
    Void Do_Pee()
       {
         //stuff here
       };
};

我需要做什么,只有基本的东西吗?使用尽可能少的文件和最大程度的代码保护,不释放头文件或任何东西,只使用 DLL 和可能提到方法名称和要在 DLL 中使用的内容的 txt 文件。

换句话说,这些步骤是否正确?

1) 在VS2012中新建Win32工程,工程类型选择DLL

2) 定义宏 __declspec(ddlexport)/__declspec(ddlimport) 并在类名前使用它(我应该使用 extern "C" 上课?可能不会...)

3) 编译动态链接库

4) 在VS2012中创建一个CLR项目以使用C++/CLI

5)链接 native DLL(不知道怎么办??PInvoke整个类??????)

6) 定义包装器类(我仍在学习,但我认为您在 CLI 中为 native 类中的每个方法创建了一个方法)

7) 编译 CLI DLL

我应该说我有 Deitel 和 Ditel C//Deitel 和 Ditel C++//C++ programming by D. S. Malik 而这三本书都没有提到任何关于制作 DLL 的事情,我认为这有点愚蠢。

最后,感谢您浪费在帮助我上的每一秒,我非常感谢您提供的每一次帮助,即使您指导我阅读了我之前阅读过的教程...我可能遗漏了其中的某些内容:)

最佳答案

这样做了很多次之后,最简单的方法就是为您现有的类编写一个 C++/CLI 包装器。原因是 P/Invoke 最适合调用严格的 C 函数而不是 C++ 类中的方法。在您的示例中,您将如何为您指定的类调用 operator new

如果您可以将其编写为 C++/CLI dll,那么您将得到如下所示的内容:

public ref class CliHuman {
public:
    CliHuman() : _human(new Human()) { }
    ~CliHuman() { delete _human; }
protected:
    !CliHuman() { delete _human; }
public:
    void DoPee() { _human->Do_Pee(); }
private:
    Human *_human;
};

现在,您可能没有这样做的自由。在这种情况下,最好的办法是考虑如何公开 C++ 对象的 C API。例如:

extern "C" {

void *HumanCreate() { return (void *)new Human(); }
void HumanDestroy(void *p) { Human *h = (Human *)h; delete h; }
void HumanDoPee(void *p) { Human *h = (Human *)h; h->Pee(); }

};

您可以非常轻松地 P/Invoke 到这些包装器中。

从工程的角度来看,您永远不想这样做,因为调用 .NET 代码可以传入任意 IntPtr。在我的代码中,我喜欢做这样的事情:

#define kHumanMagic 0xbeefbeef;

typedef struct {
    int magic;
    Human *human;
} t_human;

static void *AllocateHuman()
{
    t_human *h = (t_human *)malloc(sizeof(t_human));
    if (!h) return 0;
    h->magic = kHumanMagic;
    h->human = new Human();
    return h;
}

static void FreeHuman(void *p) /* p has been verified */
{
    if (!p) return;
    t_human *h = (t_human)p;
    delete h->human;
    h->human = 0;
    h->magic = 0;
    free(h);
}

static Human *HumanFromPtr(void *p)
{
    if (!p) return 0;
    t_human *h = (t_human *)p;
    if (h->magic != kHumanMagic) return 0;
    return h->human;
}
void *HumanCreate() { return AllocateHuman(); }
void HumanDestroy(void *p)
{
    Human *h = HumanFromPtr(p);
    if (h) {
       FreeHuman(p);
    }
    else { /* error handling */ }
}
void HumanPee(void *p)
{
    Human *h = HumanFromPtr(p);
    if (h) h->Do_Pee();
    else { /* error handling */ }
}

您可以看到,我所做的是在该类的顶部创建一个轻型包装器,让我验证传入的内容更有可能是指向我们想要的内容的正确指针。安全性可能不是针对您的客户,而是针对您 - 如果您必须包装大量类,当您使用一个包装器代替另一个包装器时,这将更有可能在您的代码中发现错误。

在我的代码库中,我们发现拥有一个结构特别有用,在该结构中,我们使用低级代码构建静态库,并在其之上构建 C-ish API,然后将其链接到 C++/CLI 项目中调用它(尽管我想也可以从 C# 中 P/Invoke 到它)而不是让 C++/CLI 直接包装 C++。原因是(令我们惊讶的是)所有使用 STL 的低级代码都在 CLI 中而不是在 x86 或 x64 中完成了 STL 实现。这意味着在 STL 集合上迭代的低级代码会执行类似于 4n CLI 转换的操作。通过隔离代码,我们很好地解决了这个问题。

关于c# - 在 native dll 中创建 C++ 类以在 C# 中使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20889482/

相关文章:

c# - 正则表达式捕获句子中的确切单词

c# - 如何使用 C#.NET 合并在 MS Word 中创建的表格的单元格?

c# - 为什么我可以使用集合初始值设定项和来自另一个类的私有(private)集合访问?

winforms - 如何在winforms中显示视频文件?

java - 没有抽象方法的抽象类

c# - 组合框/列表框选择的项目

c# - 在 C# 构造函数中初始化什么更好 : initializer lists or assignment?

c# - 在 C# .NET 框架中获取蓝牙设备列表

namespaces - 如何在 C++/CLI 应用程序中正确设置根命名空间属性?

visual-studio - 这个奇怪的语法是什么意思