在C#中使用本机dll中的c ++类

我花了大约3天时间阅读这个主题……

由于许多教程并回答了有关如何创建本机DLL的问题,我现在完全迷失了。 如果你有空闲时间,请小心解释一下这个话题,并帮助我 – 如果你没有时间,那就去那里我的问题的简单forms……


以下是我目前对该主题的了解:

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

2)我需要在某处使用extern "C" ,但我不确定到底在哪里

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


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

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)没有libs,没有头文件,没有def文件,……等…..只有纯DLL(2个文件将被释放)


简单的forms


假设我想从这个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)编译DLL

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

5)链接本机DLL(我不知道怎么?? PInvoke全class???????)

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 */ } } 

您可以看到我已经完成的是在类的顶部创建一个轻量级包装器,让我validation进入的内容更可能是指向我们想要的正确指针。 安全性很可能不是针对您的客户而是针对您 – 如果您必须包装大量的类,则更有可能在您使用一个包装器代替另一个包装器的代码中捕获错误。

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

我认为你最好为你的C ++代码制作一个简单的C接口。 由于名称错误 ,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(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. 像plinth告诉我做的那样创建包装类(很多人都不喜欢他)。 并使用上一步中的实例来调用我的类中的方法
  4. 在发行版中包含两个dll,并指示开发人员仅引用CLR dll。

如果这样的话,那么对你们所有人来说非常棒

即将开始研究……

你的…