yiqi
yiqi

Reputation: 51

Crash in when calling a C# callback function from C DLL

I am writing a C# app on Windows CE 6 to monitor a 3G modem. The app will call functions in a C DLL to access the modem.

In startup, C# app will call this function to create a new connection:

[DllImport("swmodem.dll", CallingConvention = CallingConvention.Winapi)]
      public static extern int CreateDataConnection(EVENT_CALLBACK callback);

The EVENT_CALLBACK is defined as:

public delegate void EVENT_CALLBACK(int e, IntPtr data);

A data structure is also defined:

[StructLayout(LayoutKind.Sequential)]      
public struct ECIO_INFO
{
        public UInt32 ecio1;    /*!< Primary scramble code */
        public UInt32 ecio2;    /*!< Received signal code power */
        public UInt32 ecio3;    /*!< Energy per chip per power density */
}

In C DLL, a function pointer is passed in CreateDataConnection() for modem status update.

int CreateDataConnection(EVENT_CALLBACK ecb)
{
    .
    .               
    fEventCallback = ecb;

    // Create a connection
    .
    .
}

After a connection is created, the DLL will invoke the callback function to update the modem status, for example EC/IO (The ratio of received pilot energy).

Basically, when ECIO changes, the callback function is called to pass the ECIO data to C# app:

In C DLL:

void ProcessNotification(EVENT_CALLBACK fEventCallback)
{
    ECIO_INFO ecio_info;

        ecio_info.ecio1 = ecio_info.ecio2 = ecio_info.ecio3 = 0;
        if(data.nNumOfCells>0)
            ecio_info.ecio1 = data.arCellInfo[0].nEcIo;
        if(data.nNumOfCells>1)
            ecio_info.ecio2 = data.arCellInfo[1].nEcIo;
        if(data.nNumOfCells>2)
            ecio_info.ecio3 = data.arCellInfo[2].nEcIo;

        if(data.nNumOfCells>0)
            fEventCallback(ME_RSCP_ECIO, &ecio_info);
}

In C# app, the callback function is defined as:

private void ModemEventCallback(int e, IntPtr data)
{
    .
    .

    Modem.ECIO_INFO new_reinfo = new Modem.ECIO_INFO();
    new_reinfo = (Modem.ECIO_INFO)Marshal.PtrToStructure(
       data, typeof(Modem.ECIO_INFO));
    .
    .
}

Now, the problem comes. When the program starts, everything is fine, the connection is created ok and EC/IO is being updated. but after running for several hours, the EC/IO update is stopped. After a test, I found it is stopped when the callback is invoke:

fEventCallback(ME_RSCP_ECIO, &ecio_info);

I don't know what has gone wrong here Probably passing a function pointer in a C# DLL invoke just not right way to do it, or some fault is buried in the code?

Upvotes: 4

Views: 2692

Answers (3)

georgechen
georgechen

Reputation: 71

Try this

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void EVENT_CALLBACK(int e, IntPtr data);

It solved my problem.

Upvotes: 3

Alex F
Alex F

Reputation: 43311

Since callback function is just pointer for C/C++, callback parameter must be declared as IntPtr. Create EVENT_CALLBACK instance end ensure that it remains alive all time your program runs. Use Marshal.GetFunctionPointerForDelegate Method to convert delecate instance to IntPtr, and pass resulting IntPtr to CreateDataConnection function.

[DllImport("swmodem.dll", CallingConvention = CallingConvention.Winapi)]
      public static extern int CreateDataConnection(IntPtr callback);

...
EVENT_CALLBACK c;
c = new EVENT_CALLBACK( ... );   // keep this alive !
...
CreateDataConnection(Marshal.GetFunctionPointerForDelegate(c));

Upvotes: 3

MishaU
MishaU

Reputation: 708

I think you must use GCHandl.Alloc with GCHandleType.Pinned, by this you will tell gc that this object must remain in memory even though there might be no "roots" in the application that refer to this object and the memory for this object cannot be compacted

Upvotes: 1

Related Questions