Excel vba:打开工作簿时分配全局变量;如果发生错误则被删除

Posted

技术标签:

【中文标题】Excel vba:打开工作簿时分配全局变量;如果发生错误则被删除【英文标题】:Excel vba: global variables are assigned when workbook is opened; get erased if an error occurs 【发布时间】:2018-09-06 13:57:52 【问题描述】:

我有一个声明了全局变量的工作簿:

Public var1 As Long
Public var2 As Long
Public var3 As Long
...
Public varN As Long

我还有一个工作簿打开子,它在打开工作簿时将值分配给全局变量:

Private Sub Workbook_open()
    Application.Calculation = xlCalculationAutomatic

    Call calculateRows
    Call assignVariables


    ThisWorkbook.Worksheets("Filling").Protect "somepass", UserInterfaceOnly:=True
    MsgBox ("DONE Workbook_open")
End Sub

它工作正常。但是,我也有一个工作表选择更改子:

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
...
End Sub

和工作表更改子:

Private Sub Worksheet_Change(ByVal Target As Range)
...
End Sub

它们都包含可能导致错误的代码。无论发生什么错误,它总是会删除我的全局变量的值,这只会导致进一步的错误,并且基本上完全关闭工作簿,因为所有工作表选择/更改子都大量使用这些全局变量。

我应该怎么做才能确保它不会发生?使用“On Error GoTo”操作就足够了吗?为什么全局变量还是会被删除?

【问题讨论】:

为什么允许代码中出现错误?这应该是这里的主要关注点。没有足够的信息来真正帮助您。 正确的错误捕获应该可以防止您的 VBA 项目重置。您还可以声明一个名为Initialized 的新全局布尔变量,修改assignVariables 以便将此变量设置为True 并在其他事件处理程序的顶部添加If Not Initialized Then assignVariables 行。这不会解决核心问题,但可能会提供一点安全网。 作为一般经验法则,您的代码应该足够健壮以避免错误,或者捕获错误以便代码优雅地退出。这意味着您的主要问题是您的代码会产生未处理的错误。解决这个问题将解决您的全局变量问题。您还没有提供代码的内容,所以我们没有什么可以分析来尝试提供帮助的。您还通过尝试解决问题而不是解决主要问题来混淆问题。那叫The XY Problem 未处理的错误会导致状态丢失,这是您的全局变量被重置的时候。如果无法避免使用全局变量,或者无法避免错误,则需要使用错误处理。 单击“结束”按钮时状态丢失,就像单击 IDE/调试器中的“停止”按钮、执行 End 指令或运行 @ 立即窗格中的 987654331@ 语句。如果您的程序未托管在 Excel 中,则允许错误/异常冒泡并跳出入口点意味着您的程序崩溃和烧毁,其进程终止,并且所有状态都丢失。不要让运行时错误冒出你的入口点;工作表事件处理程序必须捕获所有错误。此外,使用全局变量可能不是最好的设计。 【参考方案1】:

如 cmets 中所述,单击 End 停止执行将始终重置全局状态。哎呀,不必有错误;单击停止按钮将具有相同的效果。

您至少应该在输入过程中进行错误处理(例如,您的工作表事件处理程序是很好的候选者,因为它们是 Excel 可以与您的 VBA 代码交互的入口点)。

如果您希望即使使用错误处理程序也能找到有问题的错误,则此模式可以提供帮助:

Public Sub Example()
On Error GoTo ErrHandler

'Some code that might error

ExitProcedure:
On Error Resume Next
'Clean up
Exit Sub

ErrorHandler:

MsgBox "Oops"
Resume ExitProcedure
Resume 'for debugging

End Sub

请注意错误处理程序末尾的无法访问的Resume。这是无法访问的,因为Resume ExitProcedure 将始终首先执行。因此,当您获得消息框时,您可以使用ctrl+break 破解代码,然后代码会将您带到Resume ExitProcedure。然后,您可以将黄色箭头拖到无法访问的Resume 上,按 F8 单步执行,然后将您带回到导致错误的行。

请注意,它仅适用于程序;如果错误冒泡,它将返回到错误冒泡之前调用的过程,而不是过程中的行。

或者,如果您不喜欢所有额外的工作,您可以考虑购买诸如vbWatchDog 之类的插件,这对错误处理有很大帮助。

即便如此,这些也无助于解决根本问题——全局状态是脆弱的,可能会被破坏……你可以使用停止按钮甚至End。您希望尽可能避免使用全局并将它们限定范围或将它们填充到类实例中,以便当您重复代码块时,它可以在该范围内重建状态。全局状态下的东西越少越好。

【讨论】:

我真的很喜欢你的最后一段。 +1。 每天更聪明一点...我现在没有你可以使用来自 MsgBoxes 的 Ctrl+Break 暂停执行。我通常的方法是尝试找到相应的MsgBox 语句并在那里放置一个断点。所以感谢您的快捷方式! @Inarion 你从来没有做过一个失控不得不放下的程序?! @Marcucciboy2 在“快速测试”那个闪亮的新 Do ... Loop 构造时,我已经失去了几个小时的工作,我不小心忽略了任何退出条件。 (它也没有DoEvents,所以我绝望的击键没有被Excel听到......)所以,我基本上知道Ctrl+Break,但我从来没有想过将它应用到MsgBox。 仅供参考,Ctrl+Break 也可用于打破无限循环,甚至只是打破一个花费时间过长的代码块。 (请注意,它不能中断指令本身的执行,因此如果写入文件的行需要 10 分钟,那么在您最终从 VBE 获得该对话框之前将需要很长时间。)

以上是关于Excel vba:打开工作簿时分配全局变量;如果发生错误则被删除的主要内容,如果未能解决你的问题,请参考以下文章

使用循环打开文件路径中的所有excel文件后,有没有办法通过vba创建工作簿变量来引用这些文件?

用VBA代码打开其他excel工作簿(有打开密码的)???

VBA Excel UDF保留原始值

VBA 一些概念与细节

Excel VBA计数和存储所有打开的工作簿列表

比较两个工作簿工作表excel vba