获取当前VBA函数的名称 [英] Get Name of Current VBA Function
问题描述
对于错误处理代码,我想获取发生错误的当前VBA函数(或子函数)的名称.有人知道该怎么做吗?
谢谢,我希望有一个未记录的技巧可以自我确定功能,但是显然不存在.猜猜我会保留当前的代码:
Option Compare Database: Option Explicit: Const cMODULE$ = "basMisc"
Public Function gfMisc_SomeFunction$(target$)
On Error GoTo err_handler: Const cPROC$ = "gfMisc_SomeFunction"
...
exit_handler:
....
Exit Function
err_handler:
Call gfLog_Error(cMODULE, cPROC, err, err.Description)
Resume exit_handler
End Function
没有什么可以获取当前函数名称的,但是您可以使用VBA对象生存期是确定性的事实来构建一个相当轻量级的跟踪系统.例如,您可以使用以下代码创建一个名为"Tracer"的类:
Private proc_ As String
Public Sub init(proc As String)
proc_ = proc
End Sub
Private Sub Class_Terminate()
If Err.Number <> 0 Then
Debug.Print "unhandled error in " & proc_
End If
End Sub
,然后在诸如以下的例程中使用该类:
Public Sub sub1()
Dim t As Tracer: Set t = New Tracer
Call t.init("sub1")
On Error GoTo EH
Call sub2
Exit Sub
EH:
Debug.Print "handled error"
Call Err.Clear
End Sub
Public Sub sub2()
Dim t As Tracer: Set t = New Tracer
Call t.init("sub2")
Call Err.Raise(4242)
End Sub
如果运行"sub1",则应获得以下输出:
unhandled error in sub2
handled error
因为错误导致例程退出时,确定性地破坏了"sub2"中的Tracer实例.
在C ++中,这种常规模式在"RAII"的名称下经常出现,但是在VBA中也可以正常工作(除了使用类的普遍麻烦之外).
要解决David Fenton的评论,即这是一个简单问题的相对复杂的解决方案,我认为问题实际上并不那么简单!
我理所当然地认为,我们都同意我们不希望在VBA程序中为每个例程提供自己的错误处理程序. (请在此处查看我的推理: VBA错误冒泡" )>
如果某些内部例程没有自己的错误处理程序,那么当我们执行捕获错误时,我们所知道的只是在触发了错误处理程序的例程中或发生在例程中时发生的情况.例程在调用堆栈中更深的地方.因此,据我了解,该问题实际上是跟踪程序执行的问题之一.跟踪例程的输入当然很容易.但是跟踪出口确实可能非常复杂.例如,可能会出现错误!
通过RAII方法,我们可以使用VBA对象生命周期管理的自然行为来识别退出例程的时间,无论是通过退出",结束"还是错误.我的玩具示例只是为了说明这一概念.在我自己的小型VBA框架中,真正的示踪剂"当然会更复杂,但还会做更多:
Private Sub Class_Terminate()
If unhandledErr_() Then
Call debugTraceException(callID_, "Err unhandled on exit: " & fmtCurrentErr())
End If
If sendEntryExit_ Then
Select Case exitTraceStatus_
Case EXIT_UNTRACED
Call debugTraceExitImplicit(callID_)
Case EXIT_NO_RETVAL
Call debugTraceExitExplicit(callID_)
Case EXIT_WITH_RETVAL
Call debugTraceExitExplicit(callID_, retval_)
Case Else
Call debugBadAssumption(callID_, "unrecognized exit trace status")
End Select
End If
End Sub
但是使用它仍然非常简单,并且比起每个例程中的EH"方法要少得多:
Public Function apply(functID As String, seqOfArgs)
Const PROC As String = "apply"
Dim dbg As FW_Dbg: Set dbg = mkDbg(MODL_, PROC, functID, seqOfArgs)
...
自动生成样板很容易,尽管我实际上输入了样板,然后自动检查以确保例程/参数名称匹配,这是我测试的一部分.
For error handling code, I would like to get the name of the current VBA function (or sub) that the error occurred in. Does anyone know how this could be done?
[EDIT] Thanks all, I had hoped that an undocumented trick existed to self-determine the function, but that obviously doesn't exist. Guess I'll stay with my current code:
Option Compare Database: Option Explicit: Const cMODULE$ = "basMisc"
Public Function gfMisc_SomeFunction$(target$)
On Error GoTo err_handler: Const cPROC$ = "gfMisc_SomeFunction"
...
exit_handler:
....
Exit Function
err_handler:
Call gfLog_Error(cMODULE, cPROC, err, err.Description)
Resume exit_handler
End Function
There's nothing to get the current function name, but you can build a fairly lightweight tracing system using the fact that VBA object lifetimes are deterministic. For example, you can have a class called 'Tracer' with this code:
Private proc_ As String
Public Sub init(proc As String)
proc_ = proc
End Sub
Private Sub Class_Terminate()
If Err.Number <> 0 Then
Debug.Print "unhandled error in " & proc_
End If
End Sub
and then use that class in routines like:
Public Sub sub1()
Dim t As Tracer: Set t = New Tracer
Call t.init("sub1")
On Error GoTo EH
Call sub2
Exit Sub
EH:
Debug.Print "handled error"
Call Err.Clear
End Sub
Public Sub sub2()
Dim t As Tracer: Set t = New Tracer
Call t.init("sub2")
Call Err.Raise(4242)
End Sub
If you run 'sub1', you should get this output:
unhandled error in sub2
handled error
because your Tracer instance in 'sub2' was deterministically destroyed when the error caused an exit from the routine.
This general pattern is seen a lot in C++, under the name "RAII", but it works just fine in VBA too (other than the general annoyance of using classes).
EDIT:
To address David Fenton's comment that this is a relatively complicated solution to a simple problem, I don't think the problem is actually that simple!
I'm taking it for granted that we all agree that we don't want to give every single routine in our VBA program its own error handler. (See my reasoning here: VBA Error "Bubble Up")
If some internal routines don't have their own error handlers, then when we do catch an error, all we know is that is happened in the routine with the error handler that fired or in a routine somewhere deeper in the call stack. So the problem as I understand it is really one of tracing the execution of our program. Tracing routine entry is easy of course. But tracing exit can indeed be quite complicated. For example, there might be an error that gets raised!
The RAII approach allows us to use the natural behavior of VBA object life management to recognize when we've exited a routine, whether through an 'Exit', 'End', or error. My toy example is just meant to illustrate the concept. The real "tracer" in my own little VBA framework is certainly more complex, but also does more:
Private Sub Class_Terminate()
If unhandledErr_() Then
Call debugTraceException(callID_, "Err unhandled on exit: " & fmtCurrentErr())
End If
If sendEntryExit_ Then
Select Case exitTraceStatus_
Case EXIT_UNTRACED
Call debugTraceExitImplicit(callID_)
Case EXIT_NO_RETVAL
Call debugTraceExitExplicit(callID_)
Case EXIT_WITH_RETVAL
Call debugTraceExitExplicit(callID_, retval_)
Case Else
Call debugBadAssumption(callID_, "unrecognized exit trace status")
End Select
End If
End Sub
But using it is still pretty simple, and amounts to less boilerplate than the "EH in every routine" approach anyway:
Public Function apply(functID As String, seqOfArgs)
Const PROC As String = "apply"
Dim dbg As FW_Dbg: Set dbg = mkDbg(MODL_, PROC, functID, seqOfArgs)
...
Automatically generating the boilerplate is easy, although I actually type it in and then automatically check to make sure routine/arg names match as part of my tests.
这篇关于获取当前VBA函数的名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!