user3073001
user3073001

Reputation: 279

How to access Windows Scheduler "Actions" with Python and win32com

I'm trying to get the action(s) for the tasks in the Windows Scheduler. Eryk Sun's excellent code snippet Python check for Completed and failed Task Windows scheduler is "almost" enough to get me the action associated with the task, but I'm stumped trying to interpret Microsofts documentation https://learn.microsoft.com/en-us/windows/win32/api/taskschd/ and translate it into something win32com.client will accept. A sample of what I can extract:

 import win32com.client
 scheduler = win32com.client.Dispatch('Schedule.Service')
 scheduler.Connect()
 folders = [scheduler.GetFolder('\\')]
 while folders:
    folder = folders.pop(0)
    folders += list(folder.GetFolders(0))
    for task in folder.GetTasks(0):
        print('Name       : %s' % task.Name)
        print('Path       : %s' % task.Path)
        print('Last Run   : %s' % task.LastRunTime)
        print('Last Result: %s\n' % task.LastTaskResult)

What I'm missing is how to get the "Actions" associated with each task

Upvotes: 2

Views: 2746

Answers (1)

DS_London
DS_London

Reputation: 4261

There are few more hoops to jump through. The MS documentation link has all the information, but it's a little terse. You also have to dig a bit deeper into win32com than usual.

GetTasks(0) returns an IRegisteredTask interface, which itself has a property Definition that returns an ITaskDefinition interface. The Actions property of this returns an IActionsCollection interface which is a collection of IAction interfaces. Phew!

Unfortunately, IAction is a base class which doesn't do much except tell you the Type of the action. There are 4 types of derived interface, the most common of which is IExecAction, then IComHandlerAction, IEmailAction and IShowMessageAction. The last two don't seem to be used on my system, so I haven't tested them.

The next step depends on how you use win32com.client: early binding (win32com.client.gencache.EnsureDispatch()) or late binding (win32com.client.dynamic.Dispatch()).

If you use early-binding, you need to cast the IAction interface to the particular action interface given by Type (using win32com.client.CastTo()). If you use late-binding, then you just call the type-specific method and hope for the best. My personal preference is for early-binding, as it enforces more type-safety, and allows you to use constant definitions rather than magic numbers.

Early-binding code:

import win32com.client

scheduler = win32com.client.gencache.EnsureDispatch('Schedule.Service')

def PrintAction(action):
    ty = action.Type

    if ty == win32com.client.constants.TASK_ACTION_COM_HANDLER: #=5
        print("COM Handler Action")
        coma = win32com.client.CastTo(action,"IComHandlerAction")
        print(coma.ClassId,coma.Data)
    elif ty == win32com.client.constants.TASK_ACTION_EXEC: #=0
        print("Exec Action")
        execa = win32com.client.CastTo(action,"IExecAction")
        print(execa.Path,execa.Arguments)
    elif ty == win32com.client.constants.TASK_ACTION_SEND_EMAIL: #=6 This might not work
        print("Send Email Action") 
        maila = win32com.client.CastTo(action,"IEmailAction")
        print(maila.Subject,maila.To)
    elif ty == win32com.client.constants.TASK_ACTION_SHOW_MESSAGE: #=7
        print("Show Message Action")
        showa = win32com.client.CastTo(action,"IShowMessageAction")
        print(showa.Title,showa.MessageBody)
    else:
        print("Unknown Action Type!") #Don't expect this


scheduler.Connect()
folders = [scheduler.GetFolder('\\')]
while folders:
    folder = folders.pop(0)
    folders += list(folder.GetFolders(0))
    for task in folder.GetTasks(0):
        print('Name       : %s' % task.Name)
        print('Path       : %s' % task.Path)
        print('Last Run   : %s' % task.LastRunTime)
        print('Last Result: %s' % task.LastTaskResult)
        defn = task.Definition
        actions = defn.Actions
        for action in actions:
            PrintAction(action)
            print()

Late-binding code:

Create the scheduler object differently, and replace the PrintAction() function:

scheduler = win32com.client.dynamic.Dispatch('Schedule.Service')

def PrintAction(action):
    ty = action.Type

    if ty == 5: 
        print("COM Handler Action")
        print(action.ClassId,action.Data)
    elif ty == 0: 
        print("Exec Action")
        print(action.Path,action.Arguments)
    elif ty == 6: # This might not work
        print("Send Email Action") 
        print(action.Subject,action.To)
    elif ty == 7: 
        print("Show Message Action")
        print(action.Title,action.MessageBody)
    else:
        print("Unknown Action Type!")

One of the output entries as an example:

Name       : Retry
Path       : \Microsoft\Windows\Management\Provisioning\Retry
Last Run   : 1999-11-30 00:00:00+00:00
Last Result: 267011
Exec Action
%windir%\system32\ProvTool.exe /turn 5 /source ProvRetryTask

Upvotes: 4

Related Questions