Обратный вызов C на C # вызывает исключение через некоторое время - PullRequest
0 голосов
/ 24 ноября 2011

У меня есть хитрая ошибка, которую я не могу найти. Я делаю позднее связывание из 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 # для обновления графического интерфейса.

Я слишком много времени занимался этой проблемой. Это похоже на классическую проблему соглашений о вызовах, но я проверил ее полностью!

Есть идеи?

Ответы [ 2 ]

1 голос
/ 24 ноября 2011
 bool l_bResult = DLLDownloadConfigration(OnDownloadStatusUpdate, OnDownloadFinished);

Код не очень понятен, но это вероятная проблема.Это создает два объекта делегата - бомбы обратного вызова после запуска сборщика мусора и удаляет объекты.Он не может отслеживать ссылки на управляемые объекты в неуправляемый код.Вам нужно будет явно создать делегаты и сохранить их в члене класса, чтобы сборщик мусора всегда видел хотя бы одну ссылку.

0 голосов
/ 24 ноября 2011

Вы пробовали использовать лямбду? Как в:

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

Моя теория заключается в том, что это дает сбой, потому что ваши методы OnDownloadStatusUpdate и OnDownloadFinished не являются статическими. Базовый IL ожидает объект this в качестве первого невидимого аргумента, но C-метод не передает его при вызове обратного вызова.

РЕДАКТИРОВАТЬ: Я думаю, что ответчик выше меня правильно, но может ли кто-нибудь пролить свет на то, как маршаллер на самом деле справляется с этим?

...