Run macro automatically via vbscript from timer - vba

I have a macro that downloads some information from the internet and I need to schedule it to run automatically starting at a certain time of day and running repeatedly both at set time intervals although occasionally at specific times output by the macro.
I have approached the problem in two ways, both of which have presented problems that I can't figure out. The first approach is using a timer sub that runs the macro using the Application.OnTime command with the runtime derived from a cell in the workbook that is updated by the macro.
This method works pretty well if I am on the computer doing something else. However, I have noticed that if I leave, even if the computer doesn't go to sleep, the program inevitably stops re-running after 30min or so. Sometimes it also stops re-running even when I'm active on the computer. Why is this? What could be causing this? Also, naturally, I can't schedule the program to open and run automatically from within the macro, so I turned to the VBScript & Task Scheduler method for this part of the solution.
I have, however, encountered two problems with the execution of the VBScript. First, is that it often fails to start when run by Task Scheduler. Second, is that when it does run, the Application.OnTime command within the macro doesn't work. This is a problem because the macro needs to run sometimes at irregular time intervals which are determined during the course of the day as the macro runs through its iterations. Why would the vbscript fail to run when task manager tries, and why would it not initiate the application.ontime command the way the macro does when I run it manually?
VBA Timer Code:
Sub Timer()
Dim Runtime As Date
If Time <= TimeValue("17:45:00") Then
Workbooks("10am_Swing_Report").Sheets("Parameters").Cells(17, 2).Value = 10
ElseIf Time > TimeValue("17:45:00") And Time <= TimeValue("18:15:00") Then
Workbooks("10am_Swing_Report").Sheets("Parameters").Cells(17, 2).Value = 13
ElseIf Time > TimeValue("18:15:00") And Time <= TimeValue("18:45:00") Then
Workbooks("10am_Swing_Report").Sheets("Parameters").Cells(17, 2).Value = 16
ElseIf Time > TimeValue("18:45:00") And Time <= TimeValue("19:15:00") Then
Workbooks("10am_Swing_Report").Sheets("Parameters").Cells(17, 2).Value = 19
End If
Workbooks("10am_Swing_Report").Sheets("Parameters").Range("B16").Calculate
If Time > Workbooks("10am_Swing_Report").Sheets("Parameters").Range("B16").Value Then
Runtime = Time + TimeValue("00:00:30")
Else
Runtime = Workbooks("10am_Swing_Report").Sheets("Parameters").Range("B16").Value
End If
Application.OnTime TimeValue(Runtime), "Swingtimer"
Workbooks("10am_Swing_Report").Sheets("Parameters").Range("C16").Value = Runtime
Workbooks("10am_Swing_Report").Save
End Sub
Thanks!
I've updated the Vbs code to include a validation that the runtime has been passed, but I'm still encountering a problem with Task Scheduler failing to start the task. I'm getting the following error: "Launch request ignored, instance already running" followed by: "Task Start Failed". I was originally thinking this was because the Vbscript was still running from the previous instance, but I'm getting this from the very beginning.
Option Explicit
Dim xlApp
Dim xlBook
Dim runtime
Dim xlSheet
Set xlApp = CreateObject("Excel.Application")
xlApp.DisplayAlerts = False
'xlApp.Application.DisplayAlerts = True
Set xlBook = xlApp.Workbooks.Open("C:\Users\DORIAN\Dropbox\Swing_Report.xlsm",1,False)
Set xlSheet = xlBook.Sheets("Parameters")
'xlBook.application.Visible = True
'MsgBox FormatDateTime(xlSheet.Range("B16").Value,3)
runtime = TimeValue(FormatDateTime(xlSheet.Range("B16").Value,3))
'MsgBox runtime
If Time > runtime Then
xlApp.Run "Swingtimer"
'MsgBox "Running..."
WScript.Sleep 30000
Else
xlApp.Application.OnTime runtime, "Swingtimer"
'MsgBox "Timer Set"
End If
xlbook.close True
WScript.Quit

Could you do it in a different way?
1)
Run vbscript it in Windows Scheduler say every 1 minute or 1 hour or ….
2)
Put some validation inside VBscript
If Condition then
Execute your code
Else
Stop vbscript
End if
Using this you probably can get rid of OnTime event handler
Best regards
Alex

I think you are over complicating things. You just need a Workbook_Open event and Windows Task Scheduler.
Something like this (customize to suit your needs).
Private Sub Workbook_Open()
Msgbox Date
Worksheets("Sheet1").Range("A1").Value = Date
End Sub
Schedule the event using the Windows Task Scheduler.
http://www.sevenforums.com/tutorials/11949-elevated-program-shortcut-without-uac-prompt-create.html

Related

Power Query Tables won't download data until VBA finishes executing

I have a spreadsheet with 4 worksheets, each one connected to a power query which connects to an OLEDB database and runs a select to import data into the workbook.
I am trying to automate the refresh of the queries.
I have VBA in the workbook which
- disables background refresh,
- runs Workbook.RefreshAll,
- resets the background refresh to true (as I need this for the automation software to be able to run it in the first place) and
- then waits 30 seconds to give the automation software a bit of time before it starts saving the workbook.
All the software does is call the macro in the workbook and then save following execution.
The queries correctly execute one after the other and I can tell the data in the tables has refreshed.
The power query confirmation that the rows have been downloaded happens after the VBA completes executing, so the automation software saves it before this is done and Excel reports the download as having failed. This isn't a problem when everything works, but we won't know when the download has truly failed, unless I add in some sort of a row count comparison which is really a workaround rather than an actual solution.
I have tried creating two subs in the workbook, one to do the refresh and then another which calls the first sub and then waits 30 seconds, I was hoping the download would complete after the first sub finishes executing, but this doesn't happen. I have tried refreshing one connection at a time instead of using RefreshAll, tried refreshing the actual power query tables instead of the connections and have tried refreshing with background query set to true and then looping until the refresh is marked as complete (which works if you step through it, but crashes if you just run the VBA in full).
I also tried saving the spreadsheet at the end of the VBA, but it still waits until the save is complete to update the power query status.
My latest VBA:
Sub Workbook_RefreshAll()
Application.DisplayAlerts = False
For Each objConnection In ThisWorkbook.Connections
'Temporarily disable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = False
objConnection.OLEDBConnection.MaintainConnection = False
Next
ActiveWorkbook.RefreshAll
For Each objConnection In ThisWorkbook.Connections
'Re-enable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = True
objConnection.OLEDBConnection.MaintainConnection = True
Next
newHour = Hour(Now())
newMinute = Minute(Now())
newSecond = Second(Now()) + 30
waitTime = TimeSerial(newHour, newMinute, newSecond)
Application.Wait waitTime
End Sub
(I can't remember whether the maintain connection parameter actually helped as I as trying to force the connection to drop after each refresh, but it works so haven't taken it out.)
Instead of Application.Wait try the following:
Do until now() >= waittime
DoEvents
Loop

Excel 2016 crash on API timer initiated Workbook.close

I have a workbook that crashes Excel on close when the close is triggered by an API timer.
Edit: The workbook in link below has nothing but the timer and exit modules and it has the same behavior so it's not an issue with the content.
https://www.dropbox.com/s/x0xdwgj5h34ctdk/Book1.xlsm?dl=0
A few seconds after the workbook closes Excel crashes. I have tried turning the timer off, unhiding all sheets, unloading forms...all the objects are set to Nothing.
All I am doing is Workbooks(ThisWorkbook.Name).Close SaveChanges:=True??!!
The same sub is called from a UserForm with no problem. The workbook closes without Excel crashing.
How to fix this?
Sub ApplicationExit()
' Call UnloadAllForms
' DoEvents
' Sleep 1000
' Call StopCloseTimer
'DoEvents
'If Application.Workbooks.Count = 1 Then
' Workbooks(ThisWorkbook.Name).Save
' Application.Quit
'Else
DoEvents
Workbooks(ThisWorkbook.Name).Close SaveChanges:=True
'End If
End Sub
The code triggered by the timer is;
Sub TimerCalled()
If CloseTimerValue = "" Then Call Reset_CloseTimerValue
DoEvents
If basTimers.CloseTimerValue <= Now() And Not Unlocked Then Call ApplicationExit
On Error Resume Next 'In case sheet is protected
ThisWorkbook.Sheets("JobIndex").Range("CloseCount").Value = Format(Now() - CloseTimerValue, "hh:m:s")
End Sub
The userform says;
CloseUp:
i = MsgBox("Close Project Register?", vbYesNo, MsgBoxTitle)
Select Case i
Case vbYes
Call ApplicationExit
Case vbNo
Workbooks(ThisWorkbook.Name).Save
End Select
Timer in excel vba is done by using the Application.OnTime command or windows timer, as you are using.
Notice that, to schedule the call for a function (using the upper-mentioned command), it's used APPLICATION, which means it tells EXCEL to execute the call, not your workbook. So, in your case, the workbook is closed, BUT the to-be-called procedure is still scheduled to go off at some time in the future. The error that you get is that the excel doesn't find the procedure and then throws an error.
The reason it crashes in your case is that you are using windows timer to do it. When you close your workbook, your instance of the lib32 is lost and when time comes, Windows can't reach in the memory and then crashes the whole application.
In both cases, it seems that your scheduled procedure is still running.
I suggest that you look into that and consider using application.ontime.

Macro to start at a particular time

I have found sources that say to use
Application.OnTime TimeValue("18:00:00"), "MyMacro"
But I can't seem to get it to work. This is what I have entered
Sub TimeStamp()
'
' TimeStamp Macro
'
' Keyboard Shortcut: Ctrl+Shift+T
'
Application.OnTime TimeValue("13:25:00"), "TimeStamp"
' Following refreshes the data
Application.CalculateFullRebuild
Then the rest of the code followed by End Sub
For whatever unknown reason, when I start the Macro, it does not wait until that given time. What syntax rules am I breaking here?
The macro is triggered when the macro is triggered. OnTime schedules it to be run automatically, but why should that prevent you from running it manually? Think of it like a virus scan. Many people have their computers configured to do automatic scans at certain set times, but are perfectly able to run nonscheduled scans at any time. If you want to make sure that your macro doesn't have any effect before a certain time, use an if - then statement involving Now (if it is too early exit sub) -- although it makes more sense to not run it at all when you don't want to.
Another method that I've used for years successfully is by just creating a small vb script that is scheduled to call the macro. My other answer to a very similar question show this example script
Dim xlApp
Dim xlWkb
Set xlApp = CreateObject("excel.application")
Set xlWkb = xlApp.Workbooks.Open("PATH TO YOUR FILE")
xlApp.Visible = True
xlWkb.RunAutoMacros 1 'enables macros to be run on open
xlApp.Run ("YOUR PROCEDURE")
xlApp.Workbooks("YOUR WORKBOOK NAME").Save 'Save the workbook
xlApp.Quit 'quits excel
Schedule this through Tasks Scheduler. My other answer can be found here
This is what I ended up with thanks to the help of several of you. I used:
Private Sub Workbook_Open()
Application.Wait "6:45:00"
Call TimeStamp
End Sub
and placed this in "ThisWorkbook". This made it so that when I open up my workbook, the macro is automatically started but it waits until 3:03 pm to perform the rest of the task.
Next, I needed it to refresh every 15 minutes so I used Chip's solution suggested by User: Findwindow. The code goes as follows
Public RunWhen As Double
Public Const cRunIntervalSeconds = 900 ' 15 minutes
Public Const cRunWhat = "TimeStamp" ' the name of the procedure to run
Sub StartTimer()
If Time < TimeSerial(13, 15, 0) Then
RunWhen = Now + TimeSerial(0, 0, cRunIntervalSeconds)
Application.OnTime EarliestTime:=RunWhen, Procedure:=cRunWhat, _
Schedule:=True
End If
End Sub
at the end of the TimeStamp macro, I put Call StartTimer so that every time the macro is ran, the timer will schedule a new run time for 15 minutes from the current time. The If Time < TimeSerial(13, 15, 0) Then allows for the macro to stop running at 1:15pm, the time that I wanted it to stop.

VBA userform is "Not Responding" while macro is running

Why my userform shows "Not Responding" when I run the following code? I have been trying to resolve it, but it is not resolved yet.
Actually it works sometimes. I think the problem has something to do with the screen updating.
' The input button in Sheet1
Sub Rectangle1_Click()
'Remember time when macro starts
StartTime = Timer
' To improve speed and performance
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
' Show the userform
UserForm1.Show vbModeless
UserForm1.Label1.Caption = "Calculation in progress ... " & vbNewLine & "Please be patient"
UserForm1.Label1.Font.Size = 12
UserForm1.Top = (Application.Height / 2) - (UserForm1.Height / 2)
UserForm1.Left = (Application.Width / 2) - (UserForm1.Width / 2)
UserForm1.CommandButton1.Visible = False
UserForm1.Repaint
Call Insert_RawData
'Determine how many seconds code took to run
SecondsElapsed = Round(Timer - StartTime, 2)
UserForm1.Label1.Caption = "This code ran successfully in " & SecondsElapsed & " seconds"
UserForm1.CommandButton1.Visible = True
' Return back to the original settings
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationSemiautomatic
End Sub
It could be a number of things. If the issue is intermittent like you mention, then in all likelihood the program IS working, and Windows is just labeling it non-responsive because the program is working too hard to respond to the OS.
The likely issue is probably one, or a combination, of the following:
The amount of data being processed by Insert_RawData
The amount of data in the workbook(s) open when the macro is running
Insert_RawData is encountering an error due to a bad/unhandled value in one of the cells it works with (less likely)
Some suggestions to try and narrow the source down:
If there's a way to consistently run so that you get the "Non-Responsive" in the Excel window, insert a break point at the call to Insert_RawData and watch it run to see if it's hitting an error
Alternatively, try to put some error checks into Insert_RawData and break points on the code handling the case that the error check fails
Capture the amount of data processed (bytes, cells, whatever is easiest) during each run of the macro along with the run time & see if there's a threshold your hitting (ex. <= 1 GB runs fine, but >1GB and the application looks frozen)
Unless you are hitting an error in Insert_RawData though, it's likely that the macro WILL complete, it just might take a really long time.

Allow user to kill process during wait in VBA

I have written a program to print multiple .pdfs with varying file extensions off of an Excel spreadsheet list.
The problem is that it takes anywhere from 30 seconds to a minute for the printer to receive the pdf after the line: Application.SendKeys "^p~", False is called.
To get by this I used Application.Wait (Now + TimeValue("0:01:03")) to wait for a minute (plus 3 seconds just to be safe) before closing the file.
To me there seems like there should be a better way than just causing the program to wait, so I looked around a little and found a question about this lovely gem known as Application.OnTime.
I tried a sample of one of the answers:
Sub test2()
ActiveSheet.Cells(1, 1).Value = ActiveSheet.Cells(1, 1).Value + 1
Application.OnTime Now + TimeValue("00:00:5"), "test2"
End Sub
However when I tried to stop the above code it kept going on an infinite loop and I was unable to stop it until I killed excel using the windows task manager.
I would like to be able to add in a little message box or something of the sort so that the user can click in between the wait time.
So that while the program is waiting for a minute, the user can either manually click and start the program on the next pdf, or click another button to exit if they need to stop printing early. Something like this:
Sub pdfPrinter()
'...
'Insert all the other code here
'...
Application.SendKeys "^p~", False
Application.OnTime Now + TimeValue("00:01:02"), "pdfPrinter"
continue= MsgBox("Click Retry to print again, or cancel to stop printer.", vbRetryCancel)
If continue = vbRetry Then
Call pdfPrinter
ElseIf continue = vbCancel Then
Exit Sub
End If
End Sub
Application.OnTime(unlike Application.Wait) is Asynchronous
so code after
Application.OnTime Now + TimeValue("00:01:02"), "pdfPrinter"
Runs Immediately.
If you want to print another PDF you need to call Application.OnTime with a different schedule of the 1st one.
Now, canceling Application.OnTime, when it is already started is a different story:
To do that you need to store that time that the respective function is scheduled to run and then Cancel it using the following code:
Application.OnTime Now + TimeValue("00:00:50"), "test", schedule:=False