2011-11-24 79 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如何处理这个问题?