Scheduled Tasks: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
(CoCreateInstance returns the pointer in $R1 and the result in $R9)
m (→‎The Script: <highlight-nsis>)
 
(10 intermediate revisions by 9 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. brainsucker wrote the original version of this script and posted it to the forums. I've made some modifications.  
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 he has the system plugin remain loaded the entire script. 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 bu this function.
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 run 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 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 runned every 10 minutes for 2 hours).
;          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 runned once
;      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 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 "ZippyBackup"
   push "Username"
   push "ZippyPassword"
   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 ===
=== Setting Task Flags ===


The following code can be added to schedule a task flag:
The following code can be added to schedule a task flag:
 
<highlight-nsis>
  ; ITask->SetFlags()
; ITask->SetFlags()
    System::Call '$R2->28(i 0x2000)'
System::Call '$R2->28(i 0x2000)'
 
</highlight-nsis>
Flags currently available:
Flags currently available:


Line 207: Line 216:


TASK_FLAG_RUN_ONLY_IF_LOGGED_ON        (0x2000)
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:System Plugin Examples]]
[[Category:COM Programming]]
[[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