建议收藏,报错不慌
一、开篇:每个 VBA 程序员的午夜惊魂
凌晨两点,你盯着屏幕上那个刺眼的 "运行时错误 '13': 类型不匹配",感觉人生也陷入了类型不匹配。
你点了"调试",Excel 像一位冷酷的法官,把光标停在那行你写了 100 遍的代码上。那一刻,你想问天问大地:这错误到底是谁生的?它从哪来?要到哪里去?
别怕,今天我们要认识一位 VBA 界的 福尔摩斯 —— Err 对象。它是 VBA 内置的"黑匣子",每当代码翻车,它都会默默记录下事故原因,等你去查阅。
二、Err 对象:不用 new 就能用的"幽灵特工"
在 VBA 的世界里,大多数对象都需要你 Set 一下才能用,比如:
Dim wb As Workbook
Set wb = Workbooks.Open("C:\报表.xlsx") ' 得先Set,不然就报错
但 Err 对象不一样,它是 全球通、自带光环、24小时待命 的内置对象。你不需要 Set Err = New Err,它就像空气一样,随时随地为你服务(或者说,随时随地提醒你犯了什么错)。
🎯 Err 对象的"个人简历"
属性/方法 | 说明 | 人话翻译 |
Number | 错误编号 | 错误的身份证号 |
Description | 错误描述 | 错误的犯罪口供 |
Source | 错误来源 | 是谁甩的锅 |
Clear | 清除错误 | 翻篇儿,既往不咎 |
Raise | 主动抛错 | 人工制造翻车现场 |
冷知识:Number 是 Err 的默认属性,所以 If Err = 0 其实等价于 If Err.Number = 0。这就好比喊"那谁"就能指代你们班最胖的同学一样,大家都懂。
三、实战篇:当 Err 对象遇上人间真实
🎬 场景一:文件失踪案(错误 53)
你的代码要打开一个 Excel 文件,但那个文件已经被手贱的同事删了。这时候如果不处理,VBA 会直接弹窗崩溃。
翻车代码:
Sub 打开文件_裸奔版()Dim wb As Workbook' 下面这行如果文件不存在,Excel会直接崩给你看Set wb = Workbooks.Open("C:\不存在的文件.xlsx")MsgBox "打开成功!"End Sub
用上 Err 对象后的优雅版本:
Sub 打开文件_优雅版()On Error GoTo 事故处理现场 ' 开启安全气囊Dim wb As WorkbookSet wb = Workbooks.Open("C:\不存在的文件.xlsx")MsgBox "打开成功!"Exit Sub ' 正常结束,别跑到错误处理区事故处理现场:' 这时候Err对象已经被自动填充了事故信息Select Case Err.NumberCase 53MsgBox "文件找不到了!错误号:" & Err.Number & vbCrLf & _"详细原因:" & Err.Description & vbCrLf & _"建议:检查一下路径,或者去问问是不是谁手贱删了文件", vbExclamationCase 76MsgBox "路径都不存在!你确定C盘存在吗?", vbCriticalCase ElseMsgBox "未知错误:" & Err.Description, vbCriticalEnd SelectEnd Sub
知识点:On Error GoTo [标签] 是 VBA 的"安全气囊"。一旦代码翻车,程序不会直接崩掉,而是会跳转到指定的标签位置,让你有机会体面地处理错误。
🎬 场景二:类型不匹配之"把字符串塞进Long型"(错误 13)
你试图把 "abc" 塞进一个 Long 类型的变量里,就像试图把大象塞进冰箱——冰箱会抗议的。
Sub 强行塞入()On Error GoTo 翻车现场Dim i As Longi = "我是字符串" ' 这里会触发错误13翻车现场:MsgBox "错误号:" & Err.Number & " - " & Err.Description' 输出:错误号:13 - 类型不匹配End Sub
这时候 Err.Description 会告诉你"类型不匹配",而 Err.Number 是 13。记住这个数字,以后看到 13 就知道是数据类型在打架。
🎬 场景三:自定义错误 —— 程序员的"甩锅艺术"
有时候,系统自带的错误描述太机械,不够有温度。比如你想告诉用户:"亲,A1单元格必须填5个字符哦~",这时候可以用 Err.Raise 人工制造一个错误。
Sub 检查输入()On Error GoTo 友好提示' 自定义错误号:vbObjectError + 513 到 65535 之间都可以Const 我的专属错误号 = vbObjectError + 513If Len(Sheet1.Range("A1").Value) <> 5 ThenErr.Raise Number:=我的专属错误号, _Source:="检查输入模块", _Description:="A1单元格的值必须正好5个字符,你现在输入了" & _Len(Sheet1.Range("A1").Value) & "个,强迫症表示很难受。"End IfMsgBox "输入正确,继续执行~"Exit Sub
友好提示:
MsgBox "【自定义错误】" & vbCrLf & Err.Description, vbExclamation
End Sub
重点提示:使用 Err.Raise 时,必须指定 Number 参数。而且要注意,Raise 方法不会自动重置 Err 对象,如果之前已经有错误了,新的错误会"覆盖"上去,但没指定的属性会保留旧值。所以保险起见,先 Err.Clear 一下再 Raise。
四、进阶篇:错误处理的"三段式"架构
在专业的 VBA 项目中,错误处理不是简单的 MsgBox,而是一个完整的生态系统。
🏗️ 标准错误处理模板(建议收藏)
Sub 标准示范()On Error GoTo ErrorHandler ' 1. 开启防护罩' ====================' 这里是你的业务代码' ====================GoTo CleanExit ' 正常流程走到这里,跳过错误处理区ErrorHandler:' 2. 错误处理区 —— 这里 Err 对象已经装满了信息Dim errMsg As StringerrMsg = "【系统发生错误】" & vbCrLf & _"错误号:" & Err.Number & vbCrLf & _"错误源:" & Err.Source & vbCrLf & _"错误描述:" & Err.Description & vbCrLf & _"发生时间:" & Now()' 记录到日志(或者发邮件给IT部门)Call 记录错误日志(errMsg)' 根据错误号给用户不同的友好提示Select Case Err.NumberCase 53, 76MsgBox "文件或路径有问题,请检查!", vbExclamationCase 91MsgBox "对象变量未设置,请联系开发者。", vbCriticalCase ElseMsgBox "发生未知错误,错误号:" & Err.Number, vbCriticalEnd Select' 3. 清理现场Resume CleanExit ' 跳转到清理区CleanExit:' 无论是否出错,都要执行的清理代码On Error Resume Next ' 防止清理代码本身出错' 关闭文件、释放对象、恢复计算模式等Application.ScreenUpdating = TrueExit SubEnd SubSub 记录错误日志(msg As String)Open ThisWorkbook.Path & "\错误日志.txt" For Append As #1Print #1, msg & vbCrLf & String(50, "-")Close #1End Sub
🔍 三个关键语句的微妙差别
表格
语句 | 作用 | 使用场景 |
On Error Resume Next | 忽略错误,继续执行下一行 | 批量处理时,不想因为一个错误而中断整个流程 |
On Error GoTo | 标签 跳转到指定位置处理错误 | 需要详细记录或针对性处理错误 |
On Error GoTo 0 | 关闭错误处理,恢复默认弹窗 | 调试时想看到原始错误,或取消之前的 Resume Next |
致命陷阱警示:如果在 For...Next 循环里用 On Error GoTo,千万记得在错误处理代码里用 Resume 或 Resume Next 关闭错误机制!否则第二次出错时,错误处理机制已经"激活",会导致程序直接崩掉。
五、Err 对象的"清道夫":Clear 方法
想象 Err 对象是一个记事本,上面记满了上一次的犯罪记录。如果你不清除,下一次检查时可能会误判。
Sub 错误计数器()On Error Resume Next ' 躺平模式,不弹窗Dim i As Long, 错误次数 As LongFor i = 1 To 10' 故意制造错误:奇数时除以0If i Mod 2 = 1 ThenDebug.Print 1 / 0 ' 这里会出错End If' 检查Err对象If Err.Number <> 0 Then错误次数 = 错误次数 + 1Debug.Print "第" & i & "次循环出错,错误号:" & Err.NumberErr.Clear ' 🧹 清空错误记录!这一步很重要!End IfNext iMsgBox "总共发生" & 错误次数 & "次错误"End Sub
如果不加 Err.Clear,Err.Number 会一直保留上次的非零值,导致你以为每次都在出错。
六、总结:与 Bug 和解的艺术
Err 对象就像是 VBA 给你的一个后悔药或者安全气囊。它不会阻止你写 Bug(那是你的天赋),但它能让你在 Bug 发生时,体面地处理现场,而不是让 Excel 直接闪退。
记住这几个要点:
✅ Err 不需要 New,直接用就行
✅ Number=0 表示一切正常,非零表示翻车
✅ Description 是给人看的,Number 是给程序判断的
✅ Raise 用来造错,Clear 用来翻篇
✅ Exit Sub 或 Resume 会自动重置 Err,但养成显式 Clear 的习惯更保险
最后,送给所有 VBA 开发者一句话:
"没有 Bug 的代码是不存在的,但有 Err 对象的代码,至少崩得优雅。"