P / Invoke вызывает сбой метода с параметрами struct и callback - PullRequest
0 голосов
/ 02 марта 2012

У меня есть DLL, из которой мне нужно P / Invoke следующий метод C:

int DAOpen(HANDLE *hOpen, UNIT *flags, void *callback, void *userData)

Используя P / Invoke Assistant, я создал следующую подпись:

[DllImportAttribute("<libName>", EntryPoint="DAOpen")]  
    static extern  int DAOpen(  
    out IntPtr hOpen,  
    ref uint flags,  
    IntPtr callback,  
    IntPtr userData);

Параметр callback должен соответствовать следующей сигнатуре C:

void CallBack(  
    int event,  
    int socket,  
    struct _iComStructure *plcom,  
    void *eventData,  
    void *myData)

, что переводится как

delegate void CallBack(  
    int @event,  
    int socket,  
    IntPtr plcom,  
    IntPtr eventData,  
    IntPtr myData);

Теперь я делаю вызов следующим образом:

void MyCallBack(int @event, int socket, IntPtr plcom, IntPtr eventData, IntPtr myData)  
{  
    // does nothing for now  
}

IntPtr hOpen;  
uint flags = 0;  
CallBack callBack = MyCallBack;  
StringBuilder userData = new StringBuilder(userData.ToString());


int rc = DAOpen(
out hOpen,    
ref flags,  
Marshal.GetFunctionPointerForDelegate(callBack),  Marshal.StringToHGlobalAnsi(userData.ToString()));

Проблема с этим кодом заключается в том, что он либо дает сбой, либо мне выдается FatalExecutionEngineError, когда я выполняю вызов в DAOpen. Я знаю, что это должно быть ошибкой, но я не могу понять ...

Спасибо за вашу помощь!

UPDATE

Как и было предложено, я попытался закрепить обратный вызов следующим образом:

GCHandle callbackHandle = GCHandle.Alloc(callBack, GCHandleType.Pinned);

Я получаю ArgumentException с этим сообщением:

Object contains non-primitive or non-blittable data

Кажется, это работает нормально, если я не укажу GCHandleType (т.е. я получаю дескриптор), но, конечно, оригинальная проблема остается.

Есть ли причина, по которой я не могу прикрепить делегата?

Спасибо за ваш вклад!

1 Ответ

2 голосов
/ 02 марта 2012

Предприняли ли вы какие-либо шаги, чтобы делегат не собирал мусор?

Во время вашего звонка может сработать коллекция поколения 0. Попробуйте либо создать локальную переменную в вызывающей функции, или вручную закрепить делегата в дескрипторе GC.

Обновление:

Кроме того, посмотрите на соглашение о вызовах обратного вызова в нативном API. Это стандартный вызов? Если нет, вы не можете использовать GetFunctionPointerForDelegate, так как возвращает указатель на встроенную функцию, ожидающую аргументы stdcall.

В этом случае вместо нас intptr используйте тип делегата и поместите «UnmanagedFunctionPointerAttribute» в определение делегата, которое определяет нативное соглашение о вызовах.

...