Task Scheduler - use an XML query to trigger a task

Chat with other TGB members about whatever is on your mind.
Post Reply
barnabas1969

Posts: 5738
Joined: Tue Jun 21, 2011 7:23 pm
Location: Titusville, Florida, USA

HTPC Specs: Show details

Task Scheduler - use an XML query to trigger a task

#1

Post by barnabas1969 » Sat Apr 13, 2013 12:22 am

I learned a new trick today, and I thought I would share.

I never thought the Task Scheduler could be used at such a granular level, but I was wrong. This is amazingly useful.

You may or may not know that the Task Scheduler can be used to trigger a task based on stuff in the Windows Event Logs. Sure, it can be used to trigger a task when the computer resumes from standby (Log: System, Source: Power-Troubleshooter, EventID: 1) but... did you know that you can trigger a task based on the DATA in the log (specifically the WakeSource)? Here's how:

The back-story:

I wanted to schedule a task that gets executed only when the computer wakes from user input. I have been using a script for a long time that executes the following command:

Code: Select all

powercfg -lastwake | find "USB\ROOT_HUB"
If the string is found, the script completes successfully. If not, it ends with a non-zero errorlevel. When the errorlevel is zero, this means that the computer woke because someone pressed the power button on the remote control, pressed a button on the keyboard, etc. If the errorlevel is not zero, this means that the computer woke for some other reason like a scheduled task (to record a show, perform some maintenance, etc).

This works most of the time. However, the powercfg command occasionally gets an error which causes a Windows dialog to be displayed which says something about powercfg getting an error, and it waits until someone presses a "Close program" button. When this happens, it totally messes things up with my automation macros. I have been unsuccessful at finding a resolution to the problem, and I am unwilling to re-install Windows in the hope of fixing the problem.

I wrote a program that automatically clicks the "Close program" button and tries to run powercfg again, but this is less than optimal in my configuration.

So... I started looking at the Windows Event Viewer and Task Scheduler to see if there was a way to trigger a task when the computer wakes due to user input on a USB device (keyboard, mouse, remote-control, etc).

I found that it is possible to create a schedule task in the Windows Task Scheduler that only runs when certain criteria are found in a system log entry. Here are the details...

In the case of my problem, I opened the Event Viewer, expanded the "Windows Logs" folder, and then clicked on the "System" log. Next, I scrolled down the list of log entries until I found the one with a source of "Power-Troubleshooter". I then double-clicked on that entry to open a dialog that shows more information about the event. Finally, I clicked on the "Details" tab, and chose the "XML View" radio button. Here's what you will see:
Details.jpg
The picture doesn't show all of the XML data, but here's the whole thing:

Code: Select all

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Microsoft-Windows-Power-Troubleshooter" Guid="{CDC05E28-C449-49C6-B9D2-88CF761644DF}" />
    <EventID>1</EventID>
    <Version>1</Version>
    <Level>4</Level>
    <Task>0</Task>
    <Opcode>0</Opcode>
    <Keywords>0x8000000000000000</Keywords>
    <TimeCreated SystemTime="2013-04-12T22:21:59.251222300Z" />
    <EventRecordID>302143</EventRecordID>
    <Correlation ActivityID="{D06482C0-1EAB-4EDF-A2C5-71687EF36751}" />
    <Execution ProcessID="1816" ThreadID="3396" />
    <Channel>System</Channel>
    <Computer>HTPC-PC</Computer>
    <Security UserID="S-1-5-19" />
  </System>
  <EventData>
    <Data Name="SleepTime">2013-04-12T22:21:11.865241900Z</Data>
    <Data Name="WakeTime">2013-04-12T22:21:48.340803200Z</Data>
    <Data Name="SleepDuration">7177</Data>
    <Data Name="WakeDuration">1841</Data>
    <Data Name="DriverInitDuration">1368</Data>
    <Data Name="BiosInitDuration">1050</Data>
    <Data Name="HiberWriteDuration">0</Data>
    <Data Name="HiberReadDuration">0</Data>
    <Data Name="HiberPagesWritten">0</Data>
    <Data Name="Attributes">20736</Data>
    <Data Name="TargetState">4</Data>
    <Data Name="EffectiveState">4</Data>
    <Data Name="WakeSourceType">4</Data>
    <Data Name="WakeSourceTextLength">12</Data>
    <Data Name="WakeSourceText">USB Root Hub</Data>
    <Data Name="WakeTimerOwnerLength">0</Data>
    <Data Name="WakeTimerContextLength">0</Data>
    <Data Name="WakeTimerOwner">
    </Data>
    <Data Name="WakeTimerContext">
    </Data>
  </EventData>
</Event>
So... I created a new task in Task Scheduler. The actions executed by the task and other information is not discussed in this post. The main thing here is the trigger for the task. In the "Triggers" tab, I clicked the "New" button, and chose "On an event" in the "Begin the task" drop-down box. Then, in the "Settings" area, I chose the "Custom" radio button and clicked the "New Event Filter" button.

This gives me a nifty filter box that allows me to filter on some things. It's actually not bad, but it doesn't go as deep into the details as I want. To be honest, I filled out this page with as much information as I knew about the event that I wanted to use as a trigger. So, on the "Filter" tab, I filled out all the information I knew.

Then, I clicked on the "XML" tab. This showed me the XML query that was generated by the information I entered on the "Filter" tab.

I then clicked the "Edit query manually" check-box, and updated the query as follows:

Code: Select all

<QueryList>
  <Query Id="0" Path="System">
    <Select Path="System">
        *[System[Provider[@Name='Microsoft-Windows-Power-Troubleshooter'] and (Level=4 or Level=0) and (EventID=1) and TimeCreated[timediff(@SystemTime) <= 120000]]]
        and
        *[EventData[Data[@Name='WakeSourceText'] and (Data='USB Root Hub')]]
     </Select>
  </Query>
</QueryList>
In the code box above, you can see that the Select tag includes Path="System". This means to look in the Windows "System" log. Then, you can see two lines that begin with "*[System" and "*[EventData". If you look at the 2nd code box in this post, you will see a "<System>" tag and an "<EventData>" tag. These correspond to the [System and [EventData portions of the query.

I think the rest is self-explanatory. It allows you to find stuff in your Windows Event logs, and create triggers in the Task Scheduler that can start tasks based on just about any data point in the event logs.

I hope this helps some people. Any questions, just post below.

User avatar
holidayboy

Posts: 2836
Joined: Sun Jun 05, 2011 1:44 pm
Location: Northants, UK

HTPC Specs: Show details

#2

Post by holidayboy » Sat Apr 13, 2013 8:55 am

Great post :thumbup:

Could be very useful.
Rob.

TGB.tv - the one stop shop for the more discerning Media Center user.

toshfive

Posts: 34
Joined: Fri Dec 23, 2011 4:05 pm
Location:

HTPC Specs: Show details

#3

Post by toshfive » Sat May 04, 2013 5:36 am

excellent!!!

good to see people thinking outside the box...sorta.

niczoom

Posts: 15
Joined: Sun Feb 07, 2016 8:51 am
Location:

HTPC Specs: Show details

#4

Post by niczoom » Tue Feb 09, 2016 1:31 am

Great post! I read this a couple of years ago and implemented in my Win 7 HTPC, worked great.
Now I have a new Win 10 system running, I went to implement this again but im getting 'Wake Source: Unknown' !!

Now I can just test for 'Nothing' as the variable WakeSourceText = "" or just test WakeSourceTextLength for 0. Apart from WMC waking for a recording there is really not much else to wake the PC for without the need to turn the TV on, which is my reason to use this test.

Ive read around a few forums but have not come up with why? In the bios i have setup the keyboard and mouse to wake the system which works.

Any ideas why im getting 'unknown' ?? instead of the usb hub info from which the system has been woken from.

Post Reply