Обработка исключений VC ++ различается на x86 и x64 для клиента IBPP / Firebird

Я работаю с IBPP на Visual Studio 2015 / VC ++. IBPP — это оболочка c ++ для API Firebird / Interbase.
IBPP, клиентский интерфейс C ++ к серверу Firebird

Часть этого пакета — небольшой набор тестов, вы можете скачать его здесь:
ibpp-2-5-3-1-src.zip

Для начала с набором тестов вы найдете простой пакетный файл для его компиляции в

х: … \ ППИР-2-5-3-1-Src \ Тесты \ VS2005 \ Простейшие-build.bat

Он прекрасно компилируется с нативными наборами инструментов x86 и x64 vc ++ 2015.

Перед компиляцией необходимо отредактировать строки с 84 по 86

х: … \ ППИР-2-5-3-1-Src \ Тесты \ tests.cpp

const char* DbName = "x:/ibpptest/test.fdb";    // FDB extension (GDB is hacked by Windows Me/XP "System Restore")
const char* BkName = "x:/ibpptest/test.fbk";
const std::string ServerName = ""; //"localhost";   // Change to "" for local protocol / embedded

Пожалуйста, имейте в виду, чтобы создать каталог x:\ibpptest\,

Кроме того, вам необходимо скачать файлы fblient, которые не доступны сами по себе, но являются частью всего архива сервера. Получите эти оба файла:
32-битный встроенный
а также
64-битный встраиваемый
.

Для упрощения создайте две директории помимо x:\...\ibpp-2-5-3-1-src\tests\vs2005\:

x:\...\ibpp-2-5-3-1-src\tests\vs2015x86\
x:\...\ibpp-2-5-3-1-src\tests\vs2015x84\

и скопировать x:\...\ibpp-2-5-3-1-src\tests\vs2005\simplest-build.bat в них. Теперь скопируйте файлы fbclient (32-разрядные в x86, 64-разрядные в x64) в следующие каталоги:

intl/*
udf/*
fbembed.dll
firebird.msg
ib_util.dll
icudt30.dll
icuin30.dll
icuuc30.dll
msvcp80.dll
msvcr80.dll

Теперь вы можете скомпилировать и запустить tests.exe. Двоичный файл x86 генерирует некоторые ошибки в тесте 6, и это нормально, потому что вы используете встроенную версию файлов fblient. Двоичный файл x64 будет отображаться в окне сбоя программы Windows. Это происходит в Test3, когда набор тестов активирует исключение:

try
{
#if defined(IBPP_WINDOWS) && defined(_DEBUG)
OutputDebugString(_("An exception will now get logged in the debugger: this is expected.\n"));
#endif
st1->ExecuteImmediate(  "CREATE SYNTAX ERROR(X, Y) AS ""SELECT ERRONEOUS FROM MUSTFAIL M" );
}
catch(IBPP::SQLException& e)
{
//~ std::cout<< e.what();
if (e.EngineCode() != 335544569)
{
_Success = false;
printf(_("The error code returned by the engine during a\n""voluntary statement syntax error is unexpected.\n"));
}
}

В двоичном файле x86 это исключение было получено, как и ожидалось, но в двоичном файле x64 это не так. Кто-нибудь знает, как добиться аналогичного исключения исключений в бинарном x64?

Заранее благодарю за любую помощь!

7

Решение

Предупреждение: я использовал среду Visual Studio 2017 для запуска файла simplest-build.bat.

Из приведенных ниже доказательств видно, что существует проблема с разветвлением или другим различием компиляции между 32-битной и 64-битной версиями службы Firebird, которая вызывает эту проблему.

Решение: функция-член EngineCode () не существует в 64-битной версии. Вы должны использовать функцию-член what () исключения, как показано в закомментированной строке внутри блока catch Test3 (). Если вы хотите использовать информацию EngineCode, вам придется проанализировать ее из строки what (), поскольку она содержит всю информацию, которая изначально была предоставлена ​​как отдельные элементы данных в классе IBPP :: SQLExceptionImpl для 32-разрядных систем.

try
{
#if defined(IBPP_WINDOWS) && defined(_DEBUG)
OutputDebugString(_("An exception will now get logged in the debugger: this is expected.\n"));
#endif
st1->ExecuteImmediate(  "CREATE SYNTAX ERROR(X, Y) AS ""SELECT ERRONEOUS FROM MUSTFAIL M" );
}
catch(IBPP::SQLException& e)
{
//~ std::cout<< e.what();

printf(e.what());

//if (e.EngineCode() != 335544569)
//{
//  _Success = false;
//  printf(_("The error code returned by the engine during a\n"//      "voluntary statement syntax error is unexpected.\n"));
//}
}

Результат вызова ().

*** IBPP::SQLException ***
Context: Statement::ExecuteImmediate( CREATE SYNTAX ERROR(X, Y) AS SELECT ERRONEOUS FROM MUSTFAIL M )
Message: isc_dsql_execute_immediate failed

SQL Message : -104
can't format message 13:896 -- message file C:\WINDOWS\SYSTEM32\firebird.msg not found

Engine Code    : 335544569
Engine Message :
Dynamic SQL Error
SQL error code = -104
Token unknown - line 1, column 8
SYNTAX

Головоломка: Statement.cpp показывает использование IBPP :: SQLExceptionImpl и других … ExceptionImpl, поэтому я должен верить, что этот исходный код является 32-битной ветвью. Если предполагается, что это общая ветка как для 32, так и для 64 бит, то я не вижу, как это может работать.

Есть два заголовочных файла, которые определяют классы исключений. Я считаю, что _ibbp.h использовался для компиляции 32-битного клиента, а ibbp.h использовался для 64-битного клиента. Я пришел к этим выводам, изменив предложение catch в Test3 (), чтобы увидеть возможные исключения. Классы … ExceptionImpl будут компилироваться только с 32-битными клиентскими библиотеками и не будут компилироваться с 64-битными библиотеками.

Из _ibpp.h (32-битные библиотеки fbclient распознают эти классы. 64-битные библиотеки fbclient не распознают.)

///////////////////////////////////////////////////////////////////////////////
//
//  Implementation of the "hidden" classes associated with their public
//  counterparts. Their private data and methods can freely change without
//  breaking the compatibility of the DLL. If they receive new public methods,
//  and those methods are reflected in the public class, then the compatibility
//  is broken.
//
///////////////////////////////////////////////////////////////////////////////

//
// Hidden implementation of Exception classes.
//

/*
std::exception
|
IBPP::Exception
/                 \
/                   \
IBPP::LogicException    ExceptionBase    IBPP::SQLException
|        \         /   |     \     /
|   LogicExceptionImpl |   SQLExceptionImpl
|                      |
IBPP::WrongType            |
\               |
IBPP::WrongTypeImpl
*/

Из ibpp.h (оба набора fbclient dll распознают эти классы)

/* IBPP never return any error codes. It throws exceptions.
* On database engine reported errors, an IBPP::SQLException is thrown.
* In all other cases, IBPP throws IBPP::LogicException.
* Also note that the runtime and the language might also throw exceptions
* while executing some IBPP methods. A failing new operator will throw
* std::bad_alloc, IBPP does nothing to alter the standard behaviour.
*
*                    std::exception
*                           |
*                   IBPP::Exception
*                 /                 \
*    IBPP::LogicException    IBPP::SQLException
*             |
*      IBPP::WrongType
*/

Во всех файлах tests.cpp единственный улов для IBPP :: SQLException находится в Test3 (). Любой другой улов использует IBPP :: Exception.

Так что эта проблема будет проявляться только в Test3 () при компиляции для 64-битной системы, но я думаю, что она будет проявляться всякий раз, когда IBPP :: SQLException используется в 64-битной реализации.

1

Другие решения

После долгих исследований я нашел причину. Я был неправ, полагая, что обработка исключений в x86 и x64 отличается. Библиотека IBPP содержит ошибку (с 10 лет!), Которая возникает в сценарии x64 / windows только тогда, когда библиотека firebird сообщает вызывающему (сложное) состояние ошибки.

Итак, что происходит:

Тестовая программа вызывает библиотеку IBPP. Библиотека IBPP вызывает API / библиотеку Firebird. Библиотека Firebird сообщает о своих результатах вызовов в массиве long [20], они называют его «ISC_STATUS vector». Библиотека IBPP проверяет эти результаты и в случае ошибки выдает исключение. Тестовая программа перехватывает такие исключения и сообщает о них для использования.

Все идет нормально. Но ошибка в том, что IBPP определяет ISC_STATUS как массив [20] longs как firebird, который он также делал до v2.0 — который поддерживает только окна x86. Начиная с v2.1, Firebird поддерживает 64-разрядные Windows и определяет ISC_STATUS как массив intptr_t, что приводит к «long long» в компиляторах LLP64 для Windows x64 и к длинным в компиляторах Linux LP64 — оба имеют ширину 64 бита. На компиляторах ILP32 для windows и linux x86 intptr_t имеет ширину 32 бита.
IBPP не закрывает пробел для firebird и остается в своем определении ISC_STATUS так долго, что приводит к 32-битному типу данных в системах LLP64 windows x64 (но к 64-битным в linux, так как gcc в linux использует систему LP64, так что ошибка происходит только на окнах).

Таким образом, API Firebird x64 сообщает о 20 целых числах состояния массиву «по ссылочному параметру» [20] из 64-битных целых чисел. Библиотека FBPP передает массив [20] из 32-битных целых чисел. Когда API Firebird сохраняет значения во второй половине массива, он перезаписывает память вызывающей стороны. В этом случае следующие байты после вектора ISC_STATUS заняты строковым объектом c ++. И массив состояний, и строка являются частью класса IBS (interbase status). Многие из функций IBPP часто создают экземпляр локального объекта этого класса для управления результатами API firebird и строкой описания ошибки. Когда функции выходят, среда очищает такие локальные объекты и пытается освободить строку, но ее метаданные в памяти были перезаписаны API Firebird.

Таким образом, очистка локального объекта IBS приводит к неизвестным программным исключениям, которые переопределяют исключения, создаваемые платформой IBPP. И это исключение отлавливается не тестовой программой, а windows / dr. уотсон.

Я исправил определение ISC_STATUS от «long» до «intptr_t», и все работает как положено.

Спасибо всем за ваши советы.

1

По вопросам рекламы [email protected]