0

見つけられないトリッキーなバグがあります。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を更新します。

私はこの問題に長い間取り組んできました。これは古典的な呼び出し規約の問題のように見えますが、徹底的に検証しました。

何か案は?

4

2 に答える 2

1
 bool l_bResult = DLLDownloadConfigration(OnDownloadStatusUpdate, OnDownloadFinished);

コードはあまり明確ではありませんが、これはおそらく問題のある場所です。これにより、2つのデリゲートオブジェクトが作成されます。ガベージコレクターの実行後にコールバック爆弾が発生し、オブジェクトが削除されます。管理対象オブジェクトの参照を非管理対象コードに追跡することはできません。ガベージコレクターが常に少なくとも1つの参照を参照できるように、デリゲートを明示的に作成してクラスメンバーに格納する必要があります。

于 2011-11-24T17:29:37.707 に答える
0

ラムダを使ってみましたか?のように:

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

これに関する私の理論は、OnDownloadStatusUpdateメソッドとOnDownloadFinishedメソッドが静的ではないために失敗しているというものです。基になるILは、「this」オブジェクトを最初の非表示の引数として予期しますが、Cメソッドはコールバックを呼び出すときにそれを渡しません。

編集:私の上の回答者は正しいと思いますが、マーシャラーが実際にこれをどのように処理するかについて誰かが光を当てることができますか?

于 2011-11-24T17:27:08.907 に答える