2014-10-26 3 views
1

Я пытаюсь преобразовать следующий код C в C# ... но я, кажется, застреваю при правильной конвертации структуры в этот обратный вызов. Компиляция отлично, но во время работы стек оказывается несбалансированным.P/Вызывать делегата C со структурой C#

C

INT32 RegisterCallback4ThirdParty(BtSdkCallbackStru* call_back); 

typedef struct _CallbackStru 
{ 
    BTUINT16 type;     /*type of callback*/ 
    void *func;      /*callback function*/ 
}CallbackStru, *PBCallbackStru; 

Следующая моя конверсия:

C#

[DllImport("SDK.dll")] 
[return: MarshalAs(UnmanagedType.I4)] 
public static extern Int32 RegisterCallback4ThirdParty(ref CallbackStru callback); 

public class CallbackStru 
{ 
    public ushort type; //type of callback 
    public object func; //callback function 
} 

При вызове подпрограммы в C#:

CallbackStru cb = new CallbackStru(); 
AppInquiryInd appInquiryInd = AppInquiryInd; 
cb.type = 0x04; 
cb.func = appInquiryInd; 
RegisterCallback4ThirdParty(ref cb); 

Где AppInquiryInd этот делегат:

public delegate void AppInquiryInd(UInt32 deviceHandle); 

Он проваливает RegisterCallback4ThirdParty (исх куб);

Я просто упускаю из виду что-то?

Cheers.

+0

Просто потому, что вы можете привести пример в C, это не вопрос C. – Deduplicator

+0

Что такое лучшая тема для публикации? Просто C#? – bl4kh4k

+0

Что вы спрашиваете? Не о C, только о C#. В частности, вы спрашиваете о p/invoke. – Deduplicator

ответ

4

Декларация делегата, вероятно, не правильно, попробуйте:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    public delegate void AppInquiryInd(UInt32 deviceHandle); 

И обязательно декларировать-структуру, чтобы соответствовать таким образом, указатель на функцию может быть правильно выстраивали:

[StructLayout(LayoutKind.Sequential)] 
    private struct _BtSdkCallbackStru { 
     public ushort type;  //type of callback 
     public AppInquiryInd func; //callback function 
    } 

Я изменил класс до struct, теперь ref аргумент верный:

[DllImport("SDK.dll", CallingConvention = CallingConvention.Cdecl)] 
    private static extern int RegisterCallback4ThirdParty(ref CallbackStru callback); 

Несбалансированность стека была вызвана отсутствием CallingConvention.

Кроме того, вы должны убедиться, что объект делегата остается видимым сборщику мусора, он не видит, что его использует собственный код. Для этого требуется хранить его в статическом поле или выделять дополнительный дескриптор для него с помощью GCHandle.Alloc(). Так, примерно:

CallbackStru cb = new CallbackStru(); 
    AppInquiryInd callback = new AppInquiryInd(myCallbackMethod); 
    ToCleanupLater = GCHandle.Alloc(callback); 
    cb.type = 0x04; 
    cb.func = callback; 
    RegisterCallback4ThirdParty(ref cb); 

Где «ToCleanupLater» является переменным местоблюстителем, так что вы можете вызвать GCHandle.Free(), когда нативный код больше не может делать обратные вызовы. Если нет механизма для остановки обратных вызовов, не беспокойтесь о его освобождении.

+0

Это работает! Требование о звонках было необходимо ... Я должен был сделать это, однако: CallingConvention = CallingConvention.Cdecl – bl4kh4k

+0

Для дальнейшего использования, как я узнаю, каково должно быть соглашение о вызове? – bl4kh4k

+0

Вы знаете это из кода C. Если вы явно не объявляли его, скажем, __stdcall, то компилятор C обычно выбирает __cdecl как значение по умолчанию. –

Смежные вопросы