简体   繁体   中英

P/Invoke a C delegate with structure to C#

I am trying to convert the following C code to C#... but I seem to be stuck when converting a structure to that callback properly. Compiles fine but during runtime the stack turns out imbalanced.

C

INT32 RegisterCallback4ThirdParty(BtSdkCallbackStru* call_back);

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

The following is my conversion:

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
}

When calling the routine in C#:

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

Where AppInquiryInd is this delegate:

public delegate void AppInquiryInd(UInt32 deviceHandle);

It fails at RegisterCallback4ThirdParty(ref cb);

Am I simply overlooking something?

Cheers.

The delegate declaration is probably not correct, try:

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

And be sure to declare the struct to match so the function pointer can be properly marshaled:

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

I changed class to struct , now the ref argument is correct:

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

The stack imbalance was caused by the missing CallingConvention.

You furthermore have to ensure that the delegate object stays visible to the garbage collector, it cannot see that the native code uses it. Which requires storing it in a static field or allocating an extra handle for it with GCHandle.Alloc(). So, roughly:

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

Where "ToCleanupLater" is a place-holder variable so you can call GCHandle.Free() when the native code can no longer make callbacks. If there is no mechanism to stop callbacks then don't bother freeing it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM