Excel 带日志记录的加载项 class:如何应对 VBA 状态丢失?
Excel add-in with logging class: How to react to VBA state loss?
设置
我正在开发和维护一个 Excel 插件,它在 Excel 的功能区 UI 中带有自己的控件选项卡。我以前遇到过状态丢失的问题(意思是丢失所有具有全局范围的变量,静态变量等,当然包括我对 RibbonUI 的引用)。关于功能区引用,我通过包含一个“重置功能区”按钮“解决”了这个问题,该按钮从持久存储的指针中恢复引用,然后使功能区无效。虽然肯定不是最优雅的,但这部分工作得很好。
然而,在引入日志记录class之后,状态丢失问题再次困扰着我。记录器在 ThisWorkbook 的模块中实例化:
Private Sub Workbook_Open()
Set LogToFile = SingletonFactory.getToFileLogger
End Sub
然后投入工作,例如,如下:
Private Sub buttonReloadObjects_onAction(ByVal control As IRibbonControl)
LogToFile.trace "Event firing: buttonReloadObjects_onAction"
' more stuff happening...
invalidateRibbon ' restores ribbon object and invalidates it
End Sub
加载项加载时会实例化记录器,这样我就可以在加载项代码的范围内自由记录我想要的任何内容。它有几个日志记录级别,如 trace/debug/error/... 和一些其他方法。通常它工作得很好 - 直到状态丢失命中(通常由不相关的错误引起,然后单击“结束”)。
状态丢失
此时 VBA 环境忘记了我的 LogToFile
对象的存在并且不再起作用,因为每次单击功能区控件都会触发 runtime error 91: Object variable or with block variable not set
指向到第一行包含对 LogToFile
.
的引用
解决方案?
现在,不用像放置
这样疯狂的变通办法了
if not isObject(LogToFile) then
Set LogToFile = SingletonFactory.getToFileLogger
end if
LogToFile.trace "Message"
在 任何 出现 LogToFile
之前,我能想出的唯一真正的“解决方案”是将我所有的记录器调用包装在函数中(驻留在一个标准模块)并在我想向日志发送内容时调用这些函数。这样我就可以在需要对象之前捕获丢失的对象引用,并且避免调用未实例化对象的方法。
然而,在将所有内容整齐地封装在 class 模块之后,我觉得很奇怪,甚至可能是错误的(?),沿着这条路走下去。
那么,对于记录器实例丢失的问题,是否有“合适”的解决方案呢?还是我建议的方法已经尽可能合适了?
注意:这个问题当然不是日志记录 classes 特有的。它会影响所有全局变量,尤其是我的 ApplicationEventClass。由于在代码的所有入口点周围频繁使用记录器,这个问题恰好是最明显的。
您只需要一个 returns 原始变量或重置它的函数。如果您调用该函数 LogToFile
除了删除多余的 Workbook_Open
代码外,您不需要更改任何其他代码。所以:
Function LogToFile() As WhateverVariableType
Static temp as WhateverVariableType
If temp is Nothing then Set temp = SingletonFactory.getToFileLogger
Set LogToFile = temp
End Function
通过这种方式,您在编写代码时仍将受益于 Intellisense。
注意:您可能实际上不需要临时变量 - 这取决于是否有您想要保留的设置。如果有,你可能也想在函数中重置它们。
设置
我正在开发和维护一个 Excel 插件,它在 Excel 的功能区 UI 中带有自己的控件选项卡。我以前遇到过状态丢失的问题(意思是丢失所有具有全局范围的变量,静态变量等,当然包括我对 RibbonUI 的引用)。关于功能区引用,我通过包含一个“重置功能区”按钮“解决”了这个问题,该按钮从持久存储的指针中恢复引用,然后使功能区无效。虽然肯定不是最优雅的,但这部分工作得很好。
然而,在引入日志记录class之后,状态丢失问题再次困扰着我。记录器在 ThisWorkbook 的模块中实例化:
Private Sub Workbook_Open()
Set LogToFile = SingletonFactory.getToFileLogger
End Sub
然后投入工作,例如,如下:
Private Sub buttonReloadObjects_onAction(ByVal control As IRibbonControl)
LogToFile.trace "Event firing: buttonReloadObjects_onAction"
' more stuff happening...
invalidateRibbon ' restores ribbon object and invalidates it
End Sub
加载项加载时会实例化记录器,这样我就可以在加载项代码的范围内自由记录我想要的任何内容。它有几个日志记录级别,如 trace/debug/error/... 和一些其他方法。通常它工作得很好 - 直到状态丢失命中(通常由不相关的错误引起,然后单击“结束”)。
状态丢失
此时 VBA 环境忘记了我的 LogToFile
对象的存在并且不再起作用,因为每次单击功能区控件都会触发 runtime error 91: Object variable or with block variable not set
指向到第一行包含对 LogToFile
.
解决方案?
现在,不用像放置
这样疯狂的变通办法了if not isObject(LogToFile) then
Set LogToFile = SingletonFactory.getToFileLogger
end if
LogToFile.trace "Message"
在 任何 出现 LogToFile
之前,我能想出的唯一真正的“解决方案”是将我所有的记录器调用包装在函数中(驻留在一个标准模块)并在我想向日志发送内容时调用这些函数。这样我就可以在需要对象之前捕获丢失的对象引用,并且避免调用未实例化对象的方法。
然而,在将所有内容整齐地封装在 class 模块之后,我觉得很奇怪,甚至可能是错误的(?),沿着这条路走下去。
那么,对于记录器实例丢失的问题,是否有“合适”的解决方案呢?还是我建议的方法已经尽可能合适了?
注意:这个问题当然不是日志记录 classes 特有的。它会影响所有全局变量,尤其是我的 ApplicationEventClass。由于在代码的所有入口点周围频繁使用记录器,这个问题恰好是最明显的。
您只需要一个 returns 原始变量或重置它的函数。如果您调用该函数 LogToFile
除了删除多余的 Workbook_Open
代码外,您不需要更改任何其他代码。所以:
Function LogToFile() As WhateverVariableType
Static temp as WhateverVariableType
If temp is Nothing then Set temp = SingletonFactory.getToFileLogger
Set LogToFile = temp
End Function
通过这种方式,您在编写代码时仍将受益于 Intellisense。
注意:您可能实际上不需要临时变量 - 这取决于是否有您想要保留的设置。如果有,你可能也想在函数中重置它们。