Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

.net - make c++ class in a native dll to use in C#

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 :)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...