Hey, Scripting Guy! Question

Hey, Scripting Guy! I need some help. There is a stupid process that launches every time I turn on my computer. I cannot find where it is starting from. I have looked in all the usual places, but I can't find it. This process was installed when I installed some software that I use only about once a month. I do not need this dumb process starting every single time I turn on my computer, and to make matters worse, every time I go into Task Manager and stop the process, within a few minutes the dumb thing has restarted itself. What I need is a script that will kill the process and keep on killing the process until it fails to restart. Can you help me?

- ES

SpacerHey, Scripting Guy! Answer

Hi ES,

I certainly sympathize with you. It seems that lately, every piece of software you install wants to include some kind of autoupdate service, or some quick launch feature that loads half the program files into memory so that the program will load faster when you click the icon. As you pointed out, this is really inefficient behavior for a program you only use once a month and it would be nice if the setup routine would offer you the choice. Because your program obviously did not ask you if you want a memory wasting process to run all the time, and because you cannot find where the devious thing is hiding, we can use a WMI event query to monitor for the existence of the program, and if it rears its ugly head from within your hard disk platters and attempts to byte your memory, we can stop the process. (Windows PowerShell is much friendlier than other languages. We do not kill processes, nor do we terminate them; we simply stop the process. Unfortunately, there is no rehabilitate-process cmdlet.)

This week we are 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 (MonitorProcessStartUpByNameLoopAndKill.ps1) monitors for the presence of a process by process name. If the process is detected, it calls the Stop-Process cmdlet and stops the process. It does this in a continuous loop. The script is seen here:

Clear-Host
$Start = get-date
$ProcName = "notepad"
$Proc = $null
$TimeOutValue = 10
Write-Host "Waiting for $ProcName to be created ... 
You will be notified within 10 seconds of  process  creation
 start time was $Start"
$Query = "select * from __instanceCreationEvent within $TimeOutValue 
          where targetInstance isa 'win32_Process' AND 
  targetInstance.name = '"+ $ProcName + ".exe'"

$Eventwatcher = New-Object management.managementEventWatcher $Query

while($a -eq $a) #creates infinite loop. 
{
 "
  Monitoring for $ProcName ...
  Press Ctrl+C to exit loop
 "
 $Event = $Eventwatcher.waitForNextEvent()
 Stop-Process -Name $ProcName
 $Proc +=1
 "there have been $Proc instances of $ProcName killed"
 Start-Sleep -Seconds $TimeOutValue
}

The first thing we need to do is to clear the screen. To do this, we use the Clear-Host function as shown here:

Clear-Host

Then we need to assign some values to some variables. The $Start variable will be used to hold the start date and time for the script. The name process we will monitor is stored in the $ProcName variable. The $proc variable is a counter variable that will keep track of how many times the process is stopped. The $TimeOutValue variable is used to configure both time out value of the WMI event query and the amount of sleep time inside the loop that we will see later. The code that initializes all these variables is seenhere:

$Start = get-date
$ProcName = "notepad"
$Proc = $null
$TimeOutValue = 10

Next we print out a status message that lets the user know the script is doing something other than twiddling its virtual thumbs. To print out the message, we are using the Write-Host cmdlet. We do not need to use Write-Host to write to the console. We could have simply used parentheses to do that. The advantage of Write-Host is it allows us to set foreground color and background color (which we were too lazy to do here; so in this exact example, Write-Host could be deleted completely):

Write-Host "Waiting for $ProcName to be created... 
You will be notified within 10 seconds of  process creation
 start time was $Start"

We then need to create the WMI event query. In today's example, we are looking for a new instance of a process to be created. To do this, we use the __instanceCreationEvent WMI class. We use the time out value that is stored in the $TimeOutValue variable. The target instance of the __instanceCreationEvent class will be an instance of a Win32_Process WMI class that has a name that is stored in the $procName variable. The WMI event queries need the process name to be offset with single quotation marks (just like the WMI class name), and it needs the .exe extension attached as well. When we use the Stop-Process cmdlet, it needs neither of these artifacts. We decided it would be easiest to store the process name one time, and fix up the syntax required for the WMI query in the code, as shown here:

$Query = "select * from __instanceCreationEvent within $TimeOutValue 
where targetInstance isa 'win32_Process' AND targetInstance.name = '"+ $ProcName + ".exe'"

Now we need to create an instance of the System.Management.ManagementEventWatcher .NET Framework class. This .NET Framework class knows how to receive WMI events. There are several constructors for this class, but the one I like the best is the one where you give it a WMI event query. This is what we do here. We store the returned managementObject in the $EventWatcher variable. If you are wondering why I broke my rule and did not include the word System as part of the namespace designation when creating the object, it is simply because it makes the line of code way too long. In most cases (but not all cases) you can leave out the word System when referring to .NET Framework classes because the word System is generally (but not always) assumed. It is the exceptions that will sneak up and sting you like a Portuguese Man-O-War (and when that happens you will be a very long time recovering from the sting—yes, I know from experience). Here is the code that creates the ManagementEventWatcher class:

$Eventwatcher = New-Object management.managementEventWatcher $Query

When we have created our ManagementEventWatcher class, it is now time to monitor in a continuous fashion for the creation of the new process. To do this, we use a While loop, and use the condition $a -eq $a, which means that as long as $a is equal to $a, the While statement will continue to loop. Of course, $a is equal to $a. It is always going to be equal to $a. So the script will go into an infinite loop:

while($a -eq $a) #creates infinite loop.

The next thing we need to do is print another message prompt to let the user know what is happening. This alert will actually be printed to the screen each time an alert is generated as well. This is because it is inside the While loop.

Even though the message says to press Ctrl+C to exit the loop, this does not always work in testing. Usually it will exit the loop and break the script, but not always. It seems to be related to the amount of work that is being done. If this happens to you, don't panic; that is what the little X in the upper right corner of the Windows PowerShell console is for.

The code that initiates the While loop and prints the message is shown here:

{
 "
  Monitoring for $ProcName ...
  Press Ctrl+C to exit loop
 "

Now we need to simply wait for an event to occur. When the event occurs, we store the returned managementObject in the $Event variable, but we do not do anything with it. We simply detect the event and go to the next line. A better thing to do would be to capture the ProcessID of the newly created event, and then use the ProcessID in our next line that kills the process. As it is, we stop all processes with the name $procName, regardless of when they were created. This may be what you want, or you may want to kill only the newly created processes. This code is shown here:

$Event = $Eventwatcher.waitForNextEvent()
 Stop-Process -Name $ProcName

It is time to tally the results. We increment the value of $proc by one, print out a status message, and wait for the amount of time that was indicated in the $TimeOutValue variable. This is seen here:

$Proc +=1
 "there have been $Proc instances of $ProcName killed"
 Start-Sleep -Seconds $TimeOutValue
}

When we run the script and start a couple instances of Notepad, we can only use Notepad for just a few seconds before ingloriously exiting. The output of the script is seen here:

Image of the output of the script

Well, ES, I hope our script today will help you to maintain an even keel and gain a bit of control of your wayward process. This is also the conclusion to our Event Week scripts. Join us tomorrow for Quick-Hits Friday. Until then, keep cool.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys