Hey, Scripting Guy! I need to be notified when a process begins. I would like to use Windows PowerShell to do this, but it does not seem to work. While I could do this in VBScript, I prefer to use Windows PowerShell. Can this be done?
– NL
Hi NL,
Because we have access to the .NET Framework in Windows PowerShell, it can most certainly be done. In Windows PowerShell 2.0, which just released as Community Technology Preview (CTP) 3, there is actually a cmdlet that you can use. But because a CTP is not supported, you can use the .NET Framework classes today. The advantage of this approach is that it will work with both Windows PowerShell 1.0 and Windows PowerShell 2.0, when it ships with Windows 7.
This week we will be talking about WMI eventing. For some VBScript examples of these kinds of scripts, you can refer to the eventing section of the Hey, Scripting Guy! archive. Additional information can be obtained from this Web page. The Microsoft book, Microsoft Windows Scripting with WMI: Self-Paced Learning Edition, has an entire chapter on WMI eventing. |
Today’s script, MonitorForProcessStartUp.ps1, obtains diagnostic process information when any new process starts up. Here it is:
Clear-Host $Start = get-date Write-Host "Waiting for a new process to be created ... You will be notified within 10 seconds of process creation start time was $Start" $Query = "select * from __instanceCreationEvent within 10 where targetInstance isa 'win32_Process'" $Eventwatcher = New-Object management.managementEventWatcher $Query $Event = $Eventwatcher.waitForNextEvent() $Event.targetInstance | Format-List -property [a-z]*
The first thing we want to do is to clear the screen. We do this so we can take a nice screen shot for today’s article, but it is also good for telling at a glance if your event has fired. As a best practice, you should actually prompt the user before clearing the screen. This is because someone could have data on the screen of their Windows PowerShell console, and become really enraged if you were to erase the screen. Personally, I have never seen that situation, but theoretically it could happen. To clear the screen you use the Clear-Host function as seen here:
Clear-Host
If you would like to prompt the user before clearing the screen, you could use this technique:
$response = Read-Host -prompt "Would you like to clear the screen? <yes><no>" if($response -eq "yes") { clear-host }
It is time to get the time, or the entire date as is our case here. To get a time stamp, we use the Get-Date cmdlet. The Get-Date cmdlet returns an entire System.DateTime object, which we store in the $Start variable. You may not be too excited about DateTime objects, but to me they are one of the strongest motivators for using Windows PowerShell instead of using VBScript. Why? Well, I hated date manipulation in VBScript—just a pet peeve perhaps. (Pet peeves are pretty cool. T hey do not make much noise, do not make messes, and eat very little. I have never had a pet peeve get me up in the middle of the night to take it outside either.) The members of the System.DateTime object are seen in Table 1.
Table 1 Members of the System.DateTime object
Name | Member Type | Definition |
Add |
Method |
System.DateTime Add(TimeSpan value) |
AddDays |
Method |
System.DateTime AddDays(Double value) |
AddHours |
Method |
System.DateTime AddHours(Double value) |
AddMilliseconds |
Method |
System.DateTime AddMilliseconds(Double value) |
AddMinutes |
Method |
System.DateTime AddMinutes(Double value) |
AddMonths |
Method |
System.DateTime AddMonths(Int32 months) |
AddSeconds |
Method |
System.DateTime AddSeconds(Double value) |
AddTicks |
Method |
System.DateTime AddTicks(Int64 value) |
AddYears |
Method |
System.DateTime AddYears(Int32 value) |
CompareTo |
Method |
System.Int32 CompareTo(Object value), System.Int32 CompareTo(DateTime value) |
Equals |
Method |
System.Boolean Equals(Object value), System.Boolean Equals(DateTime value) |
GetDateTimeFormats |
Method |
System.String[] GetDateTimeFormats(), System.String[] GetDateTimeFormats(IFormatProvider provider), System.String[] GetDateTimeFormats(Char format), System.String[] GetDateTimeFormats(Char format, IFormatProvider provider) |
GetHashCode |
Method |
System.Int32 GetHashCode() |
GetType |
Method |
System.Type GetType() |
GetTypeCode |
Method |
System.TypeCode GetTypeCode() |
IsDaylightSavingTime |
Method |
System.Boolean IsDaylightSavingTime() |
Subtract |
Method |
System.TimeSpan Subtract(DateTime value), System.DateTime Subtract(TimeSpan value) |
ToBinary |
Method |
System.Int64 ToBinary() |
ToFileTime |
Method |
System.Int64 ToFileTime() |
ToFileTimeUtc |
Method |
System.Int64 ToFileTimeUtc() |
ToLocalTime |
Method |
System.DateTime ToLocalTime() |
ToLongDateString |
Method |
System.String ToLongDateString() |
ToLongTimeString |
Method |
System.String ToLongTimeString() |
ToOADate |
Method |
System.Double ToOADate() |
ToShortDateString |
Method |
System.String ToShortDateString() |
ToShortTimeString |
Method |
System.String ToShortTimeString() |
ToString |
Method |
System.String ToString(), System.String ToString(String format), System.String ToString(IFormatProvider provider), System.String ToString(String format, IFormatProvider provider) |
ToUniversalTime |
Method |
System.DateTime ToUniversalTime() |
DisplayHint |
NoteProperty |
Microsoft.PowerShell.Commands.DisplayHintType DisplayHint=DateTime |
Date |
Property |
System.DateTime Date {get;} |
Day |
Property |
System.Int32 Day {get;} |
DayOfWeek |
Property |
System.DayOfWeek DayOfWeek {get;} |
DayOfYear |
Property |
System.Int32 DayOfYear {get;} |
Hour |
Property |
System.Int32 Hour {get;} |
Kind |
Property |
System.DateTimeKind Kind {get;} |
Millisecond |
Property |
System.Int32 Millisecond {get;} |
Minute |
Property |
System.Int32 Minute {get;} |
Month |
Property |
System.Int32 Month {get;} |
Second |
Property |
System.Int32 Second {get;} |
Ticks |
Property |
System.Int64 Ticks {get;} |
TimeOfDay |
Property |
System.TimeSpan TimeOfDay {get;} |
Year |
Property |
System.Int32 Year {get;} |
DateTime |
ScriptProperty |
System.Object DateTime {get=if ($this.DisplayHint -ieq Date) { {0} -f $this.ToLongDateString() } elseif ($this.DisplayHint -ieq Time) { {0} -f $this.ToLongTimeString() } else { {0} {1} -f $this.ToLongDateString(), $this.ToLongTimeString() };} |
To store the start date in the $Start variable, we use this code:
$Start = get-date
We now need to prompt the user to let them know that the script is actually working. We can use the Write-Host cmdlet to do this. We use an expanding string, which allows us to use the datetime value stored in the $Start variable directly. We do not need to fool with concatenation. Strings in Windows PowerShell are assumed to continue to the next line if they are not closed, and we can break our message up across three lines without needing to use line concatenation, or even embedding carriage returns and line feeds (a la vbcrlf from VBScript fame). This is seen here:
Write-Host "Waiting for a new process to be created ... You will be notified within 10 seconds of process creation start time was $Start"
When the script is run, the message is printed on the screen along with the current starting time. This is shown here:
After we have prompted our user, we can create our event query. This uses the same query syntax familiar from VBScript event scripts. This query string is seen here:
$Query = "select * from __instanceCreationEvent within 10 where targetInstance isa 'win32_Process'"
Now we need to actually create the event watcher. To do this, we first need to create an instance of the System.Management.ManagementEventWatcher .NET Framework class. We use the New-Object cmdlet to do this. The class name is ManagementEventWatcher, and the namespace the class is found in is System.Management. Because we need to know the namespace the class resides in to be able to create an instance of the class, I make it a habit of always referring to the class by the full name. It is a bit cumbersome, but I have found it to be much more convenient than spending 15 minutes searching MSDN documentation to try to identify the namespace of a class. When we create the instance of the ManagementEventWatcher, we give it a constructor. The constructor is used to govern the way in which the class is actually created. In this example, we are giving ManagementEventWatcher the query stored in the $query parameter. This is my favorite constructor for this class. Other constructors are listed on MSDN. We store the newly minted System.Management.ManagementEventWatcher class in the $EventWatcher variable as seen here:
$Eventwatcher = New-Object system.management.managementEventWatcher $Query
Now all we need to do is to sit and wait. We are using the WaitForNextEvent method from the System.Management.ManagementEventWatcher class. When the event occurs, the returned ManagementBaseObject is stored in the $event variable as seen here:
$Event = $Eventwatcher.waitForNextEvent()
Next we use the targetInstance property to retrieve the properties of the Win32_Process class. We pipeline this to the Format-List cmdlet and print out all the properties that begin with the letters a-z and are followed by any other character. This removes the system properties that begin with the double underscore (such as __NameSpace). This is seen here:
$Event.targetInstance | Format-List -property [a-z]*
The result is all the information normally associated with a query to the Win32_Process WMI class. This is shown here:
Well, NL, we hope you enjoyed today’s article about monitoring for process startup. We will continue working with WMI events tomorrow. Until then, do not pick up any stray pet peeves you see wandering along the side of the road. See you tomorrow.
Ed Wilson and Craig Liebendorfer, Scripting Guys
0 comments