获取当前 VBA 函数的名称 [英] Get Name of Current VBA Function

查看:52
本文介绍了获取当前 VBA 函数的名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于错误处理代码,我想获取发生错误的当前 VBA 函数(或子函数)的名称.有谁知道如何做到这一点?

谢谢大家,我曾希望存在一个未记录的技巧来自我确定函数,但这显然不存在.猜猜我会保留我当前的代码:

选项比较数据库:选项显式:Const cMODULE$ = "basMisc"公共函数 gfMisc_SomeFunction$(target$)出错时转到 err_handler: Const cPROC$ = "gfMisc_SomeFunction"...出口处理程序:....退出函数错误处理程序:调用 gfLog_Error(cMODULE, cPROC, err, err.Description)恢复exit_handler结束函数

解决方案

没有什么可以获取当前函数名称的,但是您可以利用 VBA 对象生命周期是确定性的这一事实来构建一个相当轻量级的跟踪系统.例如,您可以使用以下代码创建一个名为Tracer"的类:

private proc_ As StringPublic Sub init(proc As String)proc_ = proc结束子私有子类_Terminate()如果 Err.Number <>0 那么Debug.Print& 中未处理的错误"过程_万一结束子

然后在例程中使用该类,例如:

公共子 sub1()将 t 设为示踪剂:设置 t = 新示踪剂调用 t.init("sub1")出错时转到 EH呼叫 sub2退出子诶:Debug.Print处理错误"调用 Err.Clear结束子公共子 sub2()将 t 设为示踪剂:设置 t = 新示踪剂调用 t.init("sub2")调用 Err.Raise(4242)结束子

如果你运行'sub1',你应该得到这个输出:

 sub2 中未处理的错误处理错误

因为当错误导致例程退出时,sub2"中的 Tracer 实例被确定性地销毁.

这种通用模式在 C++ 中经常出现,名称为RAII",但它在 VBA 中也能正常工作(除了使用类的普遍烦恼).

针对 David Fenton 的评论,即这是对简单问题的相对复杂的解决方案,我认为问题实际上没有那么简单!

我想当然地认为我们都同意我们不想为 VBA 程序中的每个例程提供自己的错误处理程序.(在此处查看我的推理:VBA 错误冒泡")>

如果某些内部例程没有自己的错误处理程序,那么当我们确实捕获错误时,我们所知道的就是发生在具有触发错误处理程序的例程中或在例程位于调用堆栈更深处的某个地方.所以我理解的问题实际上是跟踪我们程序的执行之一.跟踪常规条目当然很容易.但是跟踪出口确实可能相当复杂.例如,可能会引发一个错误!

RAII 方法允许我们使用 VBA 对象生命周期管理的自然行为来识别我们何时退出例程,无论是通过退出"、结束"还是错误.我的玩具示例只是为了说明这个概念.我自己的小 VBA 框架中真正的跟踪器"当然更复杂,但也做得更多:

私有子类_Terminate()如果未处理Err_() 那么Call debugTraceException(callID_, "Err unhandled on exit: " & fmtCurrentErr())万一如果 sendEntryExit_ 然后选择案例 exitTraceStatus_案例 EXIT_UNTRACED调用 debugTraceExitImplicit(callID_)案例 EXIT_NO_RETVAL调用 debugTraceExitExplicit(callID_)案例 EXIT_WITH_RETVAL调用 debugTraceExitExplicit(callID_, retval_)其他情况调用 debugBadAssumption(callID_, "unrecognized exit trace status")结束选择万一结束子

但是使用它仍然非常简单,并且比EH in everyroutine"方法更少样板:

公共函数 apply(functID As String, seqOfArgs)Const PROC As String = "apply"Dim dbg As FW_Dbg:设置 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆