2011-11-24 31 views
0

我有一個棘手的錯誤,我找不到。我正在從C#到我編寫的本地DLL綁定。 後期綁定似乎工作正常。我添加回調後出現問題。C到C#的回調在一段時間後引發異常

的回調定義爲這樣(在C)(在DLL中全球範圍):

typedef void (*FinishedDelegate) (ProcessResult a_bResult); 

typedef void (*DownloadStatusUpdateDelegate) (int a_iParametersDownloaded, int a_iTotalParameters); 
typedef void (*DownloadFinishedDelegate) (char* a_sConfiguration_values, ProcessResult a_bResult); 

DownloadStatusUpdateDelegate pfDownloadStatusUpdateDelegate = NULL; 
DownloadFinishedDelegate pfDownloadFinishedDelegate = NULL; 

此功能輸出:

PLUGIN_API BOOL DownloadConfigration(DownloadStatusUpdateDelegate a_pfStatusDelegate, DownloadFinishedDelegate a_pfFinishedDelegate); 

這是本機功能的實現:

DWORD WINAPI DownloadThreadFunc(void* a_pParam) 
{ 
    while (g_iParameterDownloaded < PARAMETER_COUNT) 
    { 
     if (IsEventSignaled(g_hAbortEvent)) 
     { 
      CallDownloadFinished(PROCESS_ABORT); 
      return 0; 
     } 

     Sleep(STATUS_DELAY); 
     CallDownloadStatusUpdate(); 
     g_iParameterDownloaded += STATUS_PARAMS_JUMP; 
    } 

    CallDownloadFinished(PROCESS_SUCCESS); 

    return 0; 
} 

PLUGIN_API BOOL DownloadConfigration(DownloadStatusUpdateDelegate a_pfStatusDelegate, DownloadFinishedDelegate a_pfFinishedDelegate) 
{ 
    if (IsEventSignaled(g_hInProcessEvent)) 
     return false; 


    pfDownloadStatusUpdateDelegate = a_pfStatusDelegate; 
    pfDownloadFinishedDelegate = a_pfFinishedDelegate; 

    g_iParameterDownloaded = 0; 

    DWORD l_dwResult = WaitForSingleObject(g_hThreadsStructGuardian, INFINITE); 
    if (l_dwResult == WAIT_OBJECT_0) 
    { 
     g_ahThreads[PLUGIN_THREAD_DOWNLOAD] = CreateThread(NULL, 0, DownloadThreadFunc, 0, 0, NULL); 
     ReleaseMutex(g_hThreadsStructGuardian); 
     return true; 
    } 

    return false; 
} 

在管理端,功能在這裏稱爲:

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
     public delegate void DownloadStatusUpdateDelegate(int a_iParametersDownloaded, int a_iTotalParameters); 
     [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
     private delegate void DownloadFinishedDelegate_Native(StringBuilder a_sConfigurationValues, EPluginProcessResult a_eResult); 

     private void OnDownloadStatusUpdate(int a_iParametersDownloaded, int a_iTotalParameters) 
     { 
      if (DownloadStatusUpdate != null) 
      { 
       DownloadStatusUpdate(a_iParametersDownloaded, a_iTotalParameters); 
      } 
     } 

     private void OnDownloadFinished(StringBuilder a_sConfigurationValues, EPluginProcessResult a_eResult) 
     { 
      if (DownloadFinished != null) 
      { 
       DownloadFinished(a_sConfigurationValues.ToString(), a_eResult); 
      } 
     } 

     public bool DownloadConfiguration() 
     { 
      bool l_bResult = DLLDownloadConfigration(OnDownloadStatusUpdate, OnDownloadFinished); 

      return l_bResult; 
     } 

奇怪的是 - 它工作了一段時間。一段時間後,我會得到一個「特權指令」異常,但是當我降低STATUS_DELAY時,發生的情況會更少。異常顯示在IsEventSignaled函數 - 但沒有什麼。

下載線程會同步到c#GUI線程以更新GUI。

我一直在這個問題上的方式太多的時間。它看起來像一個經典的調用約定問題,但我徹底驗證了它!

任何想法?

回答

1
bool l_bResult = DLLDownloadConfigration(OnDownloadStatusUpdate, OnDownloadFinished); 

該代碼不是很清楚,但這是可能的麻煩點。這會創建兩個委託對象,垃圾收集器運行後的回調炸彈並刪除對象。它不能跟蹤託管對象引用到非託管代碼中。您需要顯式創建委託並將它們存儲在類成員中,以便垃圾回收器始終能夠看到至少一個引用。

+0

是的,就是這樣 - 你不能將託管函數傳遞給非託管代碼。這就是爲什麼我首先定義了這些代表。 – Nitay

+0

對不起,在代碼混亂。謝謝你的幫助! – Nitay

0

您是否嘗試過使用lambda?如:

bool l_bResult = DLLDownloadConfigration((downloaded, totalParams) => OnDownloadStatusUpdate(downloaded, totalParams), (values, result) => OnDownloadFinished(values, result)); 

我的理論是,它是失敗的,因爲你的OnDownloadStatusUpdate和OnDownloadFinished方法不是一成不變的。基礎IL期望'this'對象作爲第一個不可見的參數,但C調用回調時不會傳遞它。

編輯:我認爲我上面的回答是正確的,但任何人都可以闡明一下marshaller如何處理這個問題?

相關問題