How do I DllExport a C++ Class for use in a C# Application

25,986

Solution 1

C# cannot directly import C++ classes (which are effectively name-mangled C interfaces).

Your options are exposing the class via COM, creating a managed wrapper using C++/CLI or exposing a C-style interface. I would recommend the managed wrapper, since this is easiest and will give the best type safety.

A C-style interface would look something like this (warning: untested code):

extern "C" __declspec(dllexport)
void* CExampleExport_New(int param1, double param2)
{
    return new CExampleExport(param1, param2);
}

extern "C" __declspec(dllexport)
int CExampleExport_ReadValue(void* this, int param)
{
    return ((CExampleExport*)this)->ReadValue(param)
}

A C++/CLI-style wrapper would look like this (warning: untested code):

ref class ExampleExport
{
private:
    CExampleExport* impl;
public:
    ExampleExport(int param1, double param2)
    {
        impl = new CExampleExport(param1, param2);
    }

    int ReadValue(int param)
    {
        return impl->ReadValue(param);
    }

    ~ExampleExport()
    {
        delete impl;
    }
};

Solution 2

As far as I know, C# can only interop with COM interfaces. Lucky enough it doesn't need to be a full blown COM object with registry, it can be any plain old C++ class implementing IUnknown.

So do something like this in C++:

#include <Windows.h>

// Generate with from VisualStudio Tools/Create Guid menu
static const GUID IID_MyInterface = 
{ 0xefbf7d84, 0x3efe, 0x41e0, { 0x95, 0x2e, 0x68, 0xa4, 0x4a, 0x3e, 0x72, 0xca } };

struct MyInterface: public IUnknown
{
    // add your own functions here
    // they should be virtual and __stdcall
    STDMETHOD_(double, GetValue)() = 0;
    STDMETHOD(ThrowError)() = 0;
};

class MyClass: public MyInterface
{
    volatile long refcount_;

public:
    MyClass(): refcount_(1) { }

    STDMETHODIMP QueryInterface(REFIID guid, void **pObj) {
        if(pObj == NULL) {
            return E_POINTER;
        } else if(guid == IID_IUnknown) {
            *pObj = this;
            AddRef();
            return S_OK;
        } else if(guid == IID_MyInterface) {
            *pObj = this;
            AddRef();
            return S_OK;
        } else {
            // always set [out] parameter
            *pObj = NULL;
            return E_NOINTERFACE;
        }
    }

    STDMETHODIMP_(ULONG) AddRef() {
        return InterlockedIncrement(&refcount_);
    }

    STDMETHODIMP_(ULONG) Release() {
        ULONG result = InterlockedDecrement(&refcount_);
        if(result == 0) delete this;
        return result;
    }

    STDMETHODIMP_(DOUBLE) GetValue() {
        return 42.0;
    }

    STDMETHODIMP ThrowError() {
        return E_FAIL;
    }
};

extern "C" __declspec(dllexport) LPUNKNOWN WINAPI CreateInstance()
{
    return new MyClass();
}

And on the C# side you do something like this:

[ComImport]
[Guid("EFBF7D84-3EFE-41E0-952E-68A44A3E72CA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface MyInterface
{
    [PreserveSig] double GetValue();
    void ThrowError();
}

class Program
{
    [DllImport("mylib.dll")]
    static extern MyInterface CreateInstance();

    static void Main(string[] args)
    {
        MyInterface iface = CreateInstance();
        Console.WriteLine(iface.GetValue());
        try { iface.ThrowError(); }
        catch(Exception ex) { Console.WriteLine(ex); }
        Console.ReadKey(true);
    }
}

You can do pretty much anything you want this way, as long as the communication between C++ and C# goes through the virtual interface.

Solution 3

You cannot create a C++ class instance through pinvoke from C#. This is a troublesome implementation detail, only the C++ compiler knows how much memory needs to be allocated and when and how to properly call the constructor and destructor. The object size is by far the hardest nut to crack, there is no way whatsoever to make that reliable.

If you cannot flatten the C++ class out into static methods then you need to write a managed wrapper. That's done with the C++/CLI language, you'd write a "ref class" that has the unmanaged class object stored as a pointer, created in the constructor and deleted in the destructor and finalizer.

Share:
25,986
Toymakerii
Author by

Toymakerii

Working on a Master in Computer Engineering

Updated on July 10, 2020

Comments

  • Toymakerii
    Toymakerii almost 4 years

    I have created a C++ Dll project which contains a class "myCppClass" and tried to Dll export it using the following code as described by: http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx

    class __declspec(dllexport) CExampleExport : //public CObject
    { ... class definition ... };
    

    I have omitted the "public CObject" as that requires afx.h and implies it is an MFC Dll. I am not sure if this is a good thing or not but it differed from the DLL project default settings.

    From the above linked documentation I am led to believe that all "public functions and member variables" are available for import. How do I accomplish this in C#? Can simply instantiate the class?

    Edit: I just realized that the Title of the post may be misleading. The emphasis should be on DllImport-ing from C# and ensuring that I followed the documentation properly in C++