[英]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++ 类。 我喜欢这样做的方式是:
所以像这样:
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]
如果错了请纠正我
如果是这样,那么我对你们所有人都非常好
很快就会开始工作......
你的...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.