Overview
When troubleshooting a problem you sometimes need to sit and wait for it to occur. Or there may be situations where you want to take a more proactive and plan a specific response should something happen like a mission critical service stopping or a specific process starting. In this article, I'll explain how to use a variety of tools in Windows 7 to watch for specific events and take action when they occur.
In a Windows environment, when something happens like the creation of a new file, a special thing happens referred to as an event. An event is a programmed response and the types of events vary depending on the operating system or application. You cannot program your own events, but you can wait for them and when they happen, you can react. When an event is triggered or fired, Windows or the application you might be using records it. Fortunately you don't have to be a Windows programmer to take advantage of events, although I will admit it does require some advanced knowledge and a little sophistication when it comes to scripted solutions. But I'll guide you as much as I can and leave you with some working examples to get you started.
I'll focus on some common events that you might want to catch such as a service stopping, a process starting and a file getting created.
Using the Task Scheduler
The first tool we have at our disposal is the task scheduler in Windows 7. Depending on the event you are waiting for and what action you wish to take this is a relatively simple monitoring solution. Often, when an event fires, something is written to an event log. With the task scheduler we can create a task that is triggered when the specific event is recorded. Naturally, you will need to know a few specific such as an event ID, source and log. The first thing we need to do is create a new task. You will need to create this task on the computer you want to monitor; unless you have configured event forwarding which is a topic for another day.
I suggest that you create a folder for your tasks in the Task Scheduler. Windows 7 has many scheduled tasks and it will make your life much easier. Right-Click on Task Scheduler Library in the tree pane and select New Folder. Select the folder. In the left pane, click on Create Basic task and enter a name for your task.
Figure 1
Click Next. Select the 'When a specific event is logged' option.
Figure 2
Now you will be prompted to select the event log information. Be as specific as you can. In my example I'm going to select the System event log, using Service Control Manager as the source and filtering on event id 7036.
Figure 3
The next screen is where it gets interesting. This is where you decide what you want to do when the event fires. You can start a program or script, send an email or display an interactive popup message. It is possible to create advanced tasks that will do several of these items but for now I'm just going to display a message.
Figure 4
In the next screen I'll define the message title and text.
Figure 5
The last step is merely a review, although I also have the option to open advanced properties if I want to tweak anything.
Figure 6
At the point the task is enabled. If I manually stop the Spooler service, an event is written to the log which triggers the scheduled task which displays the popup message.
Figure 7
Unfortunately, I will also get the message for any service that stops. In this particular situation I need a way to get more detailed information. If the event you are monitoring can be uniquely identified then you shouldn't need to take any other steps. But in my scenario I need to refine my task so that it only responds when the Spooler service stops. This is a little more complicated and requires a little XML knowledge.
I can either create a new advanced task or edit my existing task by opening its properties. Select the Triggers tab and edit the selected trigger. Choose Custom.
Figure 8
Click on New Event Filter. Click on XML and check the box to manually enter XML. I'm going to copy and paste this XML code into the window.
' ''' '
I don't have the space to explain how I developed this suffice it to say that I looked at the raw XML of an event in the Event Viewer. You should be able to download this code from here.
Figure 9
Click OK a few times and the change is immediate. Now the only time I get the popup is when the Spooler service stops.
Using WBEMTest
Unfortunately not every type of event you might want to monitor ends up in the event log. The other approach is to use WMI and create listeners for events. One tool you can use is WBEMTest.exe which has been available on all versions of Windows since XP. Let's look at setting up an event monitor to watch when a process starts on a computer. For the sake of this article we'll watch for new instances of CALC.EXE.
One drawback to WBEMTest is that you can't automatically execute a program or script. All you get is a notification. But this is still an excellent tool to make sure you query works. After that you can use it in either VBScript or PowerShell. I'll cover an approach using the latter in a little bit.
To get started we first need to connect to WMI on a computer. Click Start-Run and enter wbemtest.
Figure10
Click the Connect button. If you want to connect to the local computer, make sure the namespace says root\cimv2 and click Connect. If you want to connect to a remote computer, modify the namespace so that it is \\computername\root\cimv2.
Figure11
Optionally, you can enter alternate credentials for a remote computer in the format domain\username. You can leave everything else. If all goes well you should see something like Figure 12.
Figure 12
In WMI-speak, we will be creating a notification query. This type of query creates a listener that monitors WMI and when a matching event is detected, a notification is sent to the listener. The notification query is structured similarly to regular WMI queries. A typically notification query looks like this.
Select * from' Within' Where TargetInstance ISA
The event is system class. These are the common event classes:
__InstanceCreationEvent
__InstanceModificationEvent
__InstanceDeletionEvent
Be aware that the name has a double underbar leading it.
The second part of the query is the polling interval. This is how often WMI will check to see if the event occurred. This should be short enough to be useful but not so short that WMI has a constant connection. I find an interval of 5 seconds is often more than adequate. So our query thus far should look like:
Select * from __InstanceCreationEvent within 5
However this would fire for any new object written to WMI. We want to limit the query to a Win32_Process. Using a Where clause we configure the query to check the TargetInstance, the object that is created and only those that are process objects.
Select * from __InstanceCreationEvent within 5 where TargetInstance ISA 'Win32_Process'
Almost there. This version will fire when ANY new process is created, any maybe you want that. But I want to limit my query to only processes where the name is CALC.EXE. I'll modify my query using the And operator. This is my final query.
Select * from __InstanceCreationEvent within 5 where TargetInstance ISA 'Win32_Process' AND TargetInstance.Name='calc.exe'
In WBEMTest.exe I click the Notification Query button and paste in this text.
Figure 13
When I click Apply, a new window pops up waiting for an event to happen that matches my query. If I start Notepad, nothing happens. But when I start Windows Calculator, within 5 seconds I will get a result in the query window.
Figure 14
I can double click the entry to examine the WMI process object. I tend to check the box to hide system properties.
Figure 15
Double click on Target Instance. Then in the next property window click View Embedded. This will display all the properties of the process.
Figure 16
As long as you leave the query window open you'll get notifications whenever a new process starts. But as soon as you close the window, the monitoring stops. Using WBEMTest is a handy way for general event monitoring, but there is no provision for taking action. However, once you have figured out a notification query that works, you can leverage VBScript or Windows PowerShell. Based on experience I can tell you that using the former is very complicated so we'll focus on using the latter.
Using PowerShell
The advantage of using a management and automation tool like PowerShell is that you have tremendous flexibility on what action you wish to take when an event has been detected. PowerShell2.0 offers two cmdlets for managing and listening for events.
Register-WMIEvent
If you have a WMI notification query working in WBEMTest, you can use that same query in PowerShell with the Register-WMIEvent cmdlet. Let's return to our original scenario where we want to take action when the Spooler service stops. Using WBEMTest I've worked out a notification query.
Select * from __InstanceModificationEvent within 10 where targetinstance isa 'Win32_Service' AND TargetInstance.Name='spooler' AND TargetInstance.State='Stopped'"
The Register-WMIEvent cmdlet makes it very easy to setup an event subscription for a remote computer that is watched from my computer.
$query="Select * from __InstanceModificationEvent within 10 where targetinstance isa 'Win32_Service' AND TargetInstance.Name='spooler' AND TargetInstance.State='Stopped'" Register-WMIEvent -Query $query -sourceIdentifier "StoppedService" -MessageData "The Spooler Service has stopped" -computername "SERVER01"
When I execute this code, PowerShell will create an event subscriber.
PS C:\> Get-EventSubscriber SubscriptionId'' : 3 SourceObject'''' : System.Management.ManagementEventWatcher EventName''''''' : EventArrived SourceIdentifier : StoppedService Action'''''''''' : HandlerDelegate' : SupportEvent'''' : False ForwardEvent'''' : False
As long as my PowerShell session is open it will watch for events where the spooler service stops. When this happens the event is registered. Use the Get-Event cmdlet to check if any events have fired.
PS C:\> get-event ComputerName'''' : RunspaceId'''''' : 6137adf2-d8ca-4ccb-bfac-2d89f2abedcd EventIdentifier' : 2 Sender'''''''''' : System.Management.ManagementEventWatcher SourceEventArgs' : System.Management.EventArrivedEventArgs SourceArgs'''''' : {System.Management.ManagementEventWatcher, System.Management.EventArrivedEventArgs} SourceIdentifier : StoppedService TimeGenerated''' : 3/17/2011 7:43:39 PM MessageData''''' : The Spooler Service has stopped
The other way you might want to use this cmdlet is to include the 'Action parameter. This parameter takes a script block which is a PowerShell command that you want to execute, perhaps sending a message using Send-MailMessage or launching another script. The 'Action parameter will create a background job for your script block that is launched when the event fires. See cmdlet help for Register-WMIEvent for more details.
Register-ObjectEvent
PowerShell can also subscribe to events from other sources like the .NET Framework. Let's say we have a folder that we want monitor when files change. The .NET framework includes a class for exactly this type of event. We'll create this watcher class, specifying the folder to watch.
PS C:\> $watcher=[System.IO.FileSystemWatcher]("c:\work")
Next we'll use the Register-ObjectEvent cmdlet. This works very similarly to Register-WMIEvent. In my example I'm going to write information to a log file whenever a new file is created in C:\Work.
PS C:\> Register-ObjectEvent -InputObject $watcher -Eventname "Created" -SourceIdentifier "FolderChange" ` >> -MessageData "A new file was created" -Action { >> "$(Get-Date) A new file was created: $($Event.SourceEventArgs.fullpath)" >>| Out-File $env:temp\log.txt -append' } >> Id'''''' Name'''''''''' State''''' HasMoreData'''' Location'''''''' Command --'''''' ----'''''''''' -----''''' -----------'''' --------'''''''' ------- 3''''''' FolderChange'' NotStarted False'''''''''''''''''''''''''''''''' ...
The EventName parameter corresponds to the event name which in the case of the FileSystemWatcher class can be 'Created', 'Changed' or 'Deleted'.
Next, I need to enable the watcher object.
PS C:\> $watcher.EnableRaisingEvents=$True
The Get-EventSubscriber cmdlet shows me the new watcher.
PS C:\> get-eventsubscriber -SourceIdentifier FolderChange SubscriptionId'' : 4 SourceObject'''' : System.IO.FileSystemWatcher EventName''''''' : Created SourceIdentifier : FolderChange Action'''''''''' : System.Management.Automation.PSEventJob HandlerDelegate' : SupportEvent'''' : False ForwardEvent'''' : False
PowerShell will now 'watch' for any new files created in C:\Work. When a new file is created a line is added to the log file with the date and full file name.
PS C:\> get-content $env:temp\log.txt 03/17/2011 20:20:49 A new file was created: C:\work\sub5.txt
While I admit this is an advanced topic, working with events in PowerShell opens up some tremendous possibilities. The biggest challenge is likely being able to define what type of event you need to watch. But once that is worked out, you can be more responsive and even pro-active. The ad hoc approach to event monitoring that I discuss is meant to be a short-term and for the most part temporary. The event subscribers stop listening as soon as you close your PowerShell session. For ongoing monitoring and alerting, you will be better served by investing in a 3rd party management solution.
Read More About It
Working with event in PowerShell is admittedly a complex task, yet it offers tremendous opportunities. If you'd like to learn more about PowerShell and events take a look at Windows PowerShell 2.0: TFM by Don Jones and Jeffery Hicks (SAPIEN Press 2010) and Windows PowerShell in Action 2nd Edition by Bruce Payette (Manning 2011).
Comments