Scheduled Tasks: Difference between revisions
(Added new version of the script.) |
m (→The Script: <highlight-nsis>) |
||
(16 intermediate revisions by 13 users not shown) | |||
Line 1: | Line 1: | ||
Scheduling tasks is possible using the System plug-in. It involves creating COM objects, which is complicated in C++ and a black art in NSIS. | Scheduling tasks is possible using the System plug-in. It involves creating COM objects, which is complicated in C++ and a black art in NSIS. brainsucker wrote the original version of this script and posted it to the forums. I've made some modifications. | ||
The first is | The first change is that brainsucker's old version has the system plugin remaining loaded during the entire script run. I opt for loading and unloading it in the plugin. If you call this in a function or section that uses the system plugin's private registers, push and pop them as they will be destroyed by this function. | ||
The second change to this script is that it sets the user that | The second change to this script is that it sets the user that runs the task. If you want the user running the installer to be the user that executes the task just put bogus values in this field. I could make the function detect null values for user name and password, but I wanted to get this script up with the increased functionality of setting the user. | ||
= The Script = | = The Script = | ||
Line 29: | Line 29: | ||
; 11, 12 - Duration and Interval of task in minutes. (duration - the whole | ; 11, 12 - Duration and Interval of task in minutes. (duration - the whole | ||
; time task will work, interval - time between runs. D = 120, | ; time task will work, interval - time between runs. D = 120, | ||
; I = 10: task will be | ; I = 10: task will be run every 10 minutes for 2 hours). | ||
; 13 - flags (should be ored (|)): | ; 13 - flags (should be ored (|)): | ||
; 1 - task has end date (6,7,8 defined) | ; 1 - task has end date (6,7,8 defined) | ||
Line 36: | Line 36: | ||
; 14 - trigger type: there are 7 different types, every one with it own | ; 14 - trigger type: there are 7 different types, every one with it own | ||
; structure | ; structure | ||
; 0 = ONCE task will be | ; 0 = ONCE task will be run once | ||
; 5 = On IDLE task will run on system IDLE (ITask->SetIdleWait) | ; 5 = On IDLE task will run on system IDLE (ITask->SetIdleWait) | ||
; 6 = At System Startup | ; 6 = At System Startup | ||
Line 98: | Line 98: | ||
; ITask->SetParameters() | ; ITask->SetParameters() | ||
System::Call '$R2->34(w | System::Call '$R2->34(w r4)' | ||
; ITask->CreateTrigger(trindex, ITaskTrigger) | ; ITask->CreateTrigger(trindex, ITaskTrigger) | ||
Line 162: | Line 162: | ||
i 1, \ | i 1, \ | ||
&i2 1, &i2 00, &i2 0, i 0, &i2 0) i.s" | &i2 1, &i2 00, &i2 0, i 0, &i2 0) i.s" | ||
push " | push "Username" | ||
push " | push "Userpassword" | ||
Call CreateTask | Call CreateTask | ||
Pop $0 | Pop $0 | ||
Line 172: | Line 172: | ||
</highlight-nsis> | </highlight-nsis> | ||
=== Delete Task === | |||
The following code can be added to the code above to delete a pre-existing task: | |||
<highlight-nsis> | |||
; ITaskScheduler->Delete() | |||
System::Call '$R1->7(w r0) i.R9' | |||
</highlight-nsis> | |||
It should be added before the line: <code>; ITaskScheduler->NewWorkItem()</code> | |||
=== Setting Task Flags === | |||
The following code can be added to schedule a task flag: | |||
<highlight-nsis> | |||
; ITask->SetFlags() | |||
System::Call '$R2->28(i 0x2000)' | |||
</highlight-nsis> | |||
Flags currently available: | |||
TASK_FLAG_INTERACTIVE (0x1) | |||
TASK_FLAG_DELETE_WHEN_DONE (0x2) | |||
TASK_FLAG_DISABLED (0x4) | |||
TASK_FLAG_START_ONLY_IF_IDLE (0x10) | |||
TASK_FLAG_KILL_ON_IDLE_END (0x20) | |||
TASK_FLAG_DONT_START_IF_ON_BATTERIES (0x40) | |||
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES (0x80) | |||
TASK_FLAG_RUN_ONLY_IF_DOCKED (0x100) | |||
TASK_FLAG_HIDDEN (0x200) | |||
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET (0x400) | |||
TASK_FLAG_RESTART_ON_IDLE_RESUME (0x800) | |||
TASK_FLAG_SYSTEM_REQUIRED (0x1000) | |||
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON (0x2000) | |||
=== SetMaxRunTime() (stop the task killing itself after 72 hours) === | |||
The following code can be added to the code above to disable the annoying "Stop the task if it runs for:" behavior. Windows' default is 72 hours. Per the API documentation, the value is in milliseconds, so you can use the below code snippet to change the 72 hours to something more to your liking. What you often care about is the INFINITE value, which disables the function altogether (note there are also other ways of doing this in this API). This value is tricky as some of the source code I read on the Internet claims that in Windows Vista and Windows 7, there is a bug where INFINITE doesn't work, and INFINITE-1 is needed to pass to the procedure. In any event, I've only tested this on Windows XP so your mileage may vary on those later operating systems: | |||
<highlight-nsis> | |||
; ITask->SetMaxRunTime() | |||
; on Windows 7 and Vista this may have to be | |||
; 0xFFFFFFFF - 1 (whatever that is, not sure | |||
; whether the hex is interpreted as signed | |||
; or unsigned | |||
System::Call '$R2->42(i 0xFFFFFFFF)' | |||
</highlight-nsis> | |||
It should be added somewhere in the section where ITask is being set up (I put it after ITask->SetParameters). | |||
= Links = | = Links = | ||
[http://forums.winamp.com/showthread.php?s=&threadid=147686&highlight=scheduled+task This forum thread] contains the original version of this script. | *[http://forums.winamp.com/showthread.php?s=&threadid=147686&highlight=scheduled+task This forum thread] contains the original version of this script. | ||
*[http://msdn.microsoft.com/en-us/library/windows/desktop/aa383618%28v=vs.85%29.aspx This MSDN link] describes the TASK_TRIGGER structure used in this script | |||
[[Category:Tutorials]] | [[Category:Tutorials]] | ||
[[Category:System Plugin Examples]] | |||
[[Category:COM Programming]] |
Latest revision as of 17:19, 1 September 2015
Scheduling tasks is possible using the System plug-in. It involves creating COM objects, which is complicated in C++ and a black art in NSIS. brainsucker wrote the original version of this script and posted it to the forums. I've made some modifications.
The first change is that brainsucker's old version has the system plugin remaining loaded during the entire script run. I opt for loading and unloading it in the plugin. If you call this in a function or section that uses the system plugin's private registers, push and pop them as they will be destroyed by this function.
The second change to this script is that it sets the user that runs the task. If you want the user running the installer to be the user that executes the task just put bogus values in this field. I could make the function detect null values for user name and password, but I wanted to get this script up with the increased functionality of setting the user.
The Script
; Adds a scheduled task running as a different user than the one ; running the installer. Modified from a script by brainsucker ; ; (c) Justin Dearing <zippy1981@gmail.com>, 2006 ; (c) brainsucker, 2002 Name "System Plugin Example" OutFile "Sheduletask.exe" ; TASK_TRIGGER is the struct that sets when your task in run. ; Setting it via the NSIS System plugin is a healthy alternative to ; banging ones head against the wall. ; ; general TASK_TRIGGER structure arguments ; 1, 2 - skip ; 3,4,5 - BEGIN year, month, day ; 6,7,8 - END year, month, day (SHOULD be = 0, if 1 flag is skipped) ; 9, 10 - Start hour, minute ; 11, 12 - Duration and Interval of task in minutes. (duration - the whole ; time task will work, interval - time between runs. D = 120, ; I = 10: task will be run every 10 minutes for 2 hours). ; 13 - flags (should be ored (|)): ; 1 - task has end date (6,7,8 defined) ; 2 - task will be killed at Duration end ; 4 - task trigger disabled ; 14 - trigger type: there are 7 different types, every one with it own ; structure ; 0 = ONCE task will be run once ; 5 = On IDLE task will run on system IDLE (ITask->SetIdleWait) ; 6 = At System Startup ; 7 = At Logon ; these types use the following structure (so 7 means trigger at Logon): ; push "*(&l2, &i2 0, &i2 2003, &i2 9, &i2 4, &i2 0, &i2 0, &i2 0, &i2 14, &i2 20, i 0, i 0, i 0, i 7, i 0, &i2 0, i 0, &i2 0) i.s" ; 1 = DAILY - field 15 gives interval in days (here it 15) ; push "*(&l2, &i2 0, &i2 2003, &i2 9, &i2 3, &i2 0, &i2 0, &i2 0, &i2 13, &i2 10, i 0, i 0, i 0, i 1, &i2 15, i 0, i 0, &i2 0) i.s" ; 2 = WEEKLY - field 15 gives interval in weeks (here 17), ; field 16 - shows which days to run (OR them with |): ; Sunday-Saturday (0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40) ; in example monday and friday ; push "*(&l2, &i2 0, &i2 2003, &i2 9, &i2 3, &i2 0, &i2 0, &i2 0, &i2 13, &i2 10, i 0, i 0, i 0, i 2, &i2 13, &i2 0x2|0x20, &i2 0, i 0, &i2 0) i.s" ; 3 = MONTHLYDATE - field 15 bit field of days (OR them) to run ; (0x1-0x40000000), ; field 16 - bit field of month (OR them with |): ; January-December (0x1-0x800) ; in example (3 and 5 days of February and June) ; push "*(&l2, &i2 0, &i2 2003, &i2 9, &i2 3, &i2 0, &i2 0, &i2 0, &i2 13, &i2 10, i 0, i 0, i 0, i 3, i 0x4|0x20, &i2 0x2|0x20, i 0, &i2 0) i.s" ; 4 = MONTHLYDOW - field 15 week of month to run (1-5) ; field 16 - shows which days to run (OR them with |): ; Sunday-Saturday (0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40) ; field 17 - bit field of month (or them with |): ; January-December (0x1-0x800) ; in example (first week, monday and friday of February and June) ; push "*(&l2, &i2 0, &i2 2003, &i2 9, &i2 3, &i2 0, &i2 0, &i2 0, &i2 13, &i2 10, i 0, i 0, i 0, i 4, &i2 1, &i2 0x2|0x20, &i2 0x2|0x20, i 0, &i2 0) i.s" Function CreateTask !define GUIDTask "{148BD520-A2AB-11CE-B11F-00AA00530503}" !define GUIDITask "{148BD524-A2AB-11CE-B11F-00AA00530503}" !define GUIDTaskScheduler "{148BD52A-A2AB-11CE-B11F-00AA00530503}" !define GUIDITaskScheduler "{148BD527-A2AB-11CE-B11F-00AA00530503}" !define GUIDITaskTrigger "{148BD52B-A2AB-11CE-B11F-00AA00530503}" !define GUIDIPersistFile "{0000010b-0000-0000-C000-000000000046}" SetPluginUnload alwaysoff ; store registers and pop params System::Store "S r8r7r5r4r3r2r1r0" StrCpy $R0 "error" ; result System::Call "ole32::CoCreateInstance(g '${GUIDTaskScheduler}', i 0, i 11, g '${GUIDITaskScheduler}', *i .R1) i.R9" IntCmp $R9 0 0 End ; ITaskScheduler->NewWorkItem() System::Call '$R1->8(w r0, g "${GUIDTask}", g "${GUIDITask}", *i .R2) i.R9' ; IUnknown->Release() System::Call '$R1->2() i' ; release Task Scheduler object IntCmp $R9 0 0 End ; ITask->SetComment() System::Call '$R2->18(w r1)' ; ITask->SetApplicationName() System::Call '$R2->32(w r2)' ; ITask->SetWorkingDir() System::Call '$R2->36(w r3)' ; ITask->SetParameters() System::Call '$R2->34(w r4)' ; ITask->CreateTrigger(trindex, ITaskTrigger) System::Call '$R2->3(*i .R4, *i .R5)' ; allocate TASK_TRIGGER structure System::Call '$5' Pop $R6 ; ITaskTrigger->SetTrigger System::Call '$R5->3(i R6)' ; ITaskTrigger->Release System::Call '$R5->2()' ; free TASK_TRIGGER structure System::Free $R6 ; ITask->SetAccountInformation System::Call '$R2->30(w r7, w r8)' ; IUnknown->QueryInterface System::Call '$R2->0(g "${GUIDIPersistFile}", *i .R3) i.R9' ; QueryInterface ; IUnknown->Release() System::Call '$R2->2() i' ; release Task object IntCmp $R9 0 0 End ; IPersistFile->Save System::Call '$R3->6(i 0, i 1) i.R9' ; release IPersistFile System::Call '$R3->2() i' IntCmp $R9 0 0 End StrCpy $R0 "ok" End: ; restore registers and push result System::Store "P0 l" ; last plugin call must not have /NOUNLOAD so NSIS will be able to delete the temporary DLL SetPluginUnload manual ; do nothing System::Free 0 FunctionEnd Section "SiteSecureBackup" SetOutPath $TEMP push "My Task" push "My Task Comment" push "c:\Working Dir\My Task Program.exe" push "c:\Working Dir" push "My Program Args" push \ "*(&l2, &i2 0, \ &i2 2006, &i2 1, &i2 1, \ &i2 0, &i2 0, &i2 0, \ &i2 0, &i2 0, \ i 0, i 0, \ i 0, \ i 1, \ &i2 1, &i2 00, &i2 0, i 0, &i2 0) i.s" push "Username" push "Userpassword" Call CreateTask Pop $0 MessageBox MB_OK "Backup job Scheduled task creation result: $0" SectionEnd ; eof
Delete Task
The following code can be added to the code above to delete a pre-existing task:
; ITaskScheduler->Delete() System::Call '$R1->7(w r0) i.R9'
It should be added before the line: ; ITaskScheduler->NewWorkItem()
Setting Task Flags
The following code can be added to schedule a task flag:
; ITask->SetFlags() System::Call '$R2->28(i 0x2000)'
Flags currently available:
TASK_FLAG_INTERACTIVE (0x1)
TASK_FLAG_DELETE_WHEN_DONE (0x2)
TASK_FLAG_DISABLED (0x4)
TASK_FLAG_START_ONLY_IF_IDLE (0x10)
TASK_FLAG_KILL_ON_IDLE_END (0x20)
TASK_FLAG_DONT_START_IF_ON_BATTERIES (0x40)
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES (0x80)
TASK_FLAG_RUN_ONLY_IF_DOCKED (0x100)
TASK_FLAG_HIDDEN (0x200)
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET (0x400)
TASK_FLAG_RESTART_ON_IDLE_RESUME (0x800)
TASK_FLAG_SYSTEM_REQUIRED (0x1000)
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON (0x2000)
SetMaxRunTime() (stop the task killing itself after 72 hours)
The following code can be added to the code above to disable the annoying "Stop the task if it runs for:" behavior. Windows' default is 72 hours. Per the API documentation, the value is in milliseconds, so you can use the below code snippet to change the 72 hours to something more to your liking. What you often care about is the INFINITE value, which disables the function altogether (note there are also other ways of doing this in this API). This value is tricky as some of the source code I read on the Internet claims that in Windows Vista and Windows 7, there is a bug where INFINITE doesn't work, and INFINITE-1 is needed to pass to the procedure. In any event, I've only tested this on Windows XP so your mileage may vary on those later operating systems:
; ITask->SetMaxRunTime() ; on Windows 7 and Vista this may have to be ; 0xFFFFFFFF - 1 (whatever that is, not sure ; whether the hex is interpreted as signed ; or unsigned System::Call '$R2->42(i 0xFFFFFFFF)'
It should be added somewhere in the section where ITask is being set up (I put it after ITask->SetParameters).
Links
- This forum thread contains the original version of this script.
- This MSDN link describes the TASK_TRIGGER structure used in this script