Hey, Scripting Guy! How Can I Be Notified When a Process Begins?


Hey, Scripting Guy! Question


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

SpacerHey, Scripting Guy! Answer

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:

Image of the message printed on the screen when the script is run

 


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:

Image of the information in the notification

 


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

Comments (2)

  1. neat stuff, getting some ideas from your examples.

  2. Mark Renton says:

    Please update this article to show the "new" PowerShell cmdlet to accomplish this.

Skip to main content