Как я могу DllExport C ++ Class для использования в приложении C # - PullRequest
12 голосов
/ 20 января 2011

Я создал проект C ++ Dll, который содержит класс «myCppClass», и попытался Dll экспортировать его, используя следующий код, как описано ниже: http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx

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

Я опустил "public CObject", так как для этого требуется afx.h и подразумевается, что это MFC Dll. Я не уверен, хорошо это или нет, но он отличался от настроек по умолчанию для проекта DLL.

Из приведенной выше связанной документации я считаю, что все "публичные функции и переменные-члены" доступны для импорта. Как мне сделать это в C #? Может просто создать экземпляр класса?

Редактировать: Я только что понял, что заголовок сообщения может вводить в заблуждение. Акцент должен быть сделан на DllImporting из C # и обеспечении правильного следования документации на C ++

Ответы [ 5 ]

14 голосов
/ 20 января 2011

C # не может напрямую импортировать классы C ++ (которые фактически являются именованными интерфейсами C).

Вы можете выставить класс через COM, создать управляемую оболочку с использованием C ++ / CLI или открыть интерфейс в стиле C,Я бы порекомендовал управляемую оболочку, так как это проще всего и обеспечит наилучшую безопасность типов.

Интерфейс в стиле C будет выглядеть примерно так (предупреждение: непроверенный код):

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

Оболочка в стиле C ++ / CLI будет выглядеть так (предупреждение: непроверенный код):

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;
    }
};
10 голосов
/ 20 января 2011

Насколько я знаю, C # может взаимодействовать только с интерфейсами COM.К счастью, это не обязательно должен быть полноценный COM-объект с реестром, это может быть любой старый класс C ++, реализующий IUnknown.

Так что-то вроде этого в 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();
}

А на стороне C # вы делаете что-то вроде этого:

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

Вы можете делать практически все, что захотите, если связь между C ++ и C # проходит через виртуальный интерфейс.

4 голосов
/ 20 января 2011

Вы не можете создать экземпляр класса C ++ с помощью pinvoke из C #. Это сложная деталь реализации, только компилятор C ++ знает, сколько памяти нужно выделить и когда и как правильно вызывать конструктор и деструктор. Размер объекта на сегодняшний день является самым трудным орешком, нет способа сделать это надежным.

Если вы не можете сгладить класс C ++ в статические методы, вам нужно написать управляемую оболочку. Это делается с помощью языка C ++ / CLI, вы должны написать «ref class», в котором объект неуправляемого класса хранится в виде указателя, создается в конструкторе и удаляется в деструкторе и финализаторе.

0 голосов
/ 09 июля 2016

C # и C ++ НЕ совместимы с ABI, как C ++ и Delphi, поэтому вы не можете экспортировать виртуальные члены класса (методы) и объявлять их чисто виртуальными на вызывающей стороне, а также вызывать их, потому что C # не может обрабатывать vtbl объектов C ++.Я бы посоветовал вам обернуть ваши классы C ++ с помощью COM, чтобы у вас был еще один положительный побочный эффект, что другие COM-совместимые языки также могут использовать ваши классы.

0 голосов
/ 11 марта 2016

На самом деле вы можете напрямую обращаться к искаженным именам, используя свойство EntryPoint атрибута DllImport . См. этот ответ для более подробной информации.

...