起始状态:一个多机同步的 Excel 业务系统。为了让副本知道"母版有没有变",我给母版文件算一个内容指纹(滚动哈希 + 文件大小),副本比对指纹决定是否更新。函数写完,小样本测试通过;一上真实文件(1.7 MB),返回空串。
#WorkBuddy
指纹函数长这样(简化版):
倒数第四行那个 If h > 1E+15 ... 是我当时的"取模":想着数值太大就减掉一个 1E+15 的倍数,防止越界。
测试时我拿一个小文件跑,返回 0088343845-1668424,看起来完全正常。

换成真实的 1.7 MB 文件,返回 "-"——哈希部分整段消失了。

没有任何报错。 因为开头那句 On Error Resume Next 把溢出错吞了:循环里某一步赋值失败,h 变成 0,函数照常往下走,最后拼出一个残缺的字符串。
CLng 的上限是 2,147,483,647,不是 1E+15VBA 的 Long 是 32 位有符号整数,最大值 2,147,483,647(约 21 亿)。
上面那句"取模"的阈值写成了 1E+15。这意味着:
h 早就超过 21 亿 → CLng(...) 溢出报错 → 被 On Error 吞掉,h 保持上一次的值或直接归零;h 落在 21 亿 ~ 1E+15 之间,h > 1E+15 这个条件永远不成立,"取模"根本不会执行,h 就是一个越界的无效值。两条路都通向"返回空/错值"。
而我的小样本测试之所以"通过",纯粹是巧合:那个文件的哈希值算完只有 88,343,845,远低于 21 亿,两条雷都没踩到。
这是我这次最想强调的一点:用小块数据验证通过,不等于正确。 数值类代码必须用真实规模的数据验证,否则你验证的只是"恰好没触发边界"的那一种情况。
[0, 2^31)不要"等它变大了再取模",而是每一步乘法之后立刻取模:
三处改动:
Long 的上限对齐(代码里的常量 FP_MOD);Double 做乘加,取完模再转回 Long;On Error Resume Next 改成 On Error GoTo Fail,失败时走明确的错误分支,返回空串让调用方能判断,而不是静默产出错误值。改完实测:1.7 MB 文件 0.23 秒算完,连续两次调用结果一致。
这里有个过程上的坑,比 bug 本身更值钱。
我第一反应是"逻辑写错了",写了个分步探针去打每一步。但探针是这样跑的:
第一行用 VBIDE 把探针代码注入工程,第二行在同一个会话里立刻运行它。
结果 Excel 直接崩了(RPC_E / 远程过程调用失败)。后来才知道:用 VBIDE 改完代码后,同一个会话里立刻 Application.Run 可能失败。
正确姿势是 注入 → 保存 → 关闭 → 新会话打开 → 再 Run。
更关键的一招:让被注入的诊断过程把每一步结果写进日志文件,而不是只靠返回值。
这样即使 Excel 中途崩了,也能看到断在哪一步。"崩溃 + 有日志"远好过"崩溃 + 什么都没有"。
On Error Resume Next 会掩盖溢出、类型不匹配、下标越界这三类错。 数值计算密集的函数里别用它;真要用,至少在关键赋值后检查值是否合理。LongLong(32 位 Excel 上),Long 上限就是 21 亿。任何"累加/乘倍数"的算法都要算一下量级:1.7 MB 文件逐字节乘 33,不取模的话第 6 个字节就爆了。CDbl 做中间态再取模,不要指望 Long 自己扛住。症状 | 根因 | 修法 |
|---|---|---|
数值函数返回空/错值且无报错 | CLng 溢出被 On Error Resume Next 吞掉 | 每步取模到 [0, 2^31);改用 On Error GoTo |
小样本通过、真实数据失败 | 小数据恰好没触发 21 亿边界 | 用真实规模验证 |
注入 VBA 后 Run 直接崩 | 同会话内改码后立刻运行不稳定 | 注入 → 保存 → 关闭 → 新会话 → Run |
崩溃后无从下手 | 诊断逻辑只靠返回值 | 让诊断过程把每步写进日志文件 |
一句话:VBA 的静默失败,几乎都和 On Error Resume Next 有关。 它让代码"看起来在跑",其实在错误地跑。
#WorkBuddy
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。