Маршалинг структуры с помощью методов C # P / Invoke - PullRequest
2 голосов
/ 16 декабря 2011

Я пытаюсь PInvoke в эту библиотечную функцию C ++:

int Initialize(Callback* callback);

struct Callback
{
    //.......
    void virtual StateChanged(int state) = 0;
};

Я пробовал этот наивный код C #, но он не работает.

Ответы [ 3 ]

3 голосов
/ 16 декабря 2011

Насколько я знаю, это невозможно сделать таким образом.Методы не «там», как у полей, кроме того, создание структуры с помощью виртуального метода создаст указатель vtable в ваших объектах, который вы не учитываете в c # зеркальном классе.

То, что вы можете сделать, - это вызвать PInvoke для метода, который принимает functionPointer (в C ++) и передает туда делегат (C #).Затем вы можете использовать этот указатель на функцию для вызова его из собственного кода, и ваш делегат запустится.

Затем вы можете изменить определение метода stateChange, чтобы принимать Callback * в качестве первого параметра, поэтому при вызове его из nativeВ коде вы можете передать указатель на объект, который отвечает за это изменение, и направить его обратно в Callback в c #.

// Редактировать, не имея исходного кода dll, построить мост между c # и c ++ - это то, что приходит к моемуразум.Это можно сделать с помощью c ++ / cli или c ++, придерживаясь нативной моей идеи, что-то вроде этого:

//c++ <--> new c++ dll <--> c#
struct CallbackBridge : public Callback
{
    void (*_stateChanged)(int);

    virtual void stateChanged(int state)
    {
        if (_stateChanged)
            _stateChanged(this, state);
    }
};

void* CreateCallback() { return new CallbackBridge(); }
void DeleteCallback(void* callback); { delete callback; }
void setStateChanged(void* callback, void (*ptr)(void*, int))
{
    CallbackBridge* bridge = (CallbackBridge*)callback;
    bridge->stateChanged = ptr;
}
///... other helper methods

Идея здесь состоит в том, чтобы рассматривать ваш объект как черный ящик (следовательно, void * везде - этоможет быть любым указателем, но из c # вы просто увидите это как SafeHandle / IntPtr и напишите вспомогательные методы, которые вы можете использовать для создания / удаления и изменения объектов PInvoke.

Использование c # может выглядеть следующим образом: (IntPtr для простоты можно использовать SafeHandle):

IntPtr callback = CreateCallback();
SetStateChanged(callback, myCallback);
//somewhere later:
DeleteCallback(callback);

void MyCallback(IntrPtr callback, int state)
{
    int someData = SomeOtherHelperMethod(callback);
    ConsoleWrite("SomeData of callback is {0} and it has changed it's state to {1}", someData, state);
}

Я знаю, это немного неуклюже для больших объектов, но без c ++ / cliОбертка Я никогда не находил лучшего способа справиться со всеми такими сложными делами, как виртуальные вызовы и т. д.

2 голосов
/ 16 декабря 2011

Ваш класс C # не может заменить структуру C ++, он имеет совершенно иную внутреннюю организацию.Структура C ++ имеет v-таблицу из-за виртуального метода.Это больше не тип POD.У него есть скрытое поле, которое содержит указатель на v-таблицу.И сгенерированный компилятором конструктор и деструктор.V-таблица представляет собой массив указателей на виртуальные методы.В классе AC # есть нечто похожее, называемое «таблица методов», но оно организовано совершенно иначе.Например, он будет содержать методы System.Object и содержит указатели на все методы, а не только на виртуальные.Расположение объектов класса также совершенно иное.Ключевое слово struct в языке C ++ в этом контексте является просто заменой ключевого слова class со всеми открытыми по умолчанию членами.

Класс оболочки, написанный на C ++/ Требуется язык CLI.У языка есть некоторая кривая обучения, но она не крутая, если у вас есть опыт работы с .NET и C ++.Код Boilerplate для такой оболочки находится в этот ответ .Вы можете перезвонить из собственного кода в управляемый код через указатель функции, возвращаемый Marshal :: GetFunctionPointerForDelegate ().

0 голосов
/ 16 декабря 2011

Я слышал, что у вас нет исходного кода библиотеки C ++. Тогда вы можете взглянуть на это .

Но экспорт классов из неуправляемой библиотеки DLL в управляемую без источника звучит для меня опасно.

...