orbs

create a scheduled task by schtasks.exe with unsupported options

2 posts in this topic

#1 ·  Posted (edited)

suppose your program has a service-like functionality, i.e. launch at Windows startup, constantly running in the background, no GUI (e.g. TCP server).

you could write it as a service (with this marvelous UDF), or you could install it as a scheduled task.

Windows Task Scheduler has an API, and is also manageable by WMI. but if you are not a seasoned developer, the simplest way is to call schtasks.exe to create the task (as well as validate and run it).

unfortunately, schtasks.exe does not directly support all options and settings of a task. tasks created by schtasks.exe have some undesirable settings enabled by default, such as "Start the task only if the computer is on AC power", "Stop if the computer switches to battery power", and the most annoying is of course, "Stop the task if it runs longer than 3 days".

to overcome this, we write an XML file with the desired options configured, and call schtasks.exe to create the task by that XML file.

this topic uses two files:

1) the tasked script itself, which - in this example - is used only to log the used and free space of the system drive:

#RequireAdmin
#AutoIt3Wrapper_Res_Fileversion=0.0.0.0
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_Change2CUI=Y
#NoTrayIcon

Global $sDrive = StringLeft(@WindowsDir, 3)
Global $sFile = $sDrive & 'Au3task_VolLog.csv'
Global $sLastMinute = ''
Global $nDriveSpaceTotal=0
Global $nDriveSpaceFree=0

While True
    If Not FileExists($sFile) Then FileWriteLine($sFile, 'Time,Used Space [MB],Free Space [MB]')
    If @SEC = '00' And @MIN <> $sLastMinute Then
        $nDriveSpaceTotal=Round(DriveSpaceTotal($sDrive))
        $nDriveSpaceFree=Round(DriveSpaceFree($sDrive))
        FileWriteLine($sFile, @YEAR & '/' & @MON & '/' & @MDAY & '  ' & @HOUR & ':' & @MIN & ':' & @SEC & ',' & $nDriveSpaceTotal - $nDriveSpaceFree & ',' & $nDriveSpaceFree)
        $sLastMinute = @MIN
    EndIf
    Sleep(100)
WEnd

compile this script and name it au3@task.exe to run the example setup script hereunder. note the wrapper directives!.

2) the setup script (which you can run without compiling). this installs the compiled task above to the root of the system drive, and configures a task to run it at startup as the local SYSTEM account:

(EDIT: updated setup script with a subfolder for the task is in post #2 hereunder)

#RequireAdmin
#AutoIt3Wrapper_Res_Fileversion=0.0.0.0
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_UseX64=y
#NoTrayIcon

#include <MsgBoxConstants.au3>

Global $sTitle = 'au3@task Setup'
Global $sDrive = StringLeft(@WindowsDir, 3)
Global $sTaskName = 'au3@task'

If AlreadyInstalled() Then
    If MsgBox($MB_ICONQUESTION + $MB_YESNO, $sTitle, 'Uninstall?  ') = $IDYES Then
        If Uninstall() Then
            MsgBox($MB_ICONINFORMATION, $sTitle, 'Uninstallation completed successfully.  ')
        Else
            MsgBox($MB_ICONERROR, $sTitle, 'Uninstallation error.  ')
        EndIf
    Else
        MsgBox($MB_ICONINFORMATION, $sTitle, 'Uninstallation aborted.  ')
    EndIf
Else
    If MsgBox($MB_ICONQUESTION + $MB_YESNO, $sTitle, 'Install?  ') = $IDYES Then
        If Install() Then
            If MsgBox($MB_ICONQUESTION + $MB_YESNO, $sTitle, 'Installation completed successfully. Run task now?  ') = $IDYES Then
                If RunWait('schtasks.exe /Run /TN ' & $sTaskName, '', @SW_HIDE) = 0 Then
                    MsgBox($MB_ICONINFORMATION, $sTitle, 'Task is running.  ')
                Else
                    MsgBox($MB_ICONERROR, $sTitle, 'Error running the task.  ')
                EndIf
            Else
                MsgBox($MB_ICONINFORMATION, $sTitle, 'Done.  ')
            EndIf
        Else
            MsgBox($MB_ICONERROR, $sTitle, 'Installation error.  ')
        EndIf
    Else
        MsgBox($MB_ICONINFORMATION, $sTitle, 'Installation aborted.  ')
    EndIf
EndIf

Func AlreadyInstalled()
    If Not FileExists($sDrive & 'au3@task.exe') Then Return False
    If RunWait('schtasks.exe /Query /TN ' & $sTaskName, '', @SW_HIDE) <> 0 Then Return False
    Return True
EndFunc   ;==>AlreadyInstalled

Func Install()
    If Not FileInstall('au3@task.exe', $sDrive, 1) Then Return False
    Local $sXML = _
            '<?xml version="1.0" encoding="UTF-16"?>' & @CRLF & _
            '<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">' & @CRLF & _
            '  <Triggers>' & @CRLF & _
            '    <BootTrigger>' & @CRLF & _
            '      <Enabled>true</Enabled>' & @CRLF & _
            '    </BootTrigger>' & @CRLF & _
            '  </Triggers>' & @CRLF & _
            '  <Principals>' & @CRLF & _
            '    <Principal id="Author">' & @CRLF & _
            '      <UserId>S-1-5-18</UserId>' & @CRLF & _
            '      <RunLevel>HighestAvailable</RunLevel>' & @CRLF & _
            '    </Principal>' & @CRLF & _
            '  </Principals>' & @CRLF & _
            '  <Settings>' & @CRLF & _
            '    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>' & @CRLF & _
            '    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>' & @CRLF & _
            '    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>' & @CRLF & _
            '    <AllowHardTerminate>false</AllowHardTerminate>' & @CRLF & _
            '    <StartWhenAvailable>false</StartWhenAvailable>' & @CRLF & _
            '    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>' & @CRLF & _
            '    <IdleSettings>' & @CRLF & _
            '      <StopOnIdleEnd>true</StopOnIdleEnd>' & @CRLF & _
            '      <RestartOnIdle>false</RestartOnIdle>' & @CRLF & _
            '    </IdleSettings>' & @CRLF & _
            '    <AllowStartOnDemand>true</AllowStartOnDemand>' & @CRLF & _
            '    <Enabled>true</Enabled>' & @CRLF & _
            '    <Hidden>false</Hidden>' & @CRLF & _
            '    <RunOnlyIfIdle>false</RunOnlyIfIdle>' & @CRLF & _
            '    <WakeToRun>false</WakeToRun>' & @CRLF & _
            '    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>' & @CRLF & _
            '    <Priority>7</Priority>' & @CRLF & _
            '  </Settings>' & @CRLF & _
            '  <Actions Context="Author">' & @CRLF & _
            '    <Exec>' & @CRLF & _
            '      <Command>ExePath</Command>' & @CRLF & _
            '    </Exec>' & @CRLF & _
            '  </Actions>' & @CRLF & _
            '</Task>'
    $sXML = StringReplace($sXML, 'ExePath', $sDrive & 'au3@task.exe')
    Local $sFileXML = @TempDir & '\au3@task.xml'
    FileDelete($sFileXML)
    FileWrite($sFileXML, $sXML)
    If FileRead($sFileXML) <> $sXML Then Return False
    If RunWait('schtasks.exe /Create /XML "' & $sFileXML & '" /TN ' & $sTaskName, '', @SW_HIDE) <> 0 Then Return False
    FileDelete($sFileXML)
    Return True
EndFunc   ;==>Install

Func Uninstall()
    RunWait('schtasks.exe /End /TN ' & $sTaskName, '', @SW_HIDE)
    If RunWait('schtasks.exe /Delete /F /TN ' & $sTaskName, '', @SW_HIDE) <> 0 Then Return False
    Sleep(3000)
    If Not FileDelete($sDrive & 'au3@task.exe') Then Return False
    Return True
EndFunc   ;==>Uninstall

note: the XML file embedded in the script was exported from a task already configured with the required options. the registration components were stripped. if you wish to configure other settings, you can study the XML schema, or simply configure a task manually and export it to XML.

tested on Windows 7 Ultimate 64-bit.

enjoy!

Edited by orbs
3 people like this

Share this post


Link to post
Share on other sites



now, suppose you want to install your scheduled task into a subfolder. that way it will be visible to the user only if the "Task Scheduler Library" branch is expanded, so it is visually segregated from users' tasks - same way Microsoft do with their phenomenal list of tasks (some 88 tasks in 58 folders...).

the trick is to prefix the task name with the subfolder, e.g. au3\au3@task instead of just au3@task as in the example above, for the task to be placed in the au3 subfolder. also, don't forget to remove it at uninstall. so this is the updated setup script:

#RequireAdmin
#AutoIt3Wrapper_Res_Fileversion=0.0.1.0
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_UseX64=y
#NoTrayIcon

#include <MsgBoxConstants.au3>

Global $sTitle = 'au3@task Setup'
Global $sDrive = StringLeft(@WindowsDir, 3)
Global $sTaskDir = 'au3'
Global $sTaskName = 'au3@task'
Global $sTaskFullName = $sTaskDir & '\' & $sTaskName

If AlreadyInstalled() Then
    If MsgBox($MB_ICONQUESTION + $MB_YESNO, $sTitle, 'Uninstall?  ') = $IDYES Then
        If Uninstall() Then
            MsgBox($MB_ICONINFORMATION, $sTitle, 'Uninstallation completed successfully.  ')
        Else
            MsgBox($MB_ICONERROR, $sTitle, 'Uninstallation error.  ')
        EndIf
    Else
        MsgBox($MB_ICONINFORMATION, $sTitle, 'Uninstallation aborted.  ')
    EndIf
Else
    If MsgBox($MB_ICONQUESTION + $MB_YESNO, $sTitle, 'Install?  ') = $IDYES Then
        If Install() Then
            If MsgBox($MB_ICONQUESTION + $MB_YESNO, $sTitle, 'Installation completed successfully. Run task now?  ') = $IDYES Then
                If RunWait('schtasks.exe /Run /TN ' & $sTaskFullName, '', @SW_HIDE) = 0 Then
                    MsgBox($MB_ICONINFORMATION, $sTitle, 'Task is running.  ')
                Else
                    MsgBox($MB_ICONERROR, $sTitle, 'Error running the task.  ')
                EndIf
            Else
                MsgBox($MB_ICONINFORMATION, $sTitle, 'Done.  ')
            EndIf
        Else
            MsgBox($MB_ICONERROR, $sTitle, 'Installation error.  ')
        EndIf
    Else
        MsgBox($MB_ICONINFORMATION, $sTitle, 'Installation aborted.  ')
    EndIf
EndIf

Func AlreadyInstalled()
    If Not FileExists($sDrive & 'au3@task.exe') Then Return False
    If RunWait('schtasks.exe /Query /TN ' & $sTaskFullName, '', @SW_HIDE) <> 0 Then Return False
    Return True
EndFunc   ;==>AlreadyInstalled

Func Install()
    If Not FileInstall('au3@task.exe', $sDrive, 1) Then Return False
    Local $sXML = _
            '<?xml version="1.0" encoding="UTF-16"?>' & @CRLF & _
            '<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">' & @CRLF & _
            '  <Triggers>' & @CRLF & _
            '    <BootTrigger>' & @CRLF & _
            '      <Enabled>true</Enabled>' & @CRLF & _
            '    </BootTrigger>' & @CRLF & _
            '  </Triggers>' & @CRLF & _
            '  <Principals>' & @CRLF & _
            '    <Principal id="Author">' & @CRLF & _
            '      <UserId>S-1-5-18</UserId>' & @CRLF & _
            '      <RunLevel>HighestAvailable</RunLevel>' & @CRLF & _
            '    </Principal>' & @CRLF & _
            '  </Principals>' & @CRLF & _
            '  <Settings>' & @CRLF & _
            '    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>' & @CRLF & _
            '    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>' & @CRLF & _
            '    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>' & @CRLF & _
            '    <AllowHardTerminate>false</AllowHardTerminate>' & @CRLF & _
            '    <StartWhenAvailable>false</StartWhenAvailable>' & @CRLF & _
            '    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>' & @CRLF & _
            '    <IdleSettings>' & @CRLF & _
            '      <StopOnIdleEnd>true</StopOnIdleEnd>' & @CRLF & _
            '      <RestartOnIdle>false</RestartOnIdle>' & @CRLF & _
            '    </IdleSettings>' & @CRLF & _
            '    <AllowStartOnDemand>true</AllowStartOnDemand>' & @CRLF & _
            '    <Enabled>true</Enabled>' & @CRLF & _
            '    <Hidden>false</Hidden>' & @CRLF & _
            '    <RunOnlyIfIdle>false</RunOnlyIfIdle>' & @CRLF & _
            '    <WakeToRun>false</WakeToRun>' & @CRLF & _
            '    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>' & @CRLF & _
            '    <Priority>7</Priority>' & @CRLF & _
            '  </Settings>' & @CRLF & _
            '  <Actions Context="Author">' & @CRLF & _
            '    <Exec>' & @CRLF & _
            '      <Command>ExePath</Command>' & @CRLF & _
            '    </Exec>' & @CRLF & _
            '  </Actions>' & @CRLF & _
            '</Task>'
    $sXML = StringReplace($sXML, 'ExePath', $sDrive & 'au3@task.exe')
    Local $sFileXML = @TempDir & '\au3@task.xml'
    FileDelete($sFileXML)
    FileWrite($sFileXML, $sXML)
    If FileRead($sFileXML) <> $sXML Then Return False
    If RunWait('schtasks.exe /Create /XML "' & $sFileXML & '" /TN ' & $sTaskFullName, '', @SW_HIDE) <> 0 Then Return False
    FileDelete($sFileXML)
    Return True
EndFunc   ;==>Install

Func Uninstall()
    RunWait('schtasks.exe /End /TN ' & $sTaskFullName, '', @SW_HIDE)
    If RunWait('schtasks.exe /Delete /F /TN ' & $sTaskFullName, '', @SW_HIDE) <> 0 Then Return False
    Sleep(3000)
    If Not FileDelete($sDrive & 'au3@task.exe') Then Return False
    DirRemove(@SystemDir & '\Tasks\' & $sTaskDir) ; remove the folder only if empty
    Return True
EndFunc   ;==>Uninstall

 

Note: $sTaskDir can be set to an empty string for not using a subfolder.

3 people like this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now