使c ++类在本机dll中使用在C# [英] make c++ class in a native dll to use in C#

查看:267
本文介绍了使c ++类在本机dll中使用在C#的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



由于许多教程和回答了有关如何创建本机DLL的问题,我现在完全迷失了。如果你有一些时间,请小心解释一点关于这个话题,并帮助我 - 如果你没有时间,那么只是去那里的简单形式的我的问题...






以下是我对此话题的了解:



1)在类名称之前定义为 __ declspec(ddlexport) __ declspec(ddlimport)的宏以导出所有类方法和变量



2)我需要使用 externC

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






这里是为什么以及我如何迷路:



1)大多数教程是用于导出方法我怀疑是很容易比较类(在C#中你使用[Dllimport,DLL名],然后你调用每个方法)



2)我需要使用 externC有类吗?



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个文件将被释放)






简单形式






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

 类人类
{
private:
int Pee_Meter;
public:
Void Do_Pee()
{
// stuff here
};
};

我需要做什么,只有基本的东西?有尽可能少的文件数量和最大的代码保护,没有发布头文件或任何东西,只使用DLL和可能的txt文件,提到方法名称和东西在DLL中使用。



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



1)在VS2012中创建新的Win32项目,然后选择DLL作为项目类型



2)define宏 __ declspec(ddlexport) / __ declspec(ddlimport)它之前的类名(应该使用 externC与类吗?可能不...)



3 )编译DLL



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



native DLL(我不知道如何 PInvoke 整个类???)



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



7)编译CLI DLL



我应该说,我有Deitel和Ditel C // Deitel和Ditel C ++ // C ++编程DS 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需要什么。例如:

  externC{

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

};

您可以非常轻松地P /调用这些包装。



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

  #define kHumanMagic 0xbeefbeef; 

typedef struct {
int magic;
人类*人类;
} 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已经验证* /
{
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 );
if(h)h-> Do_Pee();
else {/ * error handling * /}
}

看到我做的是在类的顶部创建一个轻的包装,让我验证,什么进来更可能是一个正确的指针,我们想要的。安全性可能不适合你的客户,但是对你来说 - 如果你必须包装一大堆类,这将更容易在你的代码中捕获错误,你使用一个包装器代替另一个。



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


I spent about 3 days reading about this topic...

I am totally lost now thanks to the many tutorials and answered questions about how to create a native DLL. If you have some time to spare please care to explain a little about the topic and help me - if you don't have time then just go to the simple form of my question down there...


Here is what I know about the topic so far:

1) I need to use a macro defined as __declspec(ddlexport) and __declspec(ddlimport) before class name to export all the class methods and variables

2) I need to use extern "C" somewhere but I am not sure exactly where

3) There are many ways to do this (pass class as parameter to methods that accept it c approch/ export class / use interface)


Here is why and how I am lost:

1) Most of tutorials are for exporting methods, which I suspect is very easy compared to classes (in C# you use [Dllimport, name of DLL] then you invoke each method)

2) Do i need to use extern "C" with classes or not?

3) If I used a factory method with an interface do i need distribute the .h file containing the interface?


Here is what i want to do:

1) create a C++ DLL with a class in it and to export that class to be used in .NET or C++ (I want to protect my code, since I saw how easily you can reverse managed code using the stored IL.)

2) I want to have 2 DLLs, one C++ native DLL, and the other one will be the wrapper DLL, so that if someone wants to use my class in C++ he can use the native DLL directly and if he wants to use it in C#/VB.net he can use the C++/CLI wrapper DLL...

3) no libs, no header files, no def files,...etc..... only pure DLLs (2 files will be released)


Simple form


Let's say I want to instantiate an object in C# from this C++ class

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

What do I need to do, basic stuff only? With the least possible number of files and maximum code protection, no releasing of header files or anything, only using DLLs and probably a txt file that mention methods names and stuff to use in DLL.

In other words, are these steps correct?

1) In VS2012 create new Win32 project, then select DLL as type of project

2) define macro __declspec(ddlexport) / __declspec(ddlimport) and use it before class name (should I use extern "C" with classes? Probably not...)

3) Compile DLL

4) Create a CLR project in VS2012 to use C++/CLI

5) Link the native DLL (I don't know how?? PInvoke entire class???????)

6) Define wrapper class (which I am still learning, but I think you create a method in CLI for every method in native class)

7) Compile the CLI DLL

Should I say that I have Deitel and Ditel C // Deitel and Ditel C++ // C++ programming by D. S. Malik and non of these three books mention anything about making DLLs which I think is kind of stupid.

Finally, thank you for every second you wasted in helping me, I really appreciate every help you provide even if you directed me toward a tutorial that I have read before... I might have missed something in it :)

解决方案

Having done this a bunch of times, the easiest way to do this is to write a C++/CLI wrapper to your existing classes. The reason being that P/Invoke works best on calls that are strictly C functions and not methods in a C++ class. In your example, how would you call operator new for the class that you specify?

If you can write this as a C++/CLI dll, then what you get is something that looks like this:

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;
};

Now, you might not have the freedom to do this. In this case, your best bet is to think about what it would take to expose a C API of your C++ object. For example:

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(); }

};

You can P/Invoke into these wrappers very easily.

From an engineering standpoint, you would never want to do this ever since calling .NET code could pass in any arbitrary IntPtr. In my code, I like to do something like this:

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

What you can see that I've done is create a light wrapper on top of the class that lets me verify that what comes in is more likely to be a correct pointer to what we want. The safety is likely not for your clients but for you - if you have to wrap a ton of classes, this will be more likely to catch errors in your code where you use one wrapper in place of another.

In my code base, we have found it especially useful to have a structure where we build a static library with the low-level code and the C-ish API on top of it then link that into a C++/CLI project that calls it (although I suppose to could P/Invoke into it from C# as well) instead of having the C++/CLI directly wrap the C++. The reason is that (to our surprise), all the low-level code which was using STL, was having the STL implementations done in CLI rather than in x86 or x64. This meant that supposedly low-level code that was iterating over STL collections would do something like 4n CLI transitions. By isolating the code, we worked around that quite well.

这篇关于使c ++类在本机dll中使用在C#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆