In an Excel spreadsheet/VBA script I'm making, I need to call data from a database, and refresh the values every 5 minutes. The program starts from the push of a button, and should run continuously until the user breaks the execution. I'm currently not sure how to make Excel/VBA 'wait' 5 minutes without pausing the spreadsheet and, ideally, without being computationally inefficient.
I've tried using the "Application.Wait" and "Sleep" functions, but both of those pause the spreadsheet during the 5 minute wait.
My current solution is to use a "While" loop with "DoEvents" inside it, as shown in the code below. This makes the program just run the "While" loop for 5 minutes, and it does not pause the spreadsheet thanks to "DoEvents". However, while the spreadsheet is usable, this is computationally inefficient, since the program execution isn't technically paused, it's just running the "While" loop continuously, and some of the slower computers that may end up using my program might be significantly slowed by this.
My current solution is as follows:
Sub MainProgram()
'dimension variables, open database connection, etc.
Do While 1 < 2 'ad infinitum
'get database data, write to spreadsheet, etc.
WasteTime()
Loop
End Sub
Sub WasteTime()
EndTime = Now + TimeSerial(0,5,0)
While Now < EndTime
DoEvents
Wend
End Sub
The problem with this, as mentioned above, is the computational inefficiency. CPU utilization is fairly high throughout the WasteTime loop. So I'm wondering, is there any way to pause the script without pausing the spreadsheet and without running the code continuously, thus burdening the CPU?
As BigBen mentions in comments Application.OnTime is the best option for this. It avoids the overhead you describe by scheduling a second macro to be called at a future time.
Below is an example. You can modify the wait time with the constant variable. These should be within the same Module (or change theCalculation macro to not be private).
Sub TheTimerMac()
'enter in timevalue (hour:Minute:Second)
Const DelayTime As String = "00:05:00"
Dim nextRunTime As Date
nextRunTime = Now + TimeValue(DelayTime)
'Schedules application to execute below macro at set time.
Application.OnTime nextRunTime, "TheCalculation"
End Sub
Private Sub TheCalculation()
'whatever you use for your calc here
Application.CalculateFull
'This will restart the timer portion.
Call TheTimerMac
End Sub
Related
I have a process where I use VBA to enter a webpage and download a daily file around 6 pm, although this certain file can be uploaded any time later.
Since I need to schedule automatic run for this macro because not always I will be able to run it manually (nor other person), I was thinking if there's some way to stop a macro execution after some time has passed. This is because, after the download, it should do some other things, but also the download takes a while, so if I do
Dir(file)
Do While file = ""
Do events
' CODE TO EXIT LOOP AFTER SOME TIME
Loop
The code can be hours stuck because there is no file available on the webpage, so I would like that loop to break after, let's say, 1 minute. So maybe I was thinking to catch the current time and adding the time I want it to run and then on each loop compare the current time with the time I set to stop, and then break the loop, however, I don't get how to do the time part.
Any help will be appreciated
Maybe Application.OnTime can be helpful?
https://learn.microsoft.com/en-us/office/vba/api/excel.application.ontime
As commented by Ricardo Díaz and according to this thread:
VBA - Exit Function if a timer passes 2 minutes
This is the code that works as a solution:
Dim startTime As Date
startTime = Now
Do
If DateDiff("s", startTime, Now) > 10 Then
MsgBox "There seems to be an issue. Please try again!"
Exit Do
End If
dostuff
Loop
For complex reasons, I want to automate the calling of a Bloomi BQL query in VBA.
I am changing the inputs to a BQL.Query formula in the Excel sheet from a VBA script, and calling Application.Calculate to run the query. The display changes to "N/A Requesting Data ...". In the VBA I wait a bit (using Wait()) and even throw in a DoEvents() for good measure.
While rngOS.Value < 0
Application.Calculate
Sleep 2000 'Waits 2000 ms
DoEvents
Wend
Trouble is, the BQL.Query just doesnt return. Only when I end the VBA script, does the actual data come back into the worksheet.
Ideally I would have a synchronous way to just call BQL.Query from VBA and wait for the return.
All suggestions welcome!
Here is a pointer: https://stackoverflow.com/a/33667663/829571
In substance: schedule your function to run a bit later, check if BQL is done (for example by counting the number of "N/A Requesting Data") and if not done, schedule the function a bit later again, etc. Once the count of N/As is down to 0, you know the update is finished and you can proceed with the rest of your code.
I'm trying to create a macro to save an ms-project file in a specific format to a given location at a set interval while the application is open. I've got it mostly working, except the executing on an interval part.
I've had a bit of a google around and when using excel or word you can use the Application.OnTime method & a time offset to call a sub at the required interval. Unfortunately it doesn't look like this method exists in MS-Project.
Is there an alternative method I can use here or should I abandon this idea?
I realize this is a late answer, but I have a solution I used for a similar problem. Just jump on the Project_Change event since it runs frequently, and compare against a variable.
Public lastSave As Date
Private Sub Project_Change(ByVal pj As Project)
If DateDiff("n", lastSave, Now()) > 1 Then ' Replace 1 with # of minutes between saves.
Application.FileSave
lastSave = Now()
End If
End Sub
I'm calling a third party remote call to get data.
after calling this function,datas will be filled from cells(1,1) to cells(10,1)
my code is like this
application.caculation = xlmanual
cells(1,1) = "some remotefunction"
application.calculate
while cells(10,1)<>"result"
wend
'if it goes here means we get the data
deal_with_data
however,my code would hang in the while loop.
so how to deal with this issue?
Your current approach of spinning in a while loop is not a good idea because it consumes all available resources to do...nothing. Or rather, it executes the comparison cells(10,1)<>"result" over and over as quickly as it can. And the user interface hangs in the meantime.
In short, you're finding that VBA does not lend itself well to asynchronous or multithreaded programming.
Thankfully Excel does give you a built in way to accomplish this, with a little effort: The Application.OnTime method. It lets you schedule a delayed call to method, keeping the UI (and any other VBA code) responsive and available in the meantime. You can use it to implement a sort of timer.
Here's the basic approach for your case. It checks the value of your test cell every 1 second. If the value has not changed, it schedules itself to check again in another second. If the value is "result" it calls another sub, where you would put the code to handle the results.
Public Sub CallRemoteFunc()
Application.Calculation = xlManual
Cells(1, 1) = "some remotefunction"
Application.Calculate
Call WaitForUpdate
End Sub
Public Sub WaitForUpdate()
If Cells(10, 1) <> "result" Then
Debug.Print "Still waiting"
Application.OnTime Now + TimeValue("00:00:01"), WaitForUpdate
Else
Call DoStuffWithData
End If
End Sub
While the vba code is running i do not expect any other cells to change unless you are doing something in the loop.
I would either use an if function and call this macro when you want to check the last cell. I can elaborate if you need.
Or, utilise the DoEvents vba function / method in the loop if you envisage the data changing while the loop is running. This function will enable, i believe, the application to still run and update the cells while this macro is running.
After some testing: I now suspect the DoEvents function will work the best for you.
I am trying to run a certain macro in very short intervals, such as every second, from a certain point in time until a certain end point in time. This means I need a starting point and a cutoff point. I cannot use the Workbook_Open() Event, since I already have other macros triggering at different times after the opening of the Workbook.
The basic line I use to trigger the Macro once a second is this psuedocode:
Application.OnTime Now + TimeValue("00:00:01"), "Path to Macro"
From my experiments so far, any attempt I made ended up with two results. In the first case, it ran from the moment I opened the Workbook and with the appropriate schedule of once per second. However, the first case was suboptimal, as I needed it to wait a bit before it started up. In the second case, it ran at the time I wanted it to start - but it ran only once, which was also not what I wanted to happen.
To summarize:
I need something like the code line to start running 15 minutes after the Workbook is opened and stop 3 hours later.
What other timed macros are started from workbook_open, and why are these interfering? It sounds like you're limiting yourself unnecessarily. Here's how to address the issue:
Workbook_open should use application.ontime to call a general function do_timed_events. The do_timed_events function should re-add itself using application.ontime each time it is run. It should also keep track of state. For its first few runs, it should perform the other specific tasks, then wait 15m, then start performing the every-second task.
Here's some pseudocode:
private var do_timed_events_state as string
sub do_timed_events
if do_timed_events_state = "" then
do_task_1(arg1,arg2)
do_timed_events_state = "task_2"
Application.OnTime Now + TimeValue("00:00:01"), "do_timed_events"
elseif do_timed_events_state = "task_2" then
do_timed_events_state = "repeating_task"
Application.OnTime Now + TimeValue("00:00:01"), "do_timed_events"
elseif do_timed_events_state = "repeating_task" then
Application.OnTime Now + TimeValue("00:00:01"), "do_timed_events"
end if
end sub
You can probably come up with a better design than me on this one.