PAYATHON 2026

Delphi Data.DBXEncryption 整数溢出,关闭检查为何仍报错?

数据小许

结论

先检查主程序实际使用的 Data.DBXEncryption 编译产物,确认该单元编译时是否开启了溢出检查。关闭主程序的 Overflow checking 不会改变已经编译好的 DCU 或运行时包中的机器码;在调用方添加指令,也不会改变被调用单元中已有的检查代码。

要显式关闭溢出检查,应使用 {$Q-} 或 {$OVERFLOWCHECKS OFF}。仅添加 {$Q} 不能说明检查已经关闭。

目前还无法确定异常由哪一份文件或配置引起。简化程序正常而主程序异常,可以据此对照两者的编译配置和依赖来源,但不能直接认定 Delphi 安装损坏或密码错误。

为什么同一行乘法会表现不同?

触发异常的运算发生在 TSQLConnection.Connected := True 之后:

if Ax <> 0 then
  Ax := Ax * Bx;

函数此前将 Bx 设为 20021。Ax <> 0 排除了零值,乘积仍可能超出 Integer 的取值范围。

Delphi 的 $Q 控制编译器是否生成整数溢出检查代码。开启检查后,乘法等运算会附带检查,失败时抛出 EIntOverflow。是否生成检查代码在编译时决定。官方说明:Overflow checking

因此,即使两个程序显示的 Pascal 源码相同,执行的机器码也可能不同。调试器显示了某个 .pas 文件,也不能说明该文件刚刚参与过编译。

函数中连续出现乘法、加法和 xor,从代码形态看,它可能依赖固定宽度整数的回绕运算。缺少完整实现和兼容性验证,不能认定所有溢出都可以忽略。

按顺序排查编译配置和文件来源

1. 确认修改的是当前配置

先核对主程序和简化程序当前实际使用的设置:

  • Build Configuration,例如 Debug 或 Release。
  • Target Platform,例如 Win32 或 Win64。
  • Overflow checking 的最终有效值,包括继承和覆盖关系。
  • Use debug .dcus、项目 Search path、全局 Library path。
  • 是否使用运行时包,以及使用了哪些包。

SQLConnection 的属性和参数一致,只能说明这部分连接设置一致,不能说明两个程序链接了相同的代码。

2. 检查指令所在的单元

在需要编译的目标单元中,可以使用以下指令显式关闭检查:

{$Q-}

或:

{$OVERFLOWCHECKS OFF}

这些是局部编译指令。如果只将它们写在主程序 .dpr 或调用 TSQLConnection 的业务单元中,不能推断 Data.DBXEncryption 已使用相同设置重新编译。

还要检查目标单元及其包含的 .inc 文件,确认之后是否有生效的 {$Q+} 或 {$OVERFLOWCHECKS ON}。

如需判断某个源码位置的编译状态,可以在受控的源码副本中,在目标函数定义前临时加入:

{$IFOPT Q+}
  {$MESSAGE WARN 'Overflow checking is ON at this location'}
{$ENDIF}

出现警告,说明编译器处理该位置时检查处于开启状态。没有警告不能单独证明检查已关闭,也可能是这份源码没有参与编译。

3. 清理项目输出,再执行 Build

DCU 是单元源码的编译产物,应用可以直接链接现有 DCU。修改项目选项后,需要确认相关单元确实重新生成。官方说明:Delphi Compiled Unit File

建议依次执行:

  1. 备份当前项目和配置。
  2. 清理当前配置、当前平台下的项目生成文件。
  3. 执行 Build,检查编译输出。
  4. 确认启动的是这次生成的 EXE。
  5. 核对 Data.DBXEncryption 来自哪个目录,是否存在项目目录中的旧副本。

清理范围应限于可以重新生成的项目输出,不要直接删除 Delphi 安装目录中配套提供的 DCU。

Build 不保证所有库都从源码重新编译。若使用的仍是预编译单元,需要继续确认该 DCU 的来源。

4. 若使用运行时包,检查实际加载的 BPL

如果 Data.DBXEncryption 位于运行时包中,重建 EXE 不会自动改变已有 BPL 内的代码。BPL 承载包的运行时代码,DCP 提供编译所需的信息。官方说明:Packages

可以通过调试器的模块列表核对实际加载的包路径,比较两个程序是否加载了不同目录中的版本。修复时需要使用同一 Delphi 版本配套的包和编译产物,避免混用旧文件。

此前曾恢复 System.Classes 并重装部分单元,因此值得检查文件来源和配套关系,但这段经历本身不能证明根因。

确认依赖回绕运算后,如何处理?

优先恢复与 Delphi 10.2.3 安装配套、来源明确的库文件,再验证连接行为。

如果确认目标实现需要回绕运算,而且当前构建确实错误地为它启用了溢出检查,可以在受控的单元副本中把 {$Q-} 放在相关函数定义之前,再重新编译实际承载该函数的单元或包。修改范围结束后,应恢复原先的检查状态:原来开启时恢复为 {$Q+},原来关闭时保持关闭。

IncrAfter(Ax) 是另一个方法调用,也需要留意。如果异常随后出现在该方法内部,应独立检查它的编译状态;调用方的指令不会在运行时传递过去。

不要直接把全部 Integer 改成 Int64,也不要捕获异常后继续连接。改变位宽可能改变算法状态和输出;函数在异常发生前已经修改了 FSi,吞掉异常也不能恢复正常执行结果。

怎样判断排查是否完成?

修复后,应同时确认目标乘法不再抛出异常、连接能够完成、原有凭据能够按预期工作,并检查后续乘法和加法是否还会报错。

如果确认运行的是最新产物,且目标函数确实按 {$Q-} 编译,仍在同一位置报告整数溢出,应重新核对异常类型、调用栈、机器码对应位置及源码匹配情况,再检查进入函数时的状态值。此时继续反复切换项目选项,通常无法提供新的线索。

备注:内容仅供参考。