Last week, Bart de Smet wrote about calling the Task Scheduler in Windows Vista (and Windows Server 2008) from managed code. In his usual style, he does a great job diving into the topic.
It reminded me of something I did last year for Windows Server 2008 Certification: write code to start an unprivileged task in Windows 6 specifically written for installers.
If you are familiar with UAC, you know that applications identify their least required privilege level in their manifest. Applications that don’t require elevation identify themselves with the level="asInvoker" tag (the value may be confusing, but makes sense).
This creates a bit of complexity for installations that launch applications when they finish. If your installation required elevation, but the launched application does not, what should you do? Windows UAC guidelines say that you should launch them unprivileged. This makes sense: you don’t want an application to run elevated as a side-effect of the installation.
And you do this by starting the task in the Windows 6 task scheduler.
The UAC guidelines contain some C++ code for this, and Bart has a managed version, but for reasons of maintainability and consistency, we wanted VB Script. I was able to find some pieces of this online, but the following code is basically a port of the UAC Guidelines version with a check for Windows version too.
' Arguments
set args = WScript.Arguments
if args.Count >= 1 then
strCommand = args(0)
end if
if args.Count >= 2 then
strArguments = args(1)
end if
' Determine the version of Windows
set objWMI = GetObject("winmgmts:\\.\root\cimv2")
set colOS = objWMI.InstancesOf("Win32_OperatingSystem")
for each objOS in colOS
version = split(CStr(objOS.Version), ".", 2)
next
if CInt(version(0)) < 7 then
' pre Vista / Server 2008. Just run the task directly
set objShell = CreateObject("Wscript.Shell")
set objProc = objShell.Exec(strCommand & " " & strArguments)
else
' Vista / Server 2008 or later. Schedule it for immediate execution
scheduleTask strCommand, strArguments
end if
' Schedule a task for immediate execution; requires Windows 6 or later
private sub ScheduleTask(strCommand, strArguments)
' Some constants we need
TASK_TRIGGER_REGISTRATION = 7
TASK_ACTION_EXEC = 0
TASK_CREATE = 2
TASK_LOGON_GROUP = 4
on error goto 0
' Get the TaskService class
set pService = CreateObject("Schedule.Service")
' Connect to the task service.
pService.Connect
' Get pointer to root task folder.
set pRootFolder = pService.GetFolder("\")
Randomize(20000)
taskName = "MyBackgroundTask " & CStr(Rnd(1000))
' See if task exists, delete it if it does.
on error resume next
pRootFolder.DeleteTask taskName, 0
on error goto 0
' Create task
set pTask = pService.NewTask(0)
set pSettings = pTask.Settings
pSettings.StopIfGoingOnBatteries = false
pSettings.DisallowStartIfOnBatteries = false
' Create trigger
set triggerCollection = pTask.Triggers
set trigger = triggerCollection.Create(TASK_TRIGGER_REGISTRATION)
' Create a new action
set actionCollection = pTask.Actions
set action = actionCollection.Create(TASK_ACTION_EXEC)
action.Path = strCommand
action.Arguments = strArguments
' Register the task using users group
set registeredTask = pRootFolder.RegisterTaskDefinition(taskName, pTask, TASK_CREATE, "S-1-5-32-545", null, TASK_LOGON_GROUP, "")
' give 10 seconds for the task to start
for i = 0 to 100
state = registeredTask.State
if state = TASK_STATE_RUNNING then
break
end if
WScript.Sleep 100
next
' delete the task
pRootFolder.DeleteTask taskName, 0
end sub
You can see it takes arguments for the application and an argument to pass to the application. Also note that the VBS must be run elevated in Windows 6, otherwise tasks cannot be scheduled.
Ah, VB Script. You gotta love it. Or not, but I wish someone else had posted this!
Note to Microsoft: you would have saved a lot of people time if you had included a VBS version in your guidelines document.
[tags]VBS, UAC, Server 2008, Vista, Windows 6, Task Scheduler[/tags]





