从C DLL调用C#回调函数时崩溃

时间:2011-11-01 10:06:14

标签: c# pinvoke

我正在Windows CE 6上编写一个C#应用程序来监控3G调制解调器。该应用程序将调用C DLL中的函数来访问调制解调器。

在启动时,C#app将调用此函数来创建新连接:

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

EVENT_CALLBACK定义为:

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

还定义了数据结构:

[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 */
}

在C DLL中,函数指针在CreateDataConnection()中传递,用于调制解调器状态更新。

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

    // Create a connection
    .
    .
}

创建连接后,DLL将调用回调函数来更新调制解调器状态,例如EC / IO(接收到的导频能量的比率)。

基本上,当ECIO更改时,调用回调函数将ECIO数据传递给C#app:

在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);
}

在C#app中,回调函数定义为:

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));
    .
    .
}

现在,问题来了。程序启动时,一切正常,连接创建正常,EC / IO正在更新。但运行几个小时后,EC / IO更新停止。经过测试,我发现在调用回调时它已停止:

fEventCallback(ME_RSCP_ECIO, &ecio_info);

我不知道这里出了什么问题可能在C#DLL中传递一个函数指针调用的方法不正确,或者代码中隐藏了一些错误?

3 个答案:

答案 0 :(得分:3)

由于回调函数只是C / C ++的指针,因此必须将callback参数声明为IntPtr。创建EVENT_CALLBACK实例结束,确保它在程序运行时保持活动状态。使用Marshal.GetFunctionPointerForDelegate方法将delecate实例转换为IntPtr,并将生成的IntPtr传递给CreateDataConnection函数。

[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));

答案 1 :(得分:3)

试试这个

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

它解决了我的问题。

答案 2 :(得分:1)

我认为你必须将GCHandl.AllocGCHandleType.Pinned一起使用,这样你就会告诉gc这个对象必须保留在内存中,即使 在引用此对象的应用程序中可能没有“根”,并且无法压缩此对象的内存