呵呵,
每次我需要它时,我都会为Application.OnTime
( 或,类似的)参数使用棘手的语法。Application.Run
我经常来这里,也来过这里提到的其他链接几次。通常他们几乎但并不完全将我带到那里。
因此,我花了一些时间为自己制作了一些工作示例以供将来参考,并且我最终也说服自己,我了解正在发生的事情。
所以我正在分享我的解决方案,最后我想我可以尝试回答关于..简洁地解释/证明语法的原始问题.. ..
我故意给出非常完整的明确代码行有两个原因:-
_ 1. 如果您只需要它,很容易将其简化为更常用的缩短版本,但反过来,如果您需要,从更常见的简化形式到完整的显式形式,则非常困难。
_ 2.显示完整的显式代码行语法有助于我尝试解释语法,因此需要完全回答问题。
例如,当我们想要在关闭的工作簿中触发宏时,将需要完整的显式语法以确保打开正确的文件。(在这种情况下,将打开关闭的工作簿。VBAApplication.OnTime
代码行将执行此打开操作,前提是它具有完整的显式形式)
我正在使用 2 个示例文件。如果你想试试我的演示,那么第一个应该打开,第二个可以关闭或打开,但第二个应该在同一个文件夹中。(之所以需要在同一个文件夹中只是为了简化演示,-我已经组织了演示宏将在同一个文件夹中查找关闭的工作簿。在实践中,如果您完全替换,关闭的工作簿可以在任何地方此位,(包括第一个 "
),带有已关闭工作簿的完整路径和文件名
" & ThisWorkbook.Path & "\" & "UverFile.xls
所以你可以用以下形式替换最后一点:
C:\Elston\Desktop\MyFolder\UverFile.xls
一个完整的代码行将具有如下形式:
Application.OnTime Now(), "'C:\Elston\Desktop\MyFolder\UverFile.xls" & "'" & "!'Modul1.MacroUndermacroInUverFile 465, ""25""'"
.
打开工作簿 - MainFile.xls:https ://app.box.com/s/prqhroiqcb0qccewz5si0h5kslsw5i5h
MainFile.xls 中的模块“<code>Modul1”
Option Explicit
' Public variable code section
Private Pbic_Arg1 As String
Public Pbic_Arg2 As Double
Sub MainMacro() ' https://stackoverflow.com/questions/31439866/multiple-variable-arguments-to-application-ontime/31464597 http://markrowlinson.co.uk/articles.php?id=10
Rem 1
Debug.Print "Rem 1" & vbCr & vbLf & "This workbook module, single arrgument"
' This workbook module, single argument
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.UnderMainMacro 465'": Debug.Print "!'Modul1.UnderMainMacro 465'"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.UnderMainMacro ""465""'": Debug.Print "!'Modul1.UnderMainMacro ""465""'"
Application.OnTime Now(), "'Modul1.UnderMainMacro 465'" ' --- more usual simplified form. In this case I nned the extra Modul1. because Sub UnderMainMacro( ) is private
Debug.Print vbCr & vbLf & "UverFile module, single argument"
' UverFile module, single argument
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & "UverFile.xls" & "'" & "!'Modul1.MacroInUverFile 465'": Debug.Print "!'Modul1.MacroInUverFile 465'"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & "UverFile.xls" & "'" & "!'Modul1.MacroInUverFile ""465""'": Debug.Print "!'Modul1.MacroInUverFile ""465""'"
Debug.Print vbCr & vbLf & "Thisworkbook module, multiple arguments"
' Thisworkbook module, multiple arguments
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.UnderUnderMainMacro 465, 25'": Debug.Print "!'Modul1.UnderUnderMainMacro 465, 25'"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.UnderUnderMainMacro 465, ""25""'": Debug.Print "!'Modul1.UnderUnderMainMacro 465, ""25""' "
Application.OnTime Now(), "'UnderUnderMainMacro 465, 25 '" ' --- more usual simplified form. I don't even need the extra Modul1. because it is not private
Debug.Print vbCr & vbLf & "UverFile module, multiple argument"
' UverFile module, multiple argument
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & "UverFile.xls" & "'" & "!'Modul1.MacroUnderMacroInUverFile 465, 25'": Debug.Print "!'Modul1.MacroUnderMacroInUverFile 465, 25'"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & "UverFile.xls" & "'" & "!'Modul1.MacroUndermacroInUverFile 465, ""25""'": Debug.Print "!'Modul1.MacroUndermacroInUverFile 465, ""25""'"
Debug.Print vbCr & vbLf & "mess about with argument positions"
' mess about with argument positions
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.UnderUnderMainMacro 465 , ""25"" '": Debug.Print "!'Modul1.UnderUnderMainMacro 465 , ""25"" '"
Debug.Print vbCr & vbLf & "This workbook first worksheet code module, single arrgument"
' This workbook first worksheet code module, single arrgument
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'" & ThisWorkbook.Worksheets.Item(1).CodeName & ".InLisWbFirstWsCodeModule 465'": Debug.Print "!'" & ThisWorkbook.Worksheets.Item(1).CodeName & ".InLisWbFirstWcCodeModule 465'"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'" & ThisWorkbook.Worksheets.Item(1).CodeName & ".InLisWbFirstWsCodeModule ""465""'": Debug.Print "!'" & ThisWorkbook.Worksheets.Item(1).CodeName & ".InLisWbFirstWcCodeModule ""465""'"
Debug.Print vbCr & vbLf & "UverFile first worksheet code module, single arrgument"
' UverFile first worksheet code module, single arrgument
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & "UverFile.xls" & "'" & "!'" & "Tabelle1" & ".InUverFileFirstWsCodeModule 465'": Debug.Print "!'" & "Tabelle1" & ".InUverFileFirstWsCodeModule 465'"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & "UverFile.xls" & "'" & "!'" & "Tabelle1" & ".InUverFileFirstWsCodeModule ""465""'": Debug.Print "!'" & "Tabelle1" & ".InUverFileFirstWsCodeModule ""465""'"
Debug.Print vbCr & vbLf & "This workbook first worksheet code module, multiple arguments"
' This workbook first worksheet code module, multiple arguments
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'" & ThisWorkbook.Worksheets.Item(1).CodeName & ".InLisWbFirstWsCodeModuleMultipleArguments 465 , ""25"" '": Debug.Print "!'" & ThisWorkbook.Worksheets.Item(1).CodeName & ".InLisWbFirstWcCodeModuleMultipleArguments 465 , ""25"" '"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'" & ThisWorkbook.Worksheets.Item(1).CodeName & ".InLisWbFirstWsCodeModuleMultipleArguments ""465"" , 25 '": Debug.Print "!'" & ThisWorkbook.Worksheets.Item(1).CodeName & ".InLisWbFirstWcCodeModuleMultipleArguments ""465"" , 25 '"
Debug.Print vbCr & vbLf & "UverFile first worksheet code module, Multiple arrgument"
' UverFile first worksheet code module, Multiple arrgument
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & "UverFile.xls" & "'" & "!'" & "Tabelle1" & ".InUverFileFirstWsCodeModuleMultipleArguments 465 , ""25"" '": Debug.Print "!'" & "Tabelle1" & ".InUverFileFirstWsCodeModuleMultipleArguments 465 , ""25"" '"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & "UverFile.xls" & "'" & "!'" & "Tabelle1" & ".InUverFileFirstWsCodeModuleMultipleArguments ""465"" , ""25"" '": Debug.Print "!'" & "Tabelle1" & ".InUverFileFirstWsCodeModuleMultipleArguments ""465"" , ""25"" '"
Debug.Print vbCr & vbLf & "Doubles do not have to be in quotes either ' This workbook module, double argument arrgument"
' Doubles do not have to be in quotes either ' This workbook module, double argument arrgument
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.DoubleCheck 465.5 , ""25.4"" '": Debug.Print "!'Modul1.DoubleCheck 465.5 , ""25.4"" '"
Rem 2 Variables
Debug.Print vbCr & vbLf & "Rem 2 Variables" & vbCr & vbLf & "'2a) ""Pseudo"" variables use"
'2a) "Pseudo" variables use
Dim Arg1_str465 As String, Arg2_Dbl25 As Double
Let Arg1_str465 = "465.42": Let Arg2_Dbl25 = 25.4
' Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.DoubleCheck Arg1_str465 , Arg2_Dbl25 '": Debug.Print "!'Modul1.DoubleCheck Arg1_str465 , Arg2Db_l25 '" ' This code line will not work, that is to say it will not find the varables and take 0 values when VBA later runs the Scheduled macro, Sub DoubleCheck( )
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.DoubleCheck """ & Arg1_str465 & """ , """ & Arg2_Dbl25 & """ '": Debug.Print "!'Modul1.DoubleCheck """ & Arg1_str465 & """ , """ & Arg2_Dbl25 & """ '"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.DoubleCheck """ & Arg1_str465 & """ , " & Arg2_Dbl25 & " '": Debug.Print "!'Modul1.DoubleCheck """ & Arg1_str465 & """ , " & Arg2_Dbl25 & " '"
Debug.Print vbCr & vbLf & "'2b) Real varable use"
'2b) Real varable use
Let Modul1.Pbic_Arg1 = "465.42": Let Pbic_Arg2 = 25.4
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.DoubleCheck Modul1.Pbic_Arg1 , Pbic_Arg2 '": Debug.Print "!'Modul1.DoubleCheck Modul1.Pbic_Arg1 , Pbic_Arg2 '"
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.DoubleCheck Modul1.Pbic_Arg1, Pbic_Arg2'"
'' Debug.Print Pbic_Arg2 '' This gives 999.99 in Debug F8 mode , 25.4 in normal run
Rem 3 ByRef check
Application.OnTime Now(), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.ByRefCheck'"
Application.OnTime Now() + TimeValue("00:00:00"), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.ByRefCheck'"
Application.OnTime Now() + TimeValue("00:00:01"), "'" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & "'" & "!'Modul1.ByRefCheck'"
End Sub
Private Sub UnderMainMacro(ByVal Nmbr As Long)
MsgBox prompt:="Arg1 is " & Nmbr
End Sub
Sub UnderUnderMainMacro(ByVal Nmbr As Long, ByVal NuverNmbr As Long)
MsgBox prompt:="Arg1 is " & Nmbr & ", Arg2 is " & NuverNmbr
End Sub
Sub DoubleCheck(ByVal DblNmr1 As Double, ByRef DblNmr2 As Double) ' provided the signature line is declared appropriately, all number argument types dont have to be in ""
MsgBox prompt:="Arg1 is " & DblNmr1 & ", Arg2 is " & DblNmr2
Let DblNmr2 = 999.99
End Sub
(上面是运行所有宏的主模块)
MainFile.xls 中第一个工作表“<code>Tabelle1”的工作表类模块
Option Explicit
Sub InLisWbFirstWsCodeModule(ByRef Nmbr As Long)
MsgBox prompt:="Arg1 is " & Nmbr
Let Nmbr = 999
End Sub
Sub InLisWbFirstWsCodeModuleMultipleArguments(ByVal Nmbr As Long, ByVal NuverNmbr As Long)
MsgBox prompt:="Arg1 is " & Nmbr & ", Arg2 is " & NuverNmbr
End Sub
.
. .
已关闭的工作簿 - UverFile.xls: https ://app.box.com/s/u7r2jw79m8ou70otn7xcxced2qkot4w4
UverFile.xls 中的模块“<code>Modul1”
Option Explicit
Private Sub MacroInUverFile(ByVal Nmbr As Long)
MsgBox prompt:="Arg1 is " & Nmbr
End Sub
Sub MacroUnderMacroInUverFile(ByVal Nmbr As Long, ByVal NuverNmbr As Long)
MsgBox prompt:="Arg1 is " & Nmbr & ", Arg2 is " & NuverNmbr
End Sub
UverFile.xls 中第一个工作表“<code>Tabelle1”的工作表类模块
Option Explicit
Sub InUverFileFirstWsCodeModule(ByVal Nmbr As Long)
MsgBox prompt:="Arg1 is " & Nmbr
End Sub
Sub InUverFileFirstWsCodeModuleMultipleArguments(ByVal Nmbr As Long, ByVal NuverNmbr As Long)
MsgBox prompt:="Arg1 is " & Nmbr & ", Arg2 is " & NuverNmbr
End Sub
.
.
我试图提供一个很好的工作示例,我发现这些示例很有用,然后用作模板来完全修改我的需要。
这是对事物如何工作的解释,这使语法更易于理解:
首先是嵌套 '
这通常是 VBA 处理使任何空格被视为文字空格的方式,(而不是例如,将它们误认为是分隔参数)。您会在代码中看到,正如我在论坛帖子中发布的那样,我在所有代码行中都做了一些夸张的空格,这有助于拆分代码的两个主要部分
LHS ,在简化/缩短的使用中通常会被省略
和
RHS,其中大部分总是需要的。(很可能 xou 可能只看到宏名称和参数。额外的模块代码名称允许您在任何模块中使用宏,(无论它们是私有的还是公共的)
为了清楚起见,我在其中一个&
s 任一侧上方的代码窗口中有一些夸张的空格,所以我有
"---------LHS-------------" & "---------RHS------------------"
或类似,伪
"String bit containg full path and file name what you mostly don't use" & "String bit containing the macro name and the arguments like you more typically see"
&
如果您将该代码复制并粘贴到 VB 编辑器代码窗口中,那么两边那些夸张的额外空格将会消失。但是,如果我要在 LHS 上的路径字符串中添加空格,例如将文件名从 更改 UverFile.xls
为Uver File.xls
,那么正如预期的那样,当发布到 VB 编辑器代码窗口时,空格不会更改。这是因为封闭' '
的工作是确保所有内容都按照字面意思理解。
在 RHS 上,我们还需要完全按照我们提供的信息获取信息。我的猜测是,这个字符串部分需要存储到缓冲区中,然后在运行预定的宏时由 VBA 检索和伪物理放入。这就是为什么我可以添加一些流氓空格,就像我在名为' mess about with argument Position的代码部分中所做的那样 ,以及之后代码行中的一些地方。当您发布到 VB 代码窗口时,此修改也不会更改。这有助于我们理解嵌套 " "
嵌套 ""
在变量参数位中。
这比许多文献所暗示的要容易得多。
您真正需要那些封闭引号对的唯一一次是您在参数中提供字符串值。这通常是 VBA 代码行中的情况,包含在字符串周围的引号表示正在给出一个字符串。(由于您已经在一个字符串中,因此每个引号都需要加倍,正如标准 VBA 语法所要求的那样,使单引号出现在最终字符串中,正如 VBA “看到”的那样)。如果您使用的是变量,而不是硬编码,那么您永远不需要我展示的下一个复杂的语法(前提是您的变量位于模块顶部,在任何子程序之外)。我要说的是,下面这个人们经常看到的复杂的参数语法,在大多数情况下,比需要的更复杂
""" & Arg1 & """ , """ & Arg2 & """
在大多数情况下,上面的复杂形式可以简化为下面的这种形式
Arg1 , Arg2
要使用这种简化形式,变量必须在带有调度Application.OnTime
代码行的宏之外,并且必须在代码模块的顶部,否则稍后将由 VBA 触发的调度宏不会知道从哪里获取变量。(如果变量和调度模块不在同一个模块,那么它们必须声明为Public。最好显式引用它们,比如Module2.Arg1
or Sheet1.Arg1
or ThisWorkbook.Arg1
etc)
因此,只要您使用“模块级”变量,就不需要真正“需要”那种复杂的语法。
但是,如果您使用这种复杂的语法,它将具有将变量中的值放在最终参数字符串中的效果,VBA 将其放入它编写的代码行中,以便稍后运行预定的宏。这将产生这样的效果,如果您使用该语法,并且您的变量是本地的,(也就是说它们在调度宏中),那么您可能会误以为您,(也就是说 VBA 在稍后预定宏),正在使用变量。
事实上,您正在做的是将值硬编码到字符串中,该字符串最终将由 VBA 稍后在预定的宏中使用。我想您可能会说这是在调度宏中使用变量,至少从实际使用角度来看。但我认为,了解实际发生的情况有助于了解有时令人生畏的语法来自何处。关键是在这种情况下,您并没有真正将变量放入参数中。你实际上是在使用调度宏中的变量来硬编码参数
在我的演示宏中,我将使用调度宏变量的方式称为“伪”变量使用。
此外,尼克 P 在他的回答中提出的观点是,在那个非常复杂的参数语法中,每个变量周围有 4 个引号,是为了" "
在字符串值周围给出典型的最终看到的双封闭对。如果示例中的这些变量之一,例如Arg2
,是一个数字,那么即使使用“技巧”使其看起来您在调度宏中使用变量,您也可以取消其中一些引号,特别是最终给出的,如 VBA 所见,封闭" "
对,将其减少为
""" & Arg1 & """ , " & Arg2 & "
这就是尼克 P 所展示的。
_.____________________
检查宏名称和参数的右侧语法。
在所有编码中,我Debug.Print
在每个Application.OnTime
代码行之后都有一个。这显示的是 VBA 稍后在运行预定宏时使用的字符串的实际 RHS 部分。这显示了包含宏名称和参数的部分。这有助于显示我想要表达的主要观点。
例如,我所说的“伪”变量 use 中的字符串如下所示:
!'Modul1.DoubleCheck "465.42" , "25.4" '
或者,如前所述,如果一个变量,例如,第二个是一个数字,那么你也可以使用这个
!'Modul1.DoubleCheck "465.42" , 25.4 '
对于我所说的 Real 变量使用,字符串“seen”必须实际使用变量名
!'Modul1.DoubleCheck Modul1.Pbic_Arg1 , Pbic_Arg2 '
只是为了澄清上面的最后一行代码。正在安排的子例程是Sub DoubleCheck( )
我在代码模块中的代码名称Modul1
同样在同一代码中,模块放置在模块的顶部,变量的声明Pbic_Arg1
和 Pbic_Arg2
. Pbic_Arg1
是私人的,Pbic_Arg2
是公共的
如果您尝试在步骤 (F8) 模式下从 VB 编辑器运行我的编码,同时打开即时窗口,那么我认为这将有助于使一切变得清晰
概括
归根结底,正确理解和理解语法的关键如下:
您必须对其进行安排,以便 VBA“具有”(您可以通过Debug.Print
您提供的字符串进行检查)需要在右侧具有与您在代码行中手动写入参数的方式类似的形式调用一个接受参数的子例程。
您可以在多个参数和分隔角之间添加一些额外的空格,就像在典型的 VBA 调用代码行中手动键入一系列参数时可能会不小心做的那样。据推测,VBA 稍后,当它完全使用您给定的字符串时,它会执行类似于您在物理上写入或粘贴此类内容时发生的事情,其结果是那些额外的空格被删除。
封闭的重点' '
是向 VBA 表明按照您所写的字面意思进行。在我的显式代码行中,LHS 和 RHS 都需要它。更典型的是省略 LHS。
对许多双对或三"
对的复杂组合的任何使用更像是一种技巧,可以让您有效地使用调度宏中的变量,在调度Application.OnTime
代码行中。
如果您的变量位于任何子例程之外的代码模块中,则变量语法会大大简化。在这种情况下,您实际上不需要主字符串中的任何引号,即使 a 变量类型是字符串也是如此。
(与预定宏及其参数相关的完整的第二个参数Application.OnTime
总是需要包含在引号对中。这就是 的Application.OnTime
编写方式。(这非常有用,因为您可以构建带变量的字符串,而不是仅限于硬编码))
艾伦
参考
https://groups.google.com/forum/?hl=es#!msg/microsoft.public.excel.programming/S10tMoosYho/4rf3VBejtU0J
https://www.mrexcel.com/board/threads/calling-a-procedure-with-parameters.81724/#post398494
http://markrowlinson.co.uk/articles.php?id=10
http://www.excelfox.com/forum/showthread.php/2404-Notes-tests-Application-Run-OnTime-Multiple-Variable-Arguments-ByRef-ByVal?p=11870&viewfull=1#post11870
PS@Holger Leichsenring-嗨。我认为撇号必须包含宏名称和参数。任何数字类型都可以不带引号传递。您要调用的宏可以驻留在任何模块、任何工作簿(打开或关闭)中,并且不必是 Public。(我的猜测是Application.OnTime
使用与 相同的接线Application.Run
,与简单的调用 sub 相比,它的优势在于它将同时运行 Public 和 Private 子( 调用 Sub 和 Application.Run 之间的区别 ))
格鲁斯,艾伦