Learn about Windows PowerShell
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
Add
Method
System.DateTime Add(TimeSpan value)
AddDays
System.DateTime AddDays(Double value)
AddHours
System.DateTime AddHours(Double value)
AddMilliseconds
System.DateTime AddMilliseconds(Double value)
AddMinutes
System.DateTime AddMinutes(Double value)
AddMonths
System.DateTime AddMonths(Int32 months)
AddSeconds
System.DateTime AddSeconds(Double value)
AddTicks
System.DateTime AddTicks(Int64 value)
AddYears
System.DateTime AddYears(Int32 value)
CompareTo
System.Int32 CompareTo(Object value), System.Int32 CompareTo(DateTime value)
Equals
System.Boolean Equals(Object value), System.Boolean Equals(DateTime value)
GetDateTimeFormats
System.String[] GetDateTimeFormats(), System.String[] GetDateTimeFormats(IFormatProvider provider), System.String[] GetDateTimeFormats(Char format), System.String[] GetDateTimeFormats(Char format, IFormatProvider provider)
GetHashCode
System.Int32 GetHashCode()
GetType
System.Type GetType()
GetTypeCode
System.TypeCode GetTypeCode()
IsDaylightSavingTime
System.Boolean IsDaylightSavingTime()
Subtract
System.TimeSpan Subtract(DateTime value), System.DateTime Subtract(TimeSpan value)
ToBinary
System.Int64 ToBinary()
ToFileTime
System.Int64 ToFileTime()
ToFileTimeUtc
System.Int64 ToFileTimeUtc()
ToLocalTime
System.DateTime ToLocalTime()
ToLongDateString
System.String ToLongDateString()
ToLongTimeString
System.String ToLongTimeString()
ToOADate
System.Double ToOADate()
ToShortDateString
System.String ToShortDateString()
ToShortTimeString
System.String ToShortTimeString()
ToString
System.String ToString(), System.String ToString(String format), System.String ToString(IFormatProvider provider), System.String ToString(String format, IFormatProvider provider)
ToUniversalTime
System.DateTime ToUniversalTime()
DisplayHint
NoteProperty
Microsoft.PowerShell.Commands.DisplayHintType DisplayHint=DateTime
Date
Property
System.DateTime Date {get;}
Day
System.Int32 Day {get;}
DayOfWeek
System.DayOfWeek DayOfWeek {get;}
DayOfYear
System.Int32 DayOfYear {get;}
Hour
System.Int32 Hour {get;}
Kind
System.DateTimeKind Kind {get;}
Millisecond
System.Int32 Millisecond {get;}
Minute
System.Int32 Minute {get;}
Month
System.Int32 Month {get;}
Second
System.Int32 Second {get;}
Ticks
System.Int64 Ticks {get;}
TimeOfDay
System.TimeSpan TimeOfDay {get;}
Year
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
neat stuff, getting some ideas from your examples.