繁体   English   中英

在本机 dll 中制作 C++ 类以在 C# 中使用

[英]make c++ class in a native dll to use in C#

我花了大约 3 天时间阅读有关此主题的信息...

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


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

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

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

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


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

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

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

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


这是我想要做的:

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

2) 我想要 2 个 DLL,一个 C++ 原生 DLL,另一个是包装 DLL,这样如果有人想在 C++ 中使用我的类,他可以直接使用原生 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,可能还有一个 txt 文件,其中提到了方法名称和要在 DLL 中使用的内容。

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

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

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

3) 编译DLL

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

5)链接本机DLL(我不知道如何?? PInvoke整个类???????)

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

7) 编译 CLI DLL

我应该说我有 Deitel 和 Ditel C // Deitel 和 Ditel C++ // Ds Malik 的 C++ 编程,这三本书中没有一本提到关于制作 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++ 代码制作一个简单的 C 接口。 由于name mangling ,C++ 链接实际上只对其他 C++ 程序有用。 然而,C 函数可以在许多语言中使用而没有任何问题——python、C#、haskell 等。

但是,让我们假设您希望从 C 接口访问一些 C++ 类。 我喜欢这样做的方式是:

  • 在我的 C++ dll 中有一个全局对象注册表。 基本上是从 int 到 object 的映射。
  • 每当我创建一个对象时,它都会获得一个新的注册表 ID。
  • 每当我调用使用该对象的函数时,我都会传入 ID。

所以像这样:

int CreateNiftyInstance()
{
   int i = global_store.get_id();
   Nifty *n = new Nifty();
   global_store.save_obj(i, n);

   return i;
}

void DoSomethingNifty(int id, const char *aCData)
{
   // lame dynamic cast.  Making it type safe is possible with dedicated stores for 
   // each type of object.
   Nifty *n = dynamic_cast<Nifty*>(global_store.get_obj(i));
   if n
   {
      n->DoSomething(aCData);
   }
}

啊,我想我在阅读本文后找到了我想要的东西 [http://www.codeproject.com/Articles/9405/Using-classes-exported-from-a-DLL-using-LoadLibrar]

如果错了请纠正我

  1. 首先,我需要导出本机类或将工厂方法标记为 extern "C"
  2. 然后在 CLR 项目中我使用工厂方法或使用 Loadlibrary + malloc 命令来获取类的实例,如果我没有使用工厂方法方法
  3. 按照底座告诉我的方式创建包装类(对他来说非常感谢)。 并使用上一步中的实例调用我的类中的方法
  4. 在发行版中包含这两个 dll,并指示开发人员仅引用 CLR dll。

如果是这样,那么我对你们所有人都非常好

很快就会开始工作......

你的...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM