如何在运行时获取过程或函数名? [英] How to get the procedure or function name at runtime?
问题描述
我目前正在处理这样的错误:
Sub foo()
Const proc_name as string =foo
错误GoTo ErrHandler
'做东西
ExitSub:
退出子
ErrHandler:
ErrModule.ShowMessageBoxModuleName,proc_name
恢复ExitSub
End Sub
最近,在更新了函数名称后,我最近经历了一个常数,而不是常量值。我想将过程的名称返回到我的错误处理程序。
我知道我必须与$ code> VBIDE.CodeModule 对象找到它。我已经使用Microsoft Visual Basic应用程序扩展库进行了一些元编程,但是我在运行时没有做到这一点。我没有以前的尝试,在我再次尝试这个尝试之前,我想知道是否可以远程进行。
事情这不会工作
- 使用一些内置的VBA库访问调用堆栈。它不存在
- 当我进入和退出每个人时,通过从数组中推送和弹出过程名称来实现我自己的调用堆栈。这仍然需要我将proc名称作为一个字符串传递给别的地方。
- 第三方工具,如 vbWatchDog 。这个 可以正常工作,但是我不能使用这个项目的第三方工具。
注意
vbWatchdog似乎做这通过API调用直接访问内核内存。
我不太确定这将是多么有用..
好的是,您不必担心子/函数名称 - 您可以自由更改它。所有您需要关心的是错误处理程序标签名称的唯一性。
例如
如果您可以避免重复的错误处理程序标签在不同的子/功能
不要做⇩⇩ ⇩⇩
Sub Main()
错误GoTo ErrHandler
Debug.Print 1 / 0
ErrHandler:
Debug.Print主要处理错误
SubMain
End Sub
Sub SubMain()
错误GoTo ErrHandler
Debug.Print 1/0
ErrHandler:
Debug.PrintSubMain中的处理错误
End Sub
然后下面的代码应该
工作。注意:我无法彻底测试,但我确信你可以调整它,如果有任何帮助,可以让它工作。 em>
注意:添加引用到 Visual Basic for Applications可扩展性5.3
通过工具 - > VBE中的引用
Sub Main()
'另外你还应该做什么:
'写一个布尔函数,检查是否没有重复的错误处理程序标签
'这将确保您没有收到错误的子/ fn名称返回
Foo
Boo
End Sub
功能Foo()
'记住在处理程序
'中设置标签名称(handlerLabel)每个处理程序标签应该是唯一的,以避免错误
错误GoTo FooErr
单元格(0,1)= vbNullString故意导致错误
FooErr:
Dim handlerLabel $
handlerLabel =FooErr或不要暗淡并通过errHandler名称直接到GetFnOrSubName函数
Debug.Print发生在& Application.VBE.ActiveCodePane.CodeModule.Name& :& GetFnOrSubName(handlerLabel)
结束函数
Sub Boo()
错误GoTo BooErr
单元格(0, 1)= vbNullString'故障原因
BooErr:
Debug.Print错误发生在& Application.VBE.ActiveCodePane.CodeModule.Name& :& GetFnOrSubName(BooErr)
End Sub
'返回GetFnOrSubName中需要的CodeModule引用fn
专用函数GetCodeModule(codeModuleName As String)作为VBIDE.CodeModule
Dim VBProj作为VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
设置VBProj = ThisWorkbook.VBProject
设置VBComp = VBProj.VBComponents(codeModuleName)
设置GetCodeModule = VBComp.CodeModule
结束函数
'返回发生错误的sub的名称
私有函数GetFnOrSubName $(handlerLabel $)
Dim VBProj作为VBIDE.VBProject
Dim VBComp作为VBIDE.VBComponent
Dim CodeMod作为VBIDE.CodeModule
设置VBProj = ThisWorkbook.VBProject
设置VBComp = VBProj.VBComponents(Application.VBE.ActiveCodePane.CodeModule.Name)
设置CodeMod = VBComp.CodeModule
代码$
代码= CodeMod.Lines(1,CodeMod .CountOfLines)
Dim handlerAt&放大器;
handlerAt = InStr(1,code,handlerLabel,vbTextCompare)
如果handlerAt然后
Dim isFunction&
Dim isSub&
isFunction = InStrRev(Mid $(code,1,handlerAt),Function,-1,vbTextCompare)
isSub = InStrRev(Mid $(code,1,handlerAt) Sub,-1,vbTextCompare)
如果isFunction> isSub Then
'它是一个函数
GetFnOrSubName = Split(Mid $(code,isFunction,40),()(0)
Else
'它是一个子
GetFnOrSubName = Split($ $(code,isSub,40),()(0)
End If
End If
End Function
Is there any way to return the name of a function or procedure at runtime?
I'm currently error handling something like this:
Sub foo()
Const proc_name as string = "foo"
On Error GoTo ErrHandler
' do stuff
ExitSub:
Exit Sub
ErrHandler:
ErrModule.ShowMessageBox "ModuleName",proc_name
Resume ExitSub
End Sub
I recently experienced one of my constants lying to me after I updated a function name, but not the constant value. I want to return the name of the procedure to my error handler.
I know that I will have to interact with the VBIDE.CodeModule
object to find it. I've done a little bit of meta-programming with the Microsoft Visual Basic for Applications Extensibility library, but I've not had any success with doing this at runtime. I don't have my previous attempts, and before I dig my heels in to try this again, I want to know if it's even remotely possible.
Things that won't work
- Using some built in VBA Library to access the call stack. It doesn't exist.
- Implementing my own call stack by pushing and popping procedure names from an array as I enter and exit each one. This still requires that I pass the proc name somewhere else as a string.
- A third party tool like vbWatchDog. This does work, but I can't use a third party tool for this project.
Note
vbWatchdog seems to do this by directly accessing the kernel memory via API calls.
I am not quite sure how helpful this is going to be...
The good thing is that you will not have to worry about the sub/function name - you are free to change it. All you have to care about is the uniqueness of the error handler label name.
For example
if you can avoid duplicate error handler labels in different subs/functions
don't do ⇩⇩⇩⇩⇩
Sub Main()
On Error GoTo ErrHandler
Debug.Print 1 / 0
ErrHandler:
Debug.Print "handling error in Main"
SubMain
End Sub
Sub SubMain()
On Error GoTo ErrHandler
Debug.Print 1 / 0
ErrHandler:
Debug.Print "handling error in SubMain"
End Sub
then the below code should work.
Note: I haven't been able to test it thoroughly but I am sure you can tweak it and get it work if it's of any help.
Note: Add references to Visual Basic for Applications Extensibility 5.3
via Tools -> References in VBE
Sub Main()
' additionally, this is what else you should do:
' write a Boolean function that checks if there are no duplicate error handler labels
' this will ensure you don't get a wrong sub/fn name returned
Foo
Boo
End Sub
Function Foo()
' remember to set the label name (handlerLabel) in the handler
' each handler label should be unique to avoid errors
On Error GoTo FooErr
Cells(0, 1) = vbNullString ' cause error deliberately
FooErr:
Dim handlerLabel$
handlerLabel = "FooErr" ' or don't dim this and pass the errHandler name directly to the GetFnOrSubName function
Debug.Print "Error occured in " & Application.VBE.ActiveCodePane.CodeModule.Name & ": " & GetFnOrSubName(handlerLabel)
End Function
Sub Boo()
On Error GoTo BooErr
Cells(0, 1) = vbNullString ' cause error deliberately
BooErr:
Debug.Print "Error occured in " & Application.VBE.ActiveCodePane.CodeModule.Name & ": " & GetFnOrSubName("BooErr")
End Sub
' returns CodeModule reference needed in the GetFnOrSubName fn
Private Function GetCodeModule(codeModuleName As String) As VBIDE.CodeModule
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Set VBProj = ThisWorkbook.VBProject
Set VBComp = VBProj.VBComponents(codeModuleName)
Set GetCodeModule = VBComp.CodeModule
End Function
' returns the name of the sub where the error occured
Private Function GetFnOrSubName$(handlerLabel$)
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Set VBProj = ThisWorkbook.VBProject
Set VBComp = VBProj.VBComponents(Application.VBE.ActiveCodePane.CodeModule.Name)
Set CodeMod = VBComp.CodeModule
Dim code$
code = CodeMod.Lines(1, CodeMod.CountOfLines)
Dim handlerAt&
handlerAt = InStr(1, code, handlerLabel, vbTextCompare)
If handlerAt Then
Dim isFunction&
Dim isSub&
isFunction = InStrRev(Mid$(code, 1, handlerAt), "Function", -1, vbTextCompare)
isSub = InStrRev(Mid$(code, 1, handlerAt), "Sub", -1, vbTextCompare)
If isFunction > isSub Then
' it's a function
GetFnOrSubName = Split(Mid$(code, isFunction, 40), "(")(0)
Else
' it's a sub
GetFnOrSubName = Split(Mid$(code, isSub, 40), "(")(0)
End If
End If
End Function
这篇关于如何在运行时获取过程或函数名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!