Multiple Variable Arguments to Application.OnTime - vba
I am working on a data acquisition frontend for Excel 2010.
I can't figure out the syntax for passing multiple local variable arguments to Application.OnTime.
http://markrowlinson.co.uk/articles.php?id=10 provides a good example for a single variable as an argument, but the explanation on how to extrapolate this to multiple variables is not clear to me.
Does anyone have a concise explanation of the correct nesting of " and ' characters when passing multiple local variable arguments?
Edit: Code example would be like this: Application.OnTime Now + TimeSerial(0, 0, 5), "'runScheduledReport """ & iArg1 & "","" & iArg2 & "" "" & iArg3 & "" ""'".
I understand that we're using the double quote as an escape character within the string, but can't really figure out the ordering of the strings being passed.
you have to consider the following constraints:
the macro you want to call has to reside in a module. When you want to call it from another workbook it has to be public.
you cannot use brackets for calling the macro like you would do with a function or a sub with parameters most probably. When using brackets Excel will complain that macro doesn't exist
I didn't try a function, but anyway there is nobody who can work with the return value, so define your method as a sub.
you have to use aposthophs to encapsulate the macro name
you have to use quotes to encapsulate string and date values , either with chr$(34) (reminds me to old times) or just double the quotes
you can pass over integers without quotes, I didn't try Doubles
separate arguments by a comma
the order of the arguments must match the order of the arguments in your method
Find attached the code:
Option Explicit
Sub Test()
Dim strTest1 As String
Dim strTest2 As String
strTest1 = "This is test1"
strTest2 = "This is test2"
Application.OnTime Now + TimeSerial(0, 0, 1), "'CallMeOnTime """ & strTest1 & """,""" & strTest2 & "'"
Application.OnTime Now + TimeSerial(0, 0, 1), "'CallMeOnTime " & Chr$(34) & "Test" & Chr$(34) & "," & Chr$(34) & "Test" & Chr$(34) & "'"
Application.OnTime Now + TimeSerial(0, 0, 1), "'CallMeOnTime2'"
End Sub
Public Sub CallMeOnTime(strTest1 As String, strTest2 As String)
MsgBox ("test1: " & strTest1 & " test2:" & strTest2)
End Sub
Public Sub CallMeOnTime2()
MsgBox ("CallMeOnTime2")
End Sub
Just wanted to add an additional example which I found helpful, with reference to this post on MrExcel.
Application.OnTime with multiple arguments of different types (String and Integer)
Dim testName As String
Dim counter As Integer
...
' String then Integer argument
Application.OnTime Now + TimeValue("00:00:02"), "'TestSub """ & testName & """, " & counter & " '"
' Integer then String argument
Application.OnTime Now + TimeValue("00:00:02"), "'SubTest " & counter & ", """ & testName & """ '"
The only difference from this answer is the inclusion of the counter Integer, which is handled slightly differently from a String.
Huhlo,
I have fought with the tricky syntax for arguments to Application.OnTime ( or Application.Run, which is similar) every time I have needed it. I have often come here, as well as arrived a few times at the other links referenced here. As often they almost, but did not quite, got me there.
So I have spent some time making myself some worked examples to reference in the future, and I have also convinced myself finally that I understand what is going on.
So I am sharing my solutions , and finally I think I can have a stab at answering the original question regarding ..concisely explaining / justifying the syntax.. ..
I am deliberately giving very full explicit code lines for two reasons:-
_ 1. Its easy to simplify it to the more usual shortened version if you only need that, but going the other way , from the more common simplified form to the full explicit form, should you need that, is quite hard.
_ 2.Showing the full explicit code line syntax helps with my attempt at explain the syntax, and so is needed in answering the question fully.
The full explicit syntax would be needed , for example , to ensure the corrects file were opened, when we want to trigger a macro in a closed workbook. ( In such a case, the closed workbook would be opened. The VBA Application.OnTime code line will do this opening, provided it has the full explicit form )
I am using 2 example files. If you want to try my demos, then the first should be opened , the second can be closed or open , but the second should be in the same folder. ( The reason why it needs to be in the same folder is just for simplified demonstration, - I have organised that demonstration macros will look for the closed workbook in the same folder. In the practice, the closed workbook can be anywhere if you replace exactly this bit , ( including the first " ) , with the full path and file name of the closed workbook
" & ThisWorkbook.Path & "\" & "UverFile.xls
So you would replace that last bit with something of the form:
C:\Elston\Desktop\MyFolder\UverFile.xls
A complete code line would then have a form something like this:
Application.OnTime Now(), "'C:\Elston\Desktop\MyFolder\UverFile.xls" & "'" & "!'Modul1.MacroUndermacroInUverFile 465, ""25""'"
.
Open workbook - MainFile.xls : https://app.box.com/s/prqhroiqcb0qccewz5si0h5kslsw5i5h
Module “Modul1” in MainFile.xls
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
(That above is the main module from which all macros are run)
Worksheets Class module of first worksheet “Tabelle1” in MainFile.xls
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
.
.
.
Closed workbook - UverFile.xls : https://app.box.com/s/u7r2jw79m8ou70otn7xcxced2qkot4w4
Module “Modul1” in UverFile.xls
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
Worksheets Class module of first worksheet “Tabelle1” in UverFile.xls
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
.
.
I have tried to give a good spread of working examples, which I found useful to then use as a template to modify to exactly my needs.
Here is the explanation to how things work , which makes the syntax more understandable:
First the nested '
This is generally how VBA handles making any spaces be taken as literal spaces, ( rather than , for example, mistaking them as separating arguments). You will see that in the codes, as I have posted here in the forum post, I have done some exaggerated spaces in all code lines which helps to split up the two main parts of the code
the LHS , which in a simplified / shortened use would be typically be left out
and
the RHS , most of which is always needed . (Most likely xou may only see the macro name and the arguments . The extra module code name allows you to use macros in any modules , ( regardless of if they are Private or Public )
Just to make that clear, I have some exagerated spaces in the code windows above either side of one of the &s , so pseudo I have
"---------LHS-------------" & "---------RHS------------------"
or like, pseudo
"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"
Those exaggerated extra spaces either side of that & will vanish if you copy and paste that code into the VB editor code window. If, however, I were to add spaces within the path string on the LHS, such as changing a file name from UverFile.xls to Uver File.xls , then as perhaps expected, the spaces will not change when posting into the VB Editor code window. This is because the enclosing ' ' is doing its job of ensuring that all is taken as literally as it is given.
On the RHS we need also that the information is taken exactly as we give it. My geuss is that, this string part needs to be stored into a buffer from when it is then retrieved and pseudo physically put in later by VBA when the scheduled macro is run. This is why I can add some rogue spaces, as I have done in the code section named ' mess about with argument positions , and in a few places in code lines after. This modification is also not changed when you post into the VB Code window. This helps us to understand the nested " "
the nested "" in the variable arguments bit.
This is much less difficult then a lot of literature suggests.
The only time you really need those enclosing quote pair is if you are giving string values in the argument. That is generally the case in VBA code lines, the enclosing quote aroiund a string indicating that a string is being given. ( Since you are already inside a string, then each of the quotes need to be doubled, as is the standard VBA syntax required to get a single quote to appear in the final string as is "seen" by VBA).
If you are using variables, rather than hard coding, then you never need this next complicated syntax which I am showing, ( provided you have your variables at the top of a module, outside any subroutine ). What I am saying is, that the following complicated argument syntax , which one often sees, is , in most cases, more complicated than needed
""" & Arg1 & """ , """ & Arg2 & """
In most cases, that complicated form above can be reduced to this sort of form below
Arg1 , Arg2
To use that simplified form, the variables must be outside the macro with the scheduling Application.OnTime code line, and it must be at the top of the code module, or else, the scheduled macro which is to be set off by VBA later , won’t know where to get the variables from. ( If the variables are not in the same module as the scheduling module, then they must be declared as Public. It is best to referrence them explicitly, like Module2.Arg1 or Sheet1.Arg1 or ThisWorkbook.Arg1 etc )
So do not really “need” that complicated syntax, provided you use “module level” variables.
But if you use that complicated syntax, it will have the effect of placing the value from the variable in the final argument string that VBA puts into the code line it write to run the scheduled macro later. This would have the effect of that if you use that syntax, and your variables are local, ( that is to say they are within the scheduling macro ) , then you might be fooled into thinking that you , ( that is to say VBA in the scheduled macro later ), are using the variables.
In fact what you are doing is that you are hard coding with values into the string that will finally be used by VBA later in the scheduled macro. I suppose you might say that is using variables within the scheduling macro, at least from the practical point of use. But understanding what is actually going on, helps , I think, to see where the sometime daunting syntax comes from. The point is that in such a case you are not really putting variables in the argument. What you are actually dong is using variables within the scheduling macro to hard code the arguments
In my demo macros, I refer to that way of using the scheduling macro variables as "Pseudo" variables use.
Further more, the point that Nick P was making in his answer, is that 4 of those quotes around each variable in that very complicated argument syntax, are there to give the typical required finally seen double enclosing " " pair around a string value. If one of those variables in the example, for example Arg2 , is a number, then even for the case of using the “trick” to make it appear that you are using variable within the scheduling macro, you can do away with some of those quotes, in particular the ones giving finally as seen by VBA, the enclosing " " pair, reducing it to
""" & Arg1 & """ , " & Arg2 & "
That is what Nick P was demonstrating.
_.____________________
Examining the right hand side syntax for macro name and arguments.
In all the coding I have a Debug.Print after each Application.OnTime code line. What this is showing is the actual RHS part of the string that VBA uses later when running the scheduled macro. So that is showing the part containing the macro name and the arguments. This helps to show the main point I am trying to get across.
For example, the string in what I refer to as the "Pseudo" variables use , looks like this:
!'Modul1.DoubleCheck "465.42" , "25.4" '
Or, as noted, if a variable, for example, the second is a number , then you can also use this
!'Modul1.DoubleCheck "465.42" , 25.4 '
For what I call the Real variable use, the string “seen” must actually use the variable names
!'Modul1.DoubleCheck Modul1.Pbic_Arg1 , Pbic_Arg2 '
Just to clarify that Last code line above. The sub routine being scheduled is Sub DoubleCheck( ) which I have located in my code module with the code name Modul1
Also in that same code module are placed at the top of the module , declarations for the variable, Pbic_Arg1 and Pbic_Arg2 . Pbic_Arg1 is Private, and Pbic_Arg2 is Pubic
If you try my coding out running from the VB Editor in step ( F8 ) mode , whilst you have the Immediate Window open , then I think that will help make everything clear
Summary
At the end of the day, the key to getting the syntax correct , and to understanding it , is as follows:
You must arrange it such that what VBA “has”, ( which you can check via a Debug.Print of the string you are giving ) needs to have on the right hand side a similar form to how you might manually write in arguments in a code line to call a sub routine taking in arguments.
You can add a few extra spaces between multiple arguments and the separating comer , just as you might do carelessly when typing in manually a series of arguments in a typical VBA Call code line. Presumably, VBA later, when it uses exactly your given string, it does something similar to what happens when you physically write or paste such things in, the result of which is that those extra spaces get removed.
The point of the enclosing ' ' is to indicate to VBA to take literally exactly as you have written it. In my explicit code lines we need that for both the LHS and the RHS. More typically the LHS is ommited.
Any use of a complicated combination of many double or triple " pairs is more of a trick to give you a way to effectively use variables that are within the scheduling macro , in the scheduling Application.OnTime code line.
If your variables are in a code module outside of any sub routine, then the variable syntax is much simplified. In this case you do not actually need any quotes within the main string, not even if the a variable type is string.
( The complete second argument of the Application.OnTime , which relates to the scheduled macro and its arguments , always needs to be enclosed in a quote pair. That is simply how the Application.OnTime has been written. ( That is very useful, since you can then build up the string with variables , rather than being restricted to hard coding ) )
Alan
Ref
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
P.S.
# Holger Leichsenring - Hi . I think the apostrophes must enclose the macro name AND the arguments. Any number types can be passed without quotes. The macro you want to call can reside in any module, in any workbook, (open or closed) , and need not be Public. ( My geuss is that Application.OnTime uses the same wiring as Application.Run , which has the advantage, over simple Calling a sub , that it will run both Public and Private subs ( Difference between Calling a Sub and Application.Run ) )
Gruß, Alan
Related
Is there a way to Get Last Directory so I can Save As into?
I am able to create a new directory on my desktop, my issues is that I don't know how to save multiple files into that folder, within the same Sub, since it has a dynamic name. Option Explicit Sub Make_Folder_On_Desktop() Dim selectionsheet As Worksheet Dim Group As Variant Dim amount As Long Dim BU As Long Dim BUname As Variant Dim sFilename As Variant Set selectionsheet = Sheets("Project Selection") Group = selectionsheet.Range("A19").Value amount = selectionsheet.Range("B19").Value BU = selectionsheet.Range("B6").Value BUname = selectionsheet.Range("C6").Value sFilename = BU & " - " & BUname MkDir Group & " - " & amount & " - " & Format(Date, "mm-dd-yyyy") & " - " & Format(Time, "hhmmss") ActiveWorkbook.SaveAs ThisWorkbook.Path & "\" & sFilename End Sub Last line is where I'm having the issue. I have "ThisWorkbook.Path" but can't figure out how to get it into the new folder I just created.
MkDir Group & " - " & amount & " - " & Format(Date, "mm-dd-yyyy") & " - " & Format(Time, "hhmmss") It's hard to know what the folder name is that you just created, because that instruction is responsible for too many things. Split it up. Build/concatenate a folder name Make a directory by that name If we split up the work, things get much simpler: Dim path As String path = Group & " - " & amount & " - " & Format(Date, "mm-dd-yyyy") & " - " & Format(Time, "hhmmss") MkDir path And now we have the path in the ...path variable, readily usable for anything you might want to do with it: ActiveWorkbook.SaveAs path & "\" & sFilename As a side note, if you make the date format yyyy-mm-dd instead, you're ISO-compliant (i.e. the date is unambiguous everywhere in the world), and the folders become sortable by name. Note that the procedure's name is misleading: it doesn't care where the folder is, and there's nothing that says it's under %USERPROFILE%\Desktop. Use Environ$("USERPROFILE") to retrieve the base path for the current user's profile directory.
Access abends after DoCmd.OpenReport
A report is called from VBA to receive returned records from an Access pass-through query. After the DoCmd completes the report's parameters are set in the report's appropriate label containers setting their .Caption property as required. Access fails intermittently during this process which leads me to believe that the report is not truly open to receive the parameters. Here's the VBA sub: Private Sub Report_Open(Cancel As Integer) Dim strFromDate As String Dim strToDate As String Dim strWC As String Dim intShift As Integer Dim strSQL As String strFromDate = InputBox("Enter From Date and Time: ") strToDate = InputBox("Enter To Date and Time: ") strWC = InputBox("Enter Work Center: ") intShift = InputBox("Enter Shift: ") strSQL = "exec dbo.uspWorkCentreReport_TEST " & "'" & strFromDate & "', " & "'" & strToDate & "', " & "'" & strWC & "', " & intShift & ";" CurrentDb.QueryDefs("ptq_uspWorkCentreReport").SQL = strSQL DoCmd.OpenReport "rpt_qry_ptq_uspWorkCentreReport", acViewReport Me.lblFromDate.Caption = strFromDate Me.lblToDate.Caption = strToDate Me.lblWC.Caption = strWC Me.lblShift.Caption = intShift End Sub When the failure occurrs VBA highlights the Me.lblFromDate.Caption = strFromDate. If I press Reset in VBA or End on the Run-time error '2467': dialog, Access abends without any other outward signs. Access then re-opens to save the copied *_Backupx.accdb and opens with a fresh copy of the .accdb. The error seems to be a standars MS error: As I said the report is intermittent and when it fails VB always highlights the same line in code. How do I capture what is happening or can I make VB wait a half of full second before it tries to write the parameters?
As I remember, captions can not be modified, when report open. Only in design mode. So this is not correct, because you have already opened report Me.lblFromDate.Caption = strFromDate You should use text boxes instead of captions. Also you can clear the borders, fillings and so on, that text box will appear like a caption.
Finally the correct set of code was produced. The button click creates strOpenArgs and passes it with .OpenReport. The report opens and splits the OpenArgs and populates the appropriate labels with updated Captions. Text boxes would not work! Here's the button click event: Private Sub btnPreviewP1_Click() If (Me.txtToDateP1 < Me.txtFromDateP1) Then MsgBox ("The From Date must occurr before the To Date!") End If Dim strFromDateHMS As String Dim strToDateHMS As String Dim strSQLP1 As String Dim strOpenArgs As String strFromDateHMS = Format(Me.txtFromDateP1, "yyyy-mm-dd") & " " & Me.cboFromHourP1 & ":" & Me.cboFromMinuteP1 & ":" & Me.cboFromSecondP1 strToDateHMS = Format(Me.txtToDateP1, "yyyy-mm-dd") & " " & Me.cboToHourP1 & ":" & Me.cboToMinuteP1 & ":" & Me.cboToSecondP1 strSQLP1 = "exec dbo.uspWorkCentreReport '" & strFromDateHMS & "','" & strToDateHMS & "','" & strWCP1 & "'," & strShiftP1 strOpenArgs = Me.RecordSource & "|" & strFromDateHMS & "|" & strToDateHMS & "|" & strWCP1 & "|" & strShiftP1 ' This line is all that's needed to modify the PT query CurrentDb.QueryDefs("ptq_uspWorkCentreReport").SQL = strSQLP1 DoCmd.OpenReport "rpt_ptq_uspWorkCentreReport", acViewReport, , , , strOpenArgs End Sub And here's the reports _Open: Private Sub Report_Open(Cancel As Integer) Dim SplitOpenArgs() As String SplitOpenArgs = Split(Me.OpenArgs, "|") Me.lblFromDate.Caption = SplitOpenArgs(1) Me.lblToDate.Caption = SplitOpenArgs(2) Me.lblWC.Caption = SplitOpenArgs(3) Me.lblShift.Caption = SplitOpenArgs(4) End Sub This opens the report every time with new appropriate data, so long as the report is closed before the form's button is pressed again for another refresh of the report. If the report is not closed the report stays up with the original data and does not refresh with new data... But that is another question I am about to ask. Thanks All.
Excel VBA - insert formula in a set of rows with variable reference directly or replacing a string for example "\=" with "="
I have the goal to write a formula in a set of rows. Some references in the formula have to change each row. I implemented the following script: Dim i As Integer Dim formcolM As String Dim temprng As String For i = 0 To 100 formcolM = "NUMBERVALUE(IF(Q" & i & "=""Bedarf kum."";A" & i & ";IF(Q" & i & "=""Ist"";OFFSET(A" & i & ";-1;0);IF(Q" & i & "=""Lz."";OFFSET(A" & i & ";-2;0);IF(Q" & i & "=""Ist+Lz.-Bedarf"";OFFSET(A" & i & ";-3;0);)))))" Let temprng = "M" & i Range(temprng).Select ActiveCell.Value = "\=" & formcolM next i With this script I am writing a string each row in my excel table at column M. I noticed that if the formula hasn't the symbol "\" , you can find an error . In order to avoid the error I thought to leave the symbol "\" and to use a trick deleting it after (because I don't know how to solve with R1C1 formula. I read some answers on Stackoverflow, but unfortunately I did not understand ) The replacing script after the for cycle: Columns("M:M").Replace What:="\=", Replacement:="=", LookAt:=xlPart The strange thing is that the macro doesn't delete it. Infact when the script finishes , it seems that nothing happened, without errors. But if I want substitute "\=" with another symbol, for example "*", the replacing script works. I did not understand if the problem is : the replace method did not recognized the symbol "=" to search I cannot use the replace method because the symbol "=" disturbs in some way , I don't know in what. OR, is there another simplest way to get this task done? Someone could help me in order to fix? I should have the formula working in the column M , automatically with vba (not with another formula in the excel sheet) . Thanks in advance for your time.
We can apply the formula directly. The issue is that vba is very US-EN Centric and all formula when using the .Formula needs to be in that format. Also since your formula refers to values in a row 3 above the one in which it is put we need to start the loop at 4 not 0. There is no row 0 There are two ways, in US-En format with English functions and , as the deliminator using .Formula: Dim i As Integer For i = 4 To 100 Range("M" & i).Formula = "=NUMBERVALUE(IF(Q" & i & "=""Bedarf kum."",A" & i & ",IF(Q" & i & "=""Ist"",OFFSET(A" & i & ",-1,0),IF(Q" & i & "=""Lz."",OFFSET(A" & i & ",-2,0),IF(Q" & i & "=""Ist+Lz.-Bedarf"",OFFSET(A" & i & ",-3,0),)))))" Next i Or using .FormulaLocal and the formula as you would write it in your native tongue. Dim i As Integer For i = 4 To 100 Range("M" & i).FormulaLocal = "=NUMERO.VALORE(SE(Q" & i & "=""Bedarf kum."";A" & i & ";SE(Q" & i & "=""Ist"";SCARTO(A" & i & ";-1;0);SE(Q" & i & "=""Lz."";SCARTO(A" & i & ";-2;0);SE(Q" & i & "=""Ist+Lz.-Bedarf"";SCARTO(A" & i & ";-3;0);)))))" Next i
By the time I got this worked out, Scott already had an answer. I just wanted to post your original code modified to work. I would suggest his method. Sub TestScript() Dim i As Integer Dim formcolM As String Dim temprng As String For i = 4 To 100 formcolM = "NUMBERVALUE(IF(Q" & i & "=" & "Bedarf kum." & ";A" & i & ";IF(Q" & i & "=" & "Ist" & ";OFFSET(A" & i & ";-1;0);IF(Q" & i & "=" & "Lz." & ";OFFSET(A" & i & ";-2;0);IF(Q" & i & "=" & "Ist+Lz.-Bedarf" & ";OFFSET(A" & i & ";-3;0);)))))" temprng = "M" & i Sheets("Sheet1").Range(temprng).Select ActiveCell.Value = " = " & formcolM Next i End Sub
AutoCAD VBA: Selecting objects
I have a 3D model of an intricate chimney which is essentially a cylindrical tube with decorative features. I'd like to write a VBA script which find the section properties at several points along its length but I'm not really sure how to do it. From online searches, I've managed to write a code which puts in a section at a point which I can then run MASSPROP on but I'm not quite sure how to finish it off... I think I'm only one line of code away. I just need to select the section that I've just created. My almost complete code is below with a comment on the line that I need help with. Public Sub Section() Dim SolidObject As Acad3DSolid Dim NewRegionObject As AcadRegion Dim PlaneOrigin As Variant Dim PlaneXaxisPoint As Variant Dim PlaneYaxisPoint As Variant Dim PickedPoint As Variant On Error Resume Next With ThisDrawing.Utility .GetEntity SolidObject, PickedPoint, vbCr & "Select solid to cut." If Err Then MsgBox "Selected solid must be a 3DSolid" Exit Sub End If PlaneOrigin = .GetPoint(PickedPoint, vbCr & "Select point to define origin.") PlaneXaxisPoint = .GetPoint(PickedPoint, vbCr & "Select point to define x-axis.") PlaneYaxisPoint = .GetPoint(PickedPoint, vbCr & "Select point to define y-axis.") Set NewRegionObject = SolidObject.SectionSolid(PlaneOrigin, PlaneXaxisPoint, PlaneYaxisPoint) End With ThisDrawing.SendCommand ("qaflags" & vbCr & "2" & vbCr) 'This is needed for the operation ThisDrawing.SendCommand ("massprop" & vbCr) 'How do I select my NewRegionObject??? ThisDrawing.SendCommand (vbCr & vbCr & "y" & vbCr & vbCr & "y" & vbCr) End Sub If I can get this code to run MASSPROP with my newly created section fine I should be able to adapt it to do the process automatically at several points along the chimney so I think I'm only one line of code off. Thanks for your help, Tom
you'd better exploit Autocad Object Model: Dim minPoint As Variant, maxPoint As Variant Set NewRegionObject = SolidObject.SectionSolid(PlaneOrigin, PlaneXaxisPoint, PlaneYaxisPoint) With NewRegionObject MsgBox "Area: " & .Area MsgBox "Perimeter: " & .Perimeter .GetBoundingBox minPoint, maxPoint MsgBox "Min Point coordinates: (" & minPoint(0) & "," & minPoint(1) & "," & minPoint(2) & ")" MsgBox "Max Point coordinates: (" & maxPoint(0) & "," & maxPoint(1) & "," & maxPoint(2) & ")" MsgBox "Centroid coordinates: (" & .Centroid(0) & "," & .Centroid(1) & ")" MsgBox "Moments of Inertia: (" & .MomentOfInertia(0) & "," & .MomentOfInertia(1) & "," & .MomentOfInertia(2) & ")" '.. and so on End With
VBA execute code in string
I am trying to execute vba code that is inside a string WITHOUT writting the code in a temp file. For exemple : Dim code As String code = "n = 0 : e_i_e = 0 : For e_i_e = 0 To 100 : n+=1 : Next" I have tried Eval, Evaluate, Run, executeGlobal and adding a new module with Set VBComp = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule) VBComp.Name = "NewModule" Set VBCodeMod = ThisWorkbook.VBProject.VBComponents("NewModule").CodeModule With VBCodeMod LineNum = .CountOfLines + 1 .InsertLines LineNum, _ "Sub MyNewProcedure()" & Chr(13) & _ code & Chr(13) & _ "End Sub" End With Application.Run "MyNewProcedure" but all of these are returning errors ='(. Thank you !
You cannot break in code that's been generated after you've compiled your project, so you need to make sure you build that dynamic module with valid, compilable code. You know before you hit that F5 button that your code is going to look like this: Sub MyNewProcedure() n = 0 : e_i_e = 0 : For e_i_e = 0 To 100 : n+=1 : Next End Sub Why not just take that snippet and paste it somewhere and see what the VBE complains about? Wow. See, this is why cramming half a dozen instructions on the same line of code is a bad idea - if it was one instruction per line you wouldn't be wondering which one is broken. As was already mentioned, n += 1 is not VBA syntax (it's not specifically C# syntax either); incrementing a value in VBA needs to access the current value, so n = n + 1. It's not clear where n and e_i_e are coming from. If both are locals, then your procedure accomplishes essentially nothing. If n is declared outside MyNewProcedure, then you should consider passing it as a ByRef parameter, or better, leaving it out completely and making a Function with the result of which the calling code assigns n to. Sub MyNewProcedure(ByRef n As Long) Dim i As Long For i = 0 To 100 n = i Next End Sub Which boils down to: Function MyNewFunction() As Long MyNewFunction = 100 End Function Which makes me wonder what the heck you're trying to accomplish. If there is a bug in your generated code, you're going to have to debug it in a string, because the VBE's debugger won't let you break on generated code - this means it's crucially important that you generate code in a readable and maintainable way. There's currently nowhere in your code where you have the actual full generated code in a clear string - it's concatenated inline inside the InsertLines call. Consider: Dim code As String code = "'Option Explicit" & vbNewLine & _ "Public Sub MyNewProcedure()" & vbNewLine & _ " n = 0" & vbNewLine & _ " e_i_e = 0" & vbNewLine & _ " For e_i_e = 0 To 100" & vbNewLine & _ " n = n + 1 ' fixed from n += 1" & vbNewLine & _ " Next" & vbNewLine & _ "End Sub" & vbNewLine '... 'Debug.Print code .InsertLines LineNum, code It's much easier to get the full code back while debugging, and much easier to review and fix as well. Note that there's a limit to how many line continuations you can chain though.
Your code is c# addition, it needs to be n=n+1
You can create a module and populate it with a sub from a string. In fact one of the way developers place their vba code into a repository is to do just that: extract the code from modules as strings and then read them back in from whatever version control software they're using. Here's a full example to do what you're looking to do (assuming you want your sub in a new separate module): Sub make_module_sub_and_run(): Dim strMacro As String Dim myModule As VBComponent Set myModule = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule) strMacro = "Public Sub exampleSub()" & vbCrLf & _ " n=0" & vbCrLf & _ " For e_i_e = 0 to 100:" & vbCrLf & _ " n=n+1" & vbCrLf & _ " Next" & vbCrLf & _ " MsgBox(""Hello World. Oh and n="" & n)" & vbCrLf & _ "End Sub" myModule.CodeModule.AddFromString strMacro Application.Run myModule.Name & ".exampleSub" End Sub Note that what should happen as you type "vbext_ct_StdModule" is that excel intellisense will note that this is missing and will ask whether you want to load it in - which you, of course, do. Also note that I've deliberately prefixed the sub with the module name when running it - otherwise if you were to run it repeatedly you'll create new modules with a sub of the same name and excel wouldn't know which one to run.