2011-11-01 207 views
4

我在Windows CE 6上编写了一个C#应用程序来监视3G调制解调器。该应用程序将调用C DLL中的函数来访问调制解调器。从C DLL调用C#回调函数时发生崩溃

在启动时,C#的应用程序将调用该函数创建一个新的连接:

[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#应用程序:

在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#应用程序,回调函数被定义为:

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调用只是做不正确的方式,或者代码中埋藏了一些故障?

+1

它被埋在我们看不见的代码中。委托对象可能正在收集垃圾。 GC无法从C代码中看到对它的引用。将对象存储在类或静态变量的字段中,以便GC可以看到它。 –

+0

你会指针吗? –

+0

同意@Hans同样发生在我身上,如果您不将它们存储在某处,代表将收集,GC无法跟踪传递给extern的引用C –

回答

1

我认为你必须使用GCHandl.AllocGCHandleType.Pinned,通过这个,你会告诉GC这个对象必须保留在内存中,即使 有可能在应用程序引用此对象没有“根”和存储此对象不能被压缩

+1

不,不需要固定。 CLR为本地代码创建一个特殊的thunk,Marshal.GetFunctionPointerForDelegate()做同样的事情。由于固定这么长时间,固定将会产生积极的危害。所有必要的是对委托对象的可见引用。 –

3

因为回调函数只是C/C++的指针,所以回调参数必须声明为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)); 
+0

谢谢Alex,你解决方案的作品! – yiqi

2

试试这个

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

它解决了我的问题。