Нарушение прав доступа после отлова исключения DLL - PullRequest
3 голосов
/ 09 июня 2009

Я должен загружать модули как библиотеки динамически во время выполнения, так как они не известны заранее, только потому, что они соответствуют интерфейсу класса. Что я заметил, так это то, что после того, как я поймаю исключение, сгенерированное dll (в основной программе в главном потоке), вызываются нужные деструкторы и модули уничтожаются и dll выгружается, но затем как} в конце блока catch достигается отладчиком Visual Studio C ++ при пошаговом переходе, я получаю еще одно исключение, которое вызывает сбой программы с

Исключение первого шанса в 0x68ad2377 (msvcr90d.dll) в xxxxx.exe: 0xC0000005: Место чтения нарушения прав доступа 0x02958f14.

Если я включаю разрыв для исключений, разрыв для этого второго исключения отображает местоположение как

msvcr90d.dll! __ DestructExceptionObject (EHExceptionRecord * pExcept = 0x0017ee4c, unsigned char fThrowNotAllowed = 0) Строка 1803 + 0xf байт

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

Упрощенная версия моей структуры кода выглядит следующим образом:

Очень упрощенная структура программы:

//shared header:
class Module
{
public:
    virtual void Foo(void) = 0;
};


//dll:
class SomeSpecificModule : public Module
{
public:
    virtual void Foo(void);
};

void SomeSpecificModule::Foo(void)
{
    throw 1;
}

extern "C" __declspec(dllexport) Module* GetModule()
{
    return new SomeSpecificModule;
}


//program:
typedef ptrGetModule* (*GetModule)();

int main(void)
{
    HANDLE hMod = LoadLibrary("SomeSpecificModule.dll");
    ptrGetModule GetModule = (ptrGetModule)GetProcAddress(hMod, "GetModule");
    try
    {
        Module *d = GetModule();
        d->Foo();
    }
    catch (...)
    {
        cout << '!' << endl;
    }
    return 0;
}

Ответы [ 7 ]

4 голосов
/ 09 июня 2009

Следует помнить, что каждая копия библиотеки времени выполнения C имеет свои собственные состояния. Если SomeSpecificModule.dll статически связан с библиотекой времени выполнения C, может возникнуть такая проблема. Если это так, попробуйте установить связь с DLL-версией библиотеки времени выполнения C. Вы также должны убедиться, что SomeSpecificModule.dll скомпилирован и связан точно так же, как ваш основной модуль.

Вы упомянули, что DLL выгружена, и были вызваны правильные деструкторы, похоже, ваша настоящая программа работает намного больше, чем пример, который вы опубликовали. Если вы выгрузили SomeSpecificModule.dll в своем блоке try, вы выгрузили запись об исключении для SomeSpecificModule :: Foo (), и я полагаю, что именно так вы получили сбой при m svcr90d.dll!__DestructExceptionObject(EHExceptionRecord * ...

Тем не менее, в общем случае исключение через границы DLL вызывает проблемы. Если вы выбрасываете объект не POD, у вас могут возникнуть проблемы с памятью, выделенной другой библиотекой времени выполнения C в другой куче, с другой настройкой компилятора, версией STL ... вы понимаете.

Измените свой код, чтобы вы не пересекали границы DLL. Однажды кто-то из вашей команды поменяет настройку компилятора или сторонний заголовок #define, и ваша программа начнет аварийно завершать работу, и вам будет очень трудно найти причину.

В любом случае, не видя реального кода, я просто пытаюсь угадать, что может пойти не так. Надеюсь, это поможет.

3 голосов
/ 09 июня 2009

Большая часть кода раскрутки стека, который должен вызываться, когда ваша DLL выдает исключение, находится в DLL. Если вы выгружаете DLL, как этот код будет вызываться?

Не выбрасывайте исключения через границы динамически связанных модулей.

1 голос
/ 09 июня 2009

Я не вижу в этом коде, где DLL выгружается (как вы говорите, это так). Можете ли вы опубликовать соответствующий код?

Выгрузка DLL может иметь решающее значение, поскольку ваша DLL содержит код, необходимый для уничтожения объектов, разматывания стека и т. Д., И неясно из того, что вы опубликовали в какой момент DLL выгружена.

1 голос
/ 09 июня 2009

Кешируете ли вы исключение по значению в вашем фактическом коде? В этом случае может быть исключение в деструкторе скопированного объекта исключения в конце блока catch.

0 голосов
/ 09 июня 2009

Canopus: когда я выкидываю int как исключение, происходит то же самое.

TK ___: Я связываюсь с многопоточным dll во всех проектах.

Assaf и Shing Yip: DLL действительно выгружаются FreeLibrary () в деструкторе обертки для них, поскольку объекты-обертки я помещаю в вектор tr1 :: shared_ptr (поскольку сама обертка не копируется как ресурс и поэтому не может быть помещен в вектор STL), существующий только в области действия try {}. Это казалось правильным решением, поэтому я могу убедиться в том, что очистка, включая выгрузку DLL, происходит в случае ошибки, и я предпочитаю проектирование в стиле RAII. Если это является источником проблемы, то мне интересно, какой дизайн использовать, чтобы он работал правильно и при этом выглядел хорошо с точки зрения разработки программного обеспечения. Что заставляет меня подозревать, что это может быть не проблемой, однако, это то, что когда я выполняю вызовы деструктора, возникающие при возникновении исключения, FreeLibrary () запускается без ошибок, и я могу продолжать идти до тех пор, пока не доберусь до закрытия} улова {}.

Магнус Ског: В режиме выпуска я также получаю сбой, вместо того, чтобы перехватить исключение, а затем продолжить выполнение в обычном режиме. Динамическая память обрабатывается с 1) оператором new в некоторых случаях, 2) tr1 :: shared_ptr и 3) _mm_malloc / _mm_free, где мне нужно выравнивание.

0 голосов
/ 09 июня 2009

Это может быть выстрел в темноте, но стоит проверить.

Ваше приложение, похоже, скомпилировано в DEBUG, так как ошибка появляется в msvcr90d.dll. Используемые вами библиотеки также скомпилированы в DEBUG? Создание памяти с помощью msvcr90.dll и освобождение с помощью msvcr90d.dll или наоборот - распространенная проблема при использовании dll.

Я думаю, что указатель на функцию typedef выглядит немного подозрительно. Я бы написал так:

typedef Module* (*moduleFnType)();

int main(void)
{
    HANDLE hMod = LoadLibrary("SomeSpecificModule.dll");
    moduleFnType GetModule = (moduleFnType)GetProcAddress(hMod, "GetModule");
    try
    {
        Module *d = GetModule();
        d->Foo();
    }
    catch (...)
    {
        cout << '!' << endl;
    }
    return 0;
}

Ваш typedef ничего не говорит о типе возвращаемого значения функции GetModule.

0 голосов
/ 09 июня 2009

Проверьте параметры проекта, если ваше приложение является многопоточным, то вам следует обратиться к многопоточному DLL

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...