2011-11-01 134 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); 

它解決了我的問題。