我可以在VBA中以编程方式检查Excel函数签名吗?

而不是按名称显式访问所有参数,有没有办法从函数内部以编程方式获取函数参数列表?

所以对于这个函数签名:

Function doSomething(Arg1 as String, Arg2 as Range, Optional Arg3 as String): 

在那里,理想情况下,包含参数名称及其元数据(types,可选,默认值等)的对象? 例如,这个函数里面的代码Me.Arguments会产生一个像这样的字典:

 { "Arg1": { "Type": String, "Optional": False, "Default": Nothing }, "Arg2": { "Type": Range, "Optional": False, "Default": Nothing }, "Arg1": { "Type": String, "Optional": True, "Default": Nothing } } 

谢谢。

你可能会考虑

  • 使用Application.Caller获取对包含公式的单元格的引用,然后使用该单元格的.Formula属性以公式forms获取公式。
  • 然后,您可以parsing公式,以文本forms获取参数。

当我想将3D范围作为parameter passing给UDF时,我这样做了(所以我可以制作一套像COUNTIFSUMIF这样的function,可以在Sheet1:Sheet99!$A$1:$A$1000等3D范围内工作) 。 我发现当3D范围中的值发生变化时,UDF将被触发 – 但是,只要我需要对接收UDF中的3D范围的变体进行任何操作,UDF就会产生运行时错误。 我的解决方法是在第一段中讨论 – 获取公式并parsing它以获得3D范围作为文本。

我尝试插入代码作为一个块,但受格式要求的阻碍。 这是在我的SkyDrive上使用它的工作簿

可以用这个吗?

 ThisWorkbook.VBProject.VBComponents("[Your_Code_Module_Name]").CodeModule 

…并从那里获取方法签名和参数? 就像下面的例子(只是一个例子)。

对于你可能只有vbext_ProcKind.vbext_pk_Proc = 0将是有用的,但在这个例子中是所有可用的proc种类。

标准模块“Module1”:

 ' Add referemce to Microsoft Scripting Runtime (Scripting.Dictionary) Sub main() Call doSomething("hello", Nothing) End Sub ' the code Me.Arguments inside this function would produce a dictionary Function doSomething(Arg1 As String, _ Arg2 As Range, Optional Arg3 As Long = 123456789) Dim thisCodeArguments As Scripting.Dictionary Dim thisCodeModule As Variant Set thisCodeModule = ThisWorkbook.VBProject.VBComponents("Module1").CodeModule With New ThisCode Set thisCodeArguments = .Arguments(thisCodeModule, "doSomething", 0) ' 0 = VBIDE.vbext_ProcKind.vbext_pk_Proc Set thisCodeArguments = .Arguments(thisCodeModule, "someProperty", 3) ' 3 = VBIDE.vbext_ProcKind.vbext_pk_Get Set thisCodeArguments = .Arguments(thisCodeModule, "someProperty", 1) ' 1 = VBIDE.vbext_ProcKind.vbext_pk_Let Set thisCodeArguments = .Arguments(thisCodeModule, "someProperty", 2) ' 2 = VBIDE.vbext_ProcKind.vbext_pk_Set End With End Function Public Property Get someProperty() As Variant End Property Public Property Let someProperty(ByVal vNewValue As Variant) End Property Public Property Set someProperty(ByVal vNewValue As Variant) End Property 

类模块“ThisCode”:

 Public Function Arguments( _ targetCodeModule As Variant, _ procedureName As String, _ vbextProcKind As Integer) _ As Scripting.Dictionary Dim startLine As Long Dim countLines As Long Dim code As String Dim leftParentheses As Long Dim rightParentheses As Long Dim argumentsText As String Dim argumentsArray() As String Dim argumentParts() As String Dim argumentName As String Set Arguments = New Scripting.Dictionary With targetCodeModule startLine = .ProcStartLine(procedureName, vbextProcKind) countLines = .ProcCountLines(procedureName, vbextProcKind) code = .Lines(startLine, countLines) End With leftParentheses = InStr(code, "(") If leftParentheses > 0 Then rightParentheses = InStr(leftParentheses + 1, code, ")") Else Err.Raise 123, , "No left parentheses found" ' TODO: error number End If If rightParentheses > 0 Then argumentsText = Trim(Mid(code, leftParentheses + 1, _ rightParentheses - leftParentheses - 1)) Else Err.Raise 456, , "No right parentheses found" ' TODO: error number End If If Len(argumentsText) = 0 Then Exit Function argumentsText = Replace(argumentsText, "_", "") argumentsText = Replace(argumentsText, vbCrLf, "") argumentsArray = Split(argumentsText, ",") Dim i As Long Dim j As Long Dim argumentInfo As Argument Dim argumentArray() As String For i = LBound(argumentsArray) To UBound(argumentsArray) Set argumentInfo = New Argument Set argumentInfo.DefaultValue = Nothing argumentInfo.IsOptional = False argumentInfo.TypeName = "" argumentParts = Split(argumentsArray(i)) For j = LBound(argumentParts) To UBound(argumentParts) If Len(Trim(argumentParts(j))) = 0 Then GoTo continue If Trim(argumentParts(j)) = "Optional" Then argumentInfo.IsOptional = True argumentName = Trim(argumentParts(j + 1)) ElseIf Trim(argumentParts(j)) = "As" Then argumentName = Trim(argumentParts(j - 1)) argumentInfo.TypeName = Trim(argumentParts(j + 1)) ElseIf Trim(argumentParts(j)) = "=" Then argumentInfo.DefaultValue = CVar(argumentParts(j + 1)) End If continue: Next j Arguments.Add argumentName, argumentInfo Next i End Function 

类模块“参数”:

 Public TypeName As String Public IsOptional As Boolean Public DefaultValue As Variant 

字典: 在这里输入图像说明

好问题!

我不这么认为….离我最近的就是使用这种解决方法,用ActiveCell的公式预先填充FunctionWizard (下面的代码使用第一个可用的空白单元格查找)来调用函数对话框与NPV

我曾尝试将parameter passing给同一个对话框,但没有成功。

 Sub Kludge() Dim rng1 As Range Set rng1 = Cells(Rows.Count, "A").End(xlUp).Offset(1, 0) With rng1 Application.Goto rng1 .Value = "=NPV(10%,-10,5,5,5)" c = Application.Dialogs(xlDialogFunctionWizard).Show .ClearContents End With 

在这里输入图像描述

您可以使用Application.RegisteredFunctions获得注册函数的typesTypestring 。 typesstring为您提供每个参数的数据types以及该函数是multithreading还是易失性的。

但它只适用于XLL函数,而不适用于VBAAutomation函数,而且您还必须做一些技巧来匹配函数的名称与typesstring。

看我的博客文章