C ++ LoadLibrary создает исключение первого шанса, но работает? - PullRequest
1 голос
/ 03 апреля 2012

Я закончил свое небольшое приложение, и я пытаюсь убедиться, что у меня нет утечек памяти и ошибок.Посмотрев свой вывод, я заметил, что одна из моих функций выдает исключение First-Chance, но функция прекрасно работает и не падает.

Функция вызывает другую функцию в CLR C ++ DLL.Я удалил почти весь код в функции DLL только для теста, и исключение по-прежнему выдается, поэтому я знаю, что проблема заключается в моей функции EXE.

Это код функции EXE для вызова функции DLL.

LPCTSTR CHAXC0RDlg::Encrypt(LPCTSTR strValue)
{
    const char* Return;
    HINSTANCE hDLL = LoadLibrary(L"Library.dll");

    if(hDLL)
    {
        FARPROC hMethod = GetProcAddress(HMODULE (hDLL), "Encrypt");

        if(hMethod)
        {
            typedef const char* (*FunctionSig)(LPCTSTR);
            FunctionSig MethodCall = FunctionSig(hMethod);

            Return = MethodCall(strValue);
            FreeLibrary(hDLL);
        }
    }

    return _tcsdup(CString(Return));
}

Это функция DLL (как вы можете видеть, я удалил весь код, кроме кода, который генерируетвозвращаемое значение просто в качестве теста):

const char* Encrypt(LPCTSTR strPValue)
{ 
    String^ strValue = gcnew String(strPValue);
    string strReturn = (const char*)(Marshal::StringToHGlobalAnsi(strValue)).ToPointer();

    char* csValue = new char[strReturn.size()];
    strcpy(csValue, strReturn.c_str());
    return const_cast<const char*> (csValue);
}

Функция EXE выдает исключение для "const char* Return = MethodCall(strValue);" (я разрешил разрыв для этого исключения, как я знаю).

Почемуточно ли эта функция вызывает это исключение?

Спасибо!

РЕДАКТИРОВАТЬ

Обновление: мой набор символов имеет UNICODE.

Обновление № 2: Из того, что я прочитал в предложениях и ответах, вы предполагаете, что этот код не работает, но работает.Я включил перерыв для исключений первого шанса (и да, я знаю, что такое исключение первого шанса), потому что я хотел, чтобы эта программа была хорошего качества с устранением всех ошибок.Код работает нормально, я просто хотел выяснить, почему возникает исключение из первого шанса, потому что мне нравится быть лучшим программистом.Поэтому я хотел бы исправить это.

Обновление № 3: Теперь у меня есть код, проверяющий значения hDLL и hMethod, и оба не равны нулю при запуске этой функции.Кажется, проблема в том, что она сама вызывает DLL.Я предполагаю, что сигнатура функции на 100% правильная, поскольку этот код работает, она просто генерирует исключение первого шанса.

Обновление № 4: я добавил новые изменения в свою функцию выше, а также добавил функцию DLLкод.Функция DLL - это библиотека CLR C ++.Я удалил весь код в функции DLL, как я уже говорил, чтобы убедиться, что это не моя DLL.

Ответы [ 5 ]

4 голосов
/ 06 апреля 2012

Это не ясно из фрагмента, но похоже, что вы использовали __declspec (dllexport) в управляемой функции, написанной на C ++ / CLI. String ^ и класс Marshal делают это однозначным.

Выполнение управляемого кода в приложении, которое в остальном является полностью нативным, возможно, но требует загрузки и инициализации CLR. Есть несколько способов сделать это, я полагаю, вы нашли самый простой способ случайно. Использование __declspec (dllexport) в управляемой функции заставляет компилятор выдавать заглушку, которая заботится о том, чтобы инициализация CLR выполнялась, и выполнялся переход от нативного к управляемому коду.

Так что да, первый первый раз, когда вы вызываете эту экспортированную функцию, целый массив кода выполняется за вашей спиной. Это время, когда заглушка должна загрузить и инициализировать CLR и загрузить сборку, содержащую ваш управляемый код. Видя, что этот код выдает и обрабатывает исключение, вообще не о чем беспокоиться. В любом случае, вы мало что можете с этим поделать, у вас нет исходного кода для этого. Вы можете сделать это меньше случайного события, разместив CLR самостоятельно. Это занимает кусок кода COM, но не уверен, что он того стоит.

Из явного доказательства, которое вы предоставили, это на самом деле не вызывает проблемы, это особенность, а не ошибка. Обратите внимание, что вы можете вызвать реальное необработанное исключение с помощью этого вида кода. Если управляемый код выдает управляемое исключение, это часто встречается в управляемом коде, тогда вы столкнетесь с исключением Windows SEH с кодом исключения 0xe0434f4d. Это неизменно плохо, вы можете использовать ключевые слова __try и __catch, чтобы поймать его, но вы не можете получить приличную диагностику для него, поскольку вся информация об управляемом исключении становится gonzo к тому времени, когда стек возвращается к вашему коду.

1 голос
/ 10 апреля 2012

Исключение выдается только при первом вызове экспортированной функции из DLL.Это связано с загрузкой / инициализацией CLR в первый раз и не имеет никакого отношения к вашему коду (происходит до того, как ваш код сможет выполнить).Последующие вызовы Encrypt не генерируют это исключение.Кажется безопасным игнорировать это.

1 голос
/ 03 апреля 2012
  • Является ли подпись функции на 100% правильной? Или отсутствует декларация о вызове? (например, WINAPI, STDCALL). Вряд ли причина вашей проблемы, но в противном случае вы получите неприятности ( см. TONT )
  • Что является исключением? Скажите отладчику, чтобы он сломался, если выброшено исключение (в моей Visual Studio это Отладка / Исключения, включите "выбросить" для ... ну, для всех них.
  • Возможно, вы неправильно указали API (но это проявится только позже)
  • Допустима ли введенная строка? Это происходит для всех значений?

Вы увидите что-то вроде «исключения C ++» или «Нарушение прав доступа» и дополнительную диагностическую информацию (если вам повезет).

Существует небольшая вероятность того, что это первое исключение является "нормальным" - то есть оно всегда происходит для правильного ввода. Однако это редко, так как нормальный путь выполнения не должен вызывать исключения.


Возвращаемое значение:

Если возвращаемое значение находится в статическом / внутреннем буфере внутри DLL (не нуждается в освобождении), оно становится недействительным, когда DLL выгружается.

Если возвращаемое значение динамически распределяется DLL (например, DLL выполняет new [] или malloc или _tcsdup), DLL также должна освободить строку, иначе строка будет утечка. не освобождайте строку в вызывающей программе, так как DLL и вызывающая сторона могут использовать или не использовать разные кучи.

Проверьте документацию для DLL, там должно быть указано, кому нужно освободить возвращенный указатель и как, и / или как долго возвращаемый указатель действителен.

0 голосов
/ 08 апреля 2012

Я поставил ваш код на VS2010, вызывающий (EXE) - консольное приложение C ++. И C ++ / CLI DLL построен с набором инструментов V90, но я не видел ни одного первого случайного исключения.

Я почти уверен, что в заголовочном файле у вас был _declspec (dllexport), но я не уверен, как вы его определили. У меня вот так.

extern "C"
{
    __declspec(dllexport) const char* Encrypt(LPCTSTR strPValue);
}

И нет файла определения модуля (.def). Можете ли вы сказать мне, что отображается в вашем окне вывода?

0 голосов
/ 03 апреля 2012

Непонятно, какой «Набор символов» используется в вашем проекте (см. Свойства конфигурации / Общие).Вы используете макрос L в «LoadLibary» и не используете его в «GetProcAddress».

HINSTANCE hDLL = LoadLibrary(L"Library.dll");
FARPROC hMethod = GetProcAddress(HMODULE (hDLL), "Encrypt");

Попробуйте вместо этого использовать макрос _T:

HINSTANCE hDLL = LoadLibrary(_T("Library.dll"));
FARPROC hMethod = GetProcAddress(HMODULE (hDLL), "Encrypt");

из-за настроек вашего проекта вам будет присвоено 'L'.

РЕДАКТИРОВАТЬ:

попробуйте

_declspec( dllexport ) const char* Encrypt(LPCTSTR strPValue)
{
...
}

и убедитесь, что ваш файл определения модуля содержит

LIBRARY      "Library"

EXPORTS
    Encrypt @1

SECTIONS

 .data READ WRITE

, а также почему бы не использовать

const char* Encrypt(char* strPValue)

...