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
Related
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.
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
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 have an Access (2013) database that I use to store/process all of our data received from a Qualtrics online survey. The raw data downloaded from Qualtrics is in a csv file that's poorly formatted for importing to Access, so I've got a fairly complex Macro in Excel (2013) that I use to pre-process the data before importing to Access.
In Access, I use the following code to open the excel file that contains the macro, run the macro, save the workbook, and close it. This had been working well several for several months, but now when I run it, it stops near the end of the Excel Macro with the run time error: -2147417856 Automation error System call failed.
ActivateOrOpenWorkbook WbkName & ".xlsm", strWbkPath
appExcel.Run ProcName, ProcArg
appExcel.Workbooks(WbkName).Save
If appExcel.Workbooks.Count = 1 Then
appExcel.Quit
Else
appExcel.Workbooks(WbkName).Close True
End If
ActivateOrOpenWorkbook is just a custom function to do exactly what the name implies, appExcel is the Excel Application. The workbook always opens fine, and the macro begins to run, but it never actually reaches the point where control returns to the Access VBA and saves the workbook.
It does run fine if I open the Workbook before running the Access procedure, insert breakpoints at every major VBA step (in both Access and Excel), and step through the whole thing one Sub at a time. It just fails if I try to let VBA run it all from start to finish on it's own.
Based on that evidence plus stories of similar problems that I've seen online, I suspect that the error is occurring because the Excel macro is taking too long to run (we recently added some new variables to the Qualtrics survey), and Access is trying to take back control before Excel is finished. I just haven't found any viable way to solve that suspected problem or investigate it further.
I did try inserting this makeshift Wait routine into my ErrorHandling for the Access Sub, but it didn't work at all, because the error message still popped up in the same amount of time as before.
If Err = -2147417856 Then
TWait = Time
TWait = DateAdd("s", 15, TWait)
Do Until TNow >= TWait
TNow = Time
Loop
Resume Next
Any help will be appreciated!
Have you tried using DoEvents in the pre-processing macro?
If you have a loop where you're processing the CSV file line by line then use DoEvents every so often to give any pending events a chance to run. It would be overkill to call it on every line so maybe start with every 100 lines and adjust from there.
Given that the macro works when you step through the Subs, it would appear that it isn't the overall execution time that's the problem. If Access is throwing an error because Excel appears to be unresponsive then DoEvents might be enough to stop Access from giving up
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.