Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wrap a C++ class in a C based dll or a CLI based dll?

Tags:

c++

c

dll

c++-cli

I am told to import my writen class in C++ into a dll and then use that dll in a c# application. Following this guide I created the dll, but I can't simply use it in a C# application since there are some issues concerning it:

  1. What should I place for the return type of my factory function?

  2. What is the equivalent of const wchar_t* which is my constructors argument type?

  3. How can I retrieve and use my functions return type which is of type vector< wstring>?

These are the problems that prevent me from using my C++ DLL inside my C# applications. I was told that I need to create a wrapper with C++/CLI and then use that inside my C#. But sadly I have no idea about it, I don't know C++.net.

The only thing that currently seems to be a bit more sensational to me is to make it somehow compatible with C and then create a C DLL and use that in my C# application. I have read that in C, class object pointers are accessible through HANDLEs, so I thought that would be good idea to get things going without a lot of changes.

So the question is how can I use Handles to access my class objects in C and use them? And how can I convert a vector<wstring> to its C counterpart? If I want to use CLI to create a wrapper (DLL?) for my C++ DLL, to be used in other dotnet apps what should I do?

like image 594
Hossein Avatar asked Mar 24 '23 00:03

Hossein


1 Answers

In order to make a C wrapper for a C++ class to be used in for example a C# application you can do the following.

In Visual Studio choose Win32 Console Application and Enter a name, Then click next and on the next pane choose DLL and click finish. When you are done you are represented with a DLL project including 3 files.

testdll.h 
testdll.cpp
dllmain

Delete everything that exists inside your testdll.h and testdll.cpp files and copy the following contents to each respectively. Add these lines to your testdll.h

// Our C wrapper for creating a dll to be used in C# apps

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the TESTDLL_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// TESTDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef  TESTDLL_EXPORTS
#define  TESTDLL_API __declspec(dllexport)
#else
#define  TESTDLL_API __declspec(dllimport)
#endif

extern "C"
{
     TESTDLL_API int OurTestFunction(int x, int y);                         
}

It is inside this extern "C" block where you define your interface, functions to access your class member functions.Note the TESTDLL before the function prototype. All of your functions must be proceeded by that.

Add these to your testdll.cpp file:

#include "testdll.h"
#include "ourClass.h"

#define DLL_EXPORT

extern "C"
{
    OurClass ourObject;
    TESTDLL_API int OurTestFunction(int x, int y)
    {
        return ourObject.Add(x,y);
    }
}

You compile this and get a C based dll which can be used in a C# application.
There are couple of things to notice though, The more important ones are:

  1. You need to understand that the code you use as a proxy- i mean function definition inside your testdll.h, must only use C compatible types, it is C after all not C++.
  2. is that you would want to be able to allocate new objects of your class instead of just using one global object to access all methods.

For this, if you need to pass your class objects between member functions, you need to first convert it to a void* which C can understand and then pass it and use it to access your member functions of whatever.

For example I would have something like this inside my testdll.h in order to make user capable of managing the objects indirectly:

#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif

extern "C"
{
    TESTDLL_API int OurTestFunction(int x, int y);                      

    TESTDLL_API void*  CreateHandle();
    TESTDLL_API void*  GetCurrentHandle();
    TESTDLL_API void   DisposeCurrentHandle();
    TESTDLL_API void   SetCurrentHandle(void* handle);
    TESTDLL_API void*  GetHandle();
    TESTDLL_API void   DisposeHandle(void*);
    TESTDLL_API void   DisposeArrayBuffers(void);
}

And inside my testdll.cpp I would define them as :

#include "testdll.h"
#include "ourClass.h"

#define DLL_EXPORT

extern "C"
{
    OurClass *ourObject;

    TESTDLL_API int OurTestFunction(int x, int y)
    {
        //return ourObject.Add(x,y); -- not any more !!
        ourObject = reinterpret_cast<OurClass *>(GetHandle());
    }

    //Handle operations
    TESTDLL_API void* CreateHandle()
    {
        if (ourObject == nullptr)
        {
            ourObject = new OurClass ;
        }
        else
        {
            delete ourObject ;
            ourObject = new OurClass ;
        }
        return reinterpret_cast<void*>(ourObject);
    }

    TESTDLL_API void* GetCurrentHandle()
    {
        return reinterpret_cast<void*>(ourObject );
    }

    TESTDLL_API void  DisposeCurrentHandle()
    {
        delete ourObject ;
        ourObject = nullptr;
    }

    TESTDLL_API void  SetCurrentHandle(void* handle)
    {
        if (handle != nullptr)
        {
            ourObject = reinterpret_cast<OurClass *>(handle);
        }
        else
        {
            ourObject = new OurClass ;
        }

    }

    //factory utility function
    TESTDLL_API void* GetHandle()
    {
        void* handle = GetCurrentHandle();
        if (handle != nullptr)
        {
            return handle;
        }
        else
        {
            ourObject = new OurClass ;
            handle = reinterpret_cast <void*>(ourObject );
        }
        return handle;
    }

    CDLL_API void  DisposeHandle(void* handle)
    {
        OurClass * tmp = reinterpret_cast<OurClass *>(handle);
        delete tmp;
    }

    TESTDLL_API void DisposeArrayBuffers(void)
    {
        ourObject = reinterpret_cast<OurClass *>(GetHandle());
        return ourObject ->DisposeBuffers();//This is a member function defined solely for this purpose of being used inside this wrapper to delete any allocated resources by our class object.
    }
}

And when we compile this Dll, we can easily work with it inside our C# application. Before being able to use our functions defined in this dll we need to use appropriate [ImportDll()]. So for our TestDll we would write:

[DllImport(@"TestDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int OurTestFunction(int firstNumber,int secondNumber); 

And finally use it like:

private void btnReadBigram_Click(object sender, EventArgs e)
{
    int x = OurTestFunction(10,50);
    MessageBox.Show(x.ToString());
}

This is all I did to make my C++ class member functions accessible inside a C# application without any hassle.

Note:
When compiling your C# application make sure you have chosen the x86 Platform for compiling your project not AnyCpu.You can change your platform through properties.

Note 2:
For knowing how to create a C++/CLI wrapper for your native C++ class read this: C++/CLI wrapper for your native C++ class.

like image 117
3 revs, 2 users 95% Avatar answered Apr 02 '23 02:04

3 revs, 2 users 95%