I have a macro that is calling a UDF that subsequently refresh some data on my workbook and then I call some other macro to perform some operation on refreshed data. The problem is the VBA execution continues before the refresh data completes. I want to hold execution until the UDF completes its job. the code is as follows:
Private Sub CommandButton1_Click()
Application.Run ("RefreshSnowflake()")
End Sub
An example of what I've tried:
Private Sub CommandButton1_Click()
Application.Run ("RefreshSnowflake()")
Do
Loop Until Not Application.CalculationState = xlDone
MsgBox "Test."
End Sub
The problem is the messagebox is immidiately shown, meaning does not wait for RefreshSnowflake() UDF call to complete execution.
How can I achieve that?
I don't want to use Wait or Sleep with a certain time amount, I want the execution to be postpone for the exact amount that the UDF RefreshSnowflake() takes to complete its operation.
Thank you!
With the code provided, would you would want to loop until the calculation state is done? (i.e. remove the "NOT")
Loop Until Application.CalculationState = xlDone
Although I think a better way may be:
While Not Application.CalculationState = xlDone Then
DoEvents
Loop
That way, the code just waits rather than infinitely looping on nothing.
Related
DISCLAIMER: I'm not a developer, just an average guy trying to use VBA on his own project. First time I post on stackoverflow so forgive me if I'm violating any unwritten community rule..
Hi all, I'm trying to have a bunch of subs running in sequence, so the next one starts only when the previous one has finished.
The problem is that excel keep on crashing during the execution, while if I run each sub manually they have no issue at all.
So far I have been using this method: First I have a mini sub that assigns value=1 to the global variable "oneclick", and then calls the first macro of the chain
then, in each sub there is a tiny piece just before the end:
sub macro1()
...macro code...
if oneclick=1 then
call macro2
end if
end sub
and so on for all the subs until the final sub resets the oneclick variable to zero.
I have no idea why this keeps on crashing. I can see that actually none of these subs is really closing until the very end of the last one, would this hit any sort of code limitation? I would be actually happier to have a single master sub that directs the process instead of relying on a kludge like this! any suggestion?
EDIT:
oh wow already so many answers.. now I'm gonna try some of those. Answering some of your questions:
1) the crash is really something sudden, not even an error message, simply excel quits and reopens, in the same state as it was when the macro chain first started.
2) I agree with you guys that there should be a master sub calling the smaller ones, however last time I tried I got a series of problems because one macro needs to work on the results of the previous one and so on. How do you tell to vba to wait until the previous sub has ended?
It is best if you don't chain the macros, but instead call them from another sub. This way you will have a clear way of understanding what you are doing, in what order.
Sub AllOfIt()
macro1
DoEvents
macro2
DoEvents
macro3
End Sub
Sub macro1
...
End Sub
Sub macro2
...
End Sub
Sub macro3
...
End Sub
There is generally no need to worry about if something starts before the previous macro ended. That does not happen unless you do something like using Application.OnTime
What probably is happening is that oneclick is not defined with a Dim statement, so by default its defined at procedure level, meaning if you set oneclick it to 0 in another Sub, your setting another local/procedure level variable, and your variable in macro1 is unchanged, so your chain of macros never stops, which leads to a stack overflow/crash as stated in one of the comments above.
So either define oneclick as public variable (I recommend to define it as Boolean as it seems to basically be a switch .... True/False) or you pass the variable as argument down your chain of macros ( Sub Macro2(ByRef oneclick as Boolean) ).
All that said, as one of comments above stated, you can have all your chained subs in the main sub ( macro1 ) as they will only get executed one after the other, e.g.
Sub macro1()
Dim oneclick As Boolean
oneclick = True 'Need to get set to true to start
If oneclick Then Call macro2(oneclick)
If oneclick Then Call macro3(oneclick)
........
End Sub
Sub macro2(ByRef oneclick As Boolean)
oneclick = False 'One of your macros has to set oneclick to false to stop the chain/execution, probably under acondition
End Sub
I have encountered a problem in the macro below
Sub RefreshAction()
Range("b7").Select
Application.Run "RefreshCurrentSelection"
Application.OnTime (Now() + TimeValue("00:00:05")), "thisworkbook.Action"
End Sub
The cell refreshes when I run the macro the first time but I get the error message immediately after
Message: Cannot run the macro "C\Desktop\XYZ.xlsm'!thisworkbook.Action'. The macro may not be available in this workbook or all macros may be disabled.
I have already gone through "Trust Center->Trust Center Settings->Macro Settings->Enable all macros and it didn't work.
The "Trust access to VBA project object model" box is also clicked.
First of all, here is a snapshot of the error you get when you attempt to run OnTime from a worksheet (not a module) as I will explain. I was getting this error too and trying to figure out why.
It looks like a security error, but in this case it isn't exactly a normal security error.
To run code on a timer you have to add it to a VBA module.
Go to the VisualBasic editor and right click the VBAProject (book).
In Excel it looks like the following:
Once the module is added you add your timer code there.
Since you want to call RefreshAction every 5 seconds you would do something like the following:
Sub StartProcess()
Debug.Print Now()
Application.OnTime Now() + TimeValue("00:00:05"), "RefreshAction", Schedule = True
End Sub
Sub RefreshAction()
Application.EnableEvents = True
Debug.Print Now() + TimeValue("00:00:05")
Application.OnTime Now() + TimeValue("00:00:05"), "RefreshAction", Schedule = True
End Sub
I'll let you add the code that you want it to do each time in the RefreshAction subroutine.
Here's what it will look like in the Module. Make sure yours shows that it is in a module as it does in the image:
Also, I found it to be quite flaky. If you have anything even slightly wrong in the OnTime call it will fail silently. Copy my code (I tested it) and try it first. Once it runs, just add your code to the RefreshAction sub.
StartProcess()
Run the StartProcess to start the thing going.
Additionally Odd Thing
After I added that Module, I still had my code in the Worksheet and I went back and attempted to run it to see the error again and the odd thing is that once the code is in the Module you won't get the error from the Worksheet any more. It's probably referencing the code in the Module now.
See the absolute reference for more details : CPearson OnTime
First issue, you need to store the time that you'll input in your OnTime method to be able to stop it. (Here I declared a Public TimeToRun As Date)
Second Point To use the OnTime method continuously, you need to reset the timer at the end of your timed procedure (here RefreshAllStaticData).
So your whole code should look like this :
Public TimeToRun As Date 'so that TimeToRun can be used in both the functions
Sub RefreshAction()
Range("b7").Select
Application.Run "RefreshCurrentSelection"
DoEvents
'Store the next date of execution in TimeToRun
TimeToRun = Now() + TimeValue("00:00:05")
'Launch the next OnTime
Application.OnTime TimeToRun, "RefreshAllStaticData"
End Sub
Sub RefreshAllStaticData()
'--++-- Place your code here, as it is now --++--
'----Call RefreshAction to reset the OnTime method
'---------to another 5 seconds and keep "looping"
RefreshAction
End Sub
Sub Kill_OnTime()
'Launch this to stop the OnTime method
Application.OnTime _
earliesttime:=TimeToRun, _
procedure:="RefreshAllStaticData", _
schedule:=False
End Sub
A different but related cause of this error can be the string-length limitation of the OnTime method's Procedure parameter's argument. See my post at: Getting around the Max String size in a vba function?
I want to monitor a value and get an email notifications when certain conditions are met. I have a macro like so:
Do While True
Worksheet.Calculate
If Value > 10 Then
SendEmail
End If
Sleep 60*CLng(1000)
Loop
However, when I run this, it clogs up the entire program and will turn unresponsive if I try to do anything.
Is there anyway to accomplish this but have it run in the background or at least not crash the program?
What I was doing before was using VBScript to open a not-visible spreadsheet and the VBScript ran continuously in the background monitoring the condition and worked fine, but my client really wants a GUI and for it to be in the program itself.
Any thoughts?
Use the Application.OnTime method to schedule code that will run in one minute.
Your code will look something like this (Untested):
Sub CreateNewSchedule()
Application.OnTime EarliestTime:=DateAdd("n", 1, Now), Procedure:="macro_name", Schedule:=True
End Sub
Sub macro_name()
If Value > 10 Then
SendEmail
Else
CreateNewSchedule
End If
End Sub
You might want to store the time of the next schedule in a global variable so that the Workbook_BeforeClose event can cancel the next schedule. Otherwise Excel will re-open the workbook.
Public nextScheduledTime As Date
Sub CreateNewSchedule()
nextScheduledTime = DateAdd("n", 1, Now)
Application.OnTime EarliestTime:=nextScheduledTime , Procedure:="macro_name", Schedule:=True
End Sub
Sub macro_name()
If Value > 10 Then
SendEmail
Else
CreateNewSchedule
End If
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
On Error Resume Next
Application.OnTime EarliestTime:=nextScheduledTime, Procedure:="macro_name", Schedule:=False
End Sub
You can then continue to use Excel between the scheduled times.
I think you need to specifically process the application event stack with a DoEvents call. This allows user interactions with the spreadsheet to occur, where normally the macro would take precedence. You code would look something like:
Do While True
If Value > 10 Then
SendEmail
End If
Sleep 60*CLng(1000)
DoEvents
Loop
You could also construct a GUI with HTA if you wanted to remain with VBScript.
I've done a fair bit of research on here to try and get this code to run, but I'm still having issues.
I am trying to run a macro at a set time. In fact it is a number of macros as different times. This macro then copies a block of cells, into a different area on the sheet.
I will not be able to use windows scheduler so I'm trying to do it this way. I gather without scheduler I will have to run the macro from a button. Therefore, my first sub is this (note button is on a separate sheet to the data)
Sub Call_save()
Call Sheets("Dashboard").8amsave
Application.OnTime TimeValue("08:10:00"), "Call_8amsave"
Call 9amsave
Application.OnTime TimeValue("09:10:00"), "Call_9amsave"
Call 10amsave
Application.OnTime TimeValue("10:10:00"), "Call_10amsave"
End Sub
This button is designed to then run the subs at the predetermined time. The subs it calls are these:
Sub 8amsave()
Dim current_data As Variant
current_data = Worksheets("Dashboard").Range("S6:V22").Value
Worksheets("Dashboard").Range("B33:E49").Value = current_data
End Sub
Sub 9amsave()
Dim current_data As Variant
current_data = Worksheets("Dashboard").Range("S6:V22").Value
Worksheets("Dashboard").Range("B54:E70").Value = current_data
End Sub
Sub 10amsave()
Dim current_data As Variant
current_data = Worksheets("Dashboard").Range("S6:V22").Value
Worksheets("Dashboard").Range("B75:E91").Value = current_data
End Sub
Now, when I run the initial button it runs all the subs at once and copies the sells into the correct places, straight away. This isnt really an issue for me. However, at the predetermined time I get an error saying unable to run the macro... can't find it or sheet unable to run macros... or something of that ilk.
Is there anything blindingly obvious that i am missing? I have kind of been working this out as I go along so my VBA knowledge is fairly limited. A point in the right direction would be great.
Your OnTime statements also appear to be referencing procedure names which don't exist, i.e., Call_8amSave as opposed to 8amsave. Note also that procedure names cannot begin with numeric (e.g., 8amsave) and this code should not even compile, let alone execute.
Rename your procedures so that they match the calls in the OnTime statement, and are legally permissible procedure names, like: Sub Call_8amSave, etc.
Also, the OnTime property can only have one procedure at a time, so you can't set 3 future times, you have to set them sequentially. So, set the OnTime for the 9am save during the 8am procedure, etc.
Option Explicit
Sub Call_save()
Application.OnTime TimeValue("08:10:00"), "Call_8amsave"
End Sub
Sub Call_8amsave()
With Worksheets("Dashboard")
.Range("B33:E49").Value = .Range("S6:V22").Value
End With
Application.OnTime TimeValue("09:10:00"), "Call_9amsave"
End Sub
Sub Call_9amsave()
With Worksheets("Dashboard")
.Range("B54:E70").Value = .Range("S6:V22").Value
End With
Application.OnTime TimeValue("10:10:00"), "Call_10amsave"
End Sub
Sub Call_10amsave()
With Worksheets("Dashboard")
.Range("B75:E91").Value = .Range("S6:V22").Value
End With
End Sub
I'm needing to run a sub once a second while also receiving data from a feed. The function looks at the data in the cells, determines if conditions are right, and if so sends a command to the trade server (where my data feed is coming in). This issue is once I run the loop, the While Loop freezes my cells in excel, making the program execute without updating.
Dim timeremaining As Integer
timeremaining = Range("E7").Value
Application.Calculation = xlCalculationAutomatic
Do While timeremaining > 5
Endowment
Sleep 1000
Loop
I've tried the setting a timer (OnTime) approach, and creating a function to run it a set number of times, yet both functions freeze my data feed.
Thank you.
Try using the wait method in VBA
For example:
Application.Wait(Now + TimeValue("0:00:10"))