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

后端 未结 3 1258
忘掉有多难
忘掉有多难 2020-12-20 04:29

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 h

相关标签:
3条回答
  • 2020-12-20 05:06

    ah i think I found what I was looking for after reading this [http://www.codeproject.com/Articles/9405/Using-classes-exported-from-a-DLL-using-LoadLibrar]

    correct me if wrong

    1. first I need to either export the native class or mark a factory method as extern "C"
    2. then in the CLR project I use the factory method or use Loadlibrary + malloc commands to get an instance of the class if I did not go with the factory method approach
    3. create the wrapper class as plinth had told me to do (many thanx to him). and use the instance from the previous step to call methods in my class
    4. include both dlls in the release and instructe developers to reference the CLR dll only.

    if that is the way then iam very greatfull for all of you guys

    going to start working on it soon...

    Yours...

    0 讨论(0)
  • 2020-12-20 05:10

    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.

    0 讨论(0)
  • 2020-12-20 05:13

    I think you'd be better off making a plain C interface to your C++ code. C++ linking is really only good for other C++ programs, due to name mangling. C functions, however, can be used in many languages without any problem - python, C#, haskell, etc.

    Let's suppose, however, you want to have some C++ classes accessible from your C interface. The way I like to do this is:

    • in my C++ dll have a global object registry. basically a map from int to object.
    • whenever I create an object, it gets a new registry ID.
    • whenever I call a function that uses the object, I pass in the ID.

    so something like this:

    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);
       }
    }
    
    0 讨论(0)
提交回复
热议问题