C到C#回调一段时间后引发异常

时间:2011-11-24 17:15:28

标签: c# interop late-binding

我有一个我找不到的棘手错误。我正在从C#进行后期绑定到我写的本机DLL。 后期绑定似乎工作正常。添加回调后问题就开始了。

回调定义如此(在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。

我一直在讨论这个问题太多时间。它看起来像一个经典的调用约定问题,但我彻底验证了它!

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

 bool l_bResult = DLLDownloadConfigration(OnDownloadStatusUpdate, OnDownloadFinished);

代码不是很清楚,但这可能是麻烦点。这将创建两个委托对象,垃圾收集器运行后的回调炸弹并删除对象。它无法跟踪托管对象引用到非托管代码中。您需要显式创建委托并将它们存储在类成员中,以便垃圾收集器始终至少看到一个引用。

答案 1 :(得分:0)

你尝试过使用lambda吗?如:

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

我的理论是它失败了,因为你的OnDownloadStatusUpdate和OnDownloadFinished方法不是静态的。底层IL期望'this'对象作为第一个不可见的arg,但是在调用回调时C方法没有传递它。

编辑:我认为我上面的回答是正确的,但任何人都可以了解编组人员实际处理这个问题的方法吗?