对于IBPP/Firebird客户端,VC++异常处理在x86和x64上有所不同

cmk*_*mks 7 c++ 64-bit firebird exception firebird2.5

我正在使用Visual Studio 2015/VC++上的IBPP进行攻击.IBPP是firebird/interbase API的c ++包装器. IBPP,Firebird服务器的C++客户端接口

这个软件包的一部分是一个小测试套件,你可以在这里下载: ibpp-2-5-3-1-src.zip

从测试套件开始,您将找到一个简单的批处理文件来编译它

X:\ ibpp-2-5-3-1-SRC \测试\ VS2005 \简单,运行build.bat

它使用vc ++ 2015的原生x86和x64工具链编译得很好.

在编译之前,您需要编辑84到86行

X:\ ibpp-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
Run Code Online (Sandbox Code Playgroud)

请记住创建目录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\
Run Code Online (Sandbox Code Playgroud)

并复制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
Run Code Online (Sandbox Code Playgroud)

现在您可以编译并启动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"));
    }
}
Run Code Online (Sandbox Code Playgroud)

在x86二进制文件中,此异常按预期捕获,但在x64二进制文件中则不会.有谁知道如何在x64二进制文件中实现类似的异常处理?

在此先感谢您的帮助!

cmk*_*mks 1

经过大量研究后,我找到了原因。我错误地认为 x86 和 x64 中的异常处理有所不同。IBPP 库包含一个错误(10 年来了!),仅当 firebird 库向调用者报告(复杂的)错误情况时,才会在 x64/windows szenario 中发生。

那么发生的事情是:

测试程序调用 IBPP 库。IBPP 库调用 firebird API/库。firebird 库以 long 数组 [20] 的形式报告其调用结果,他们将其称为“ISC_STATUS 向量”。IBPP 库检查此结果,如果出现错误,则会抛出异常。测试程序捕获此类异常并将其报告给用户。

到目前为止,一切都很好。但错误是,IBPP 将 ISC_STATUS 定义为一个 long 数组 [20],就像 firebird 一样,直到 v2.0 为止 - 它只支持 x86 windows。从 v2.1 开始,firebird 支持 x64 windows,并将 ISC_STATUS 定义为 intptr_t 数组,这会导致 Windows x64 LLP64 编译器上的“long long”和 LP64 linux 编译器上的 long - 两者都是 64 位宽。在 ILP32 windows 和 linux x86 编译器上 intptr_t 是 32 位宽。IBPP 并没有缩小与 firebird 的差距,并且长期保留其 ISC_STATUS 定义 - 这导致 LLP64 windows x64 系统上的数据类型为 32 位宽(但在 Linux 上为 64 位,因为 Linux 上的 gcc 使用 LP64 系统,所以这个错误只发生在Windows上)。

因此,firebird x64 API 向 64 位整数的“按引用参数”数组 [20] 报告最多 20 个状态整数。FBPP 库传递 32 位整数的数组[20]。当 firebird API 将值存储在数组的后半部分时,它会覆盖调用者的内存。在这种情况下,ISC_STATUS 向量之后的下一个字节被 C++ 字符串对象占用。状态数组和字符串都是 IBS(基础状态)类的一部分。许多 IBPP 函数经常实例化此类的本地对象来管理 firebird API 结果和错误描述字符串。当函数退出时,框架会清理此类本地对象并尝试释放字符串,但内存中的元数据已被 firebird API 覆盖。

因此,本地 IBS 对象的清理会导致未知的软件异常,从而过度驱动 IBPP 框架抛出的异常。而且这个异常不是被测试程序捕获的,而是被windows/dr捕获的。沃森。

我将 ISC_STATUS 的定义从“long”修复为“intptr_t”,并且所有工作都按预期进行。

谢谢大家的提示。