{"id":54573,"date":"2009-01-20T20:58:00","date_gmt":"2009-01-20T20:58:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/01\/20\/hey-scripting-guy-how-do-i-display-a-message-and-the-time-a-process-was-terminated\/"},"modified":"2009-01-20T20:58:00","modified_gmt":"2009-01-20T20:58:00","slug":"hey-scripting-guy-how-do-i-display-a-message-and-the-time-a-process-was-terminated","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-do-i-display-a-message-and-the-time-a-process-was-terminated\/","title":{"rendered":"Hey, Scripting Guy! How Do I Display a Message and the Time a Process Was Terminated?"},"content":{"rendered":"<h2><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" height=\"34\" \/> <\/h2>\n<p>Hey, Scripting Guy! We have this application that is supposed to run all the time. It is constantly giving us headaches and crashing. The applications vendor evidently expects it to crash all the time and has a thread that will restart it three times. After that, it is pretty much down for the count. The problem is that we never know when the application was restarted or how far along we are on the &#8220;three strikes and you are out&#8221; rule. I was thinking of some kind of monitoring script that would let us know when the process was terminated, and print out the time. It does not need to run all the time; it only needs to be able to count to three (if that would make it easier). Thanks for your help. <\/p>\n<p>&#8211; RV<\/p>\n<p><img decoding=\"async\" border=\"0\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" height=\"5\" \/><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" height=\"34\" \/><\/p>\n<p>Hi RV,<\/p>\n<p>It seems that every company I have ever worked with has at least one copy of Crap App, if you follow me. It also seems that Crap App is mission critical. In many cases, the opportunity for an upgrade to Crap App is nil because the company went out of business because of their (you know what&#8217;s coming) crappy apps. <\/p>\n<table id=\"EXC\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td>\n<p class=\"lastInCell\">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 <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/events.mspx\">Hey, Scripting Guy! archive<\/a>. Additional information can be obtained from <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/guide\/sas_wmi_kzcp.mspx?mfr=true\">this Web page<\/a>. The Microsoft book, <a href=\"http:\/\/www.microsoft.com\/MSPress\/books\/authors\/auth8853.aspx\">Microsoft Windows Scripting with WMI: Self-Paced Learning Edition<\/a>, has an entire chapter on WMI eventing.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>We decided to write a script called <b>MonitorForProcessDeletionAndCount.ps1<\/b>. (No, we do not get paid by the letter for these script names, but maybe we should talk to our agent about that.) The <b>MonitorForProcessDeletionAndCount.ps1<\/b> script displays a message that it is waiting for a process to terminate, when one is detected, and it displays the name of the process and the time when it was detected. It keeps this up for three times by default. But you could easily change it to monitor for a greater number of occurrences. The <b>MonitorForProcessDeletionAndCount.ps1<\/b> script is seen&nbsp;here: <\/p>\n<pre class=\"codeSample\">Clear-Host\n$dteStart = get-date\n$total = 3\n$monitorInterval = 5\nWrite-Host \"Waiting for a process to be terminated ...\nYou will be notified within $monitorInterval seconds of process termination\nThis script will monitor for $total occurences\n        start time was $dteStart\"\n$strQuery = \"select * from __instanceDeletionEvent within $monitorInterval\n          where targetInstance isa 'win32_Process'\"\n$objEventwatcher = New-Object management.managementEventWatcher $strQuery\nfor ($i = 1; $i -le $total ; $i++)\n{\n $objEvent = $objEventwatcher.waitForNextEvent()\n \"$($objEvent.targetInstance.name) was terminated at $(Get-Date)\"\n}\n<\/pre>\n<p>The first thing we do is clear the screen of our Windows PowerShell console. We use the <b>Clear-Host<\/b> function to do this. If you have ever wondered why tab expansion does not expand the name of <b>Clear-Host<\/b>, it is because it is a function and not a cmdlet. The cool thing about a function is you can see what it is doing by using the <b>Get-Content<\/b> cmdlet and pointing to the function drive. This is illustrated here, where we display the content of the <b>Clear-Host<\/b> function. <\/p>\n<pre class=\"codeSample\">PS C:\\AutoDoc&amp;gt; Get-Content Function:\\Clear-Host\n$spaceType = [System.Management.Automation.Host.BufferCell]; $space = [System.Activator]::CreateInstance($spaceType); $\nspace.Character = ' '; $space.ForegroundColor = $host.ui.rawui.ForegroundColor; $space.BackgroundColor = $host.ui.rawui\n.BackgroundColor; $rectType = [System.Management.Automation.Host.Rectangle]; $rect = [System.Activator]::CreateInstance\n($rectType); $rect.Top = $rect.Bottom = $rect.Right = $rect.Left = -1; $Host.UI.RawUI.SetBufferContents($rect, $space);\n $coordType = [System.Management.Automation.Host.Coordinates]; $origin = [System.Activator]::CreateInstance($coordType)\n; $Host.UI.RawUI.CursorPosition = $origin; <\/pre>\n<p>To use the <b>Clear-Host<\/b> function, you just type the name, or you can even use the <b>cls<\/b> alias for the <b>Clear-Host<\/b> function. However, as a best practice, I never use an alias, even one as innocent as <b>cls<\/b>, in a script because you are never assured that it will exist. The function call is seen here: <\/p>\n<pre class=\"codeSample\">Clear-Host<\/pre>\n<p>Next, we use the <b>Get-Date<\/b> cmdlet to retrieve the date and time, and we store it in the <b>$dteStart<\/b> variable, as shown here: <\/p>\n<pre class=\"codeSample\">$dteStart = get-date<\/pre>\n<p>When we have stored the date in a variable, we next need to initialize a couple of variables. The <b>$total<\/b> variable is used to control how many loops we will make. It is inside the loops that we will detect a process that is terminated. So as a matter of practice, the <b>$total<\/b> variable is used to determine how many process termination events will be detected. The <b>$monitorInterval<\/b> variable is used to control how long WMI will wait between polling cycles. We have it set to five seconds. In real life, you should never set this value below 30 seconds; better yet, set it to several minutes. This is because of the processor load that is generated each time WMI triggers a polling cycle. You should test completely to determine if the monitoring load will topple your server. The two lines of code that create and initialize the variables are seen&nbsp;here. <\/p>\n<pre class=\"codeSample\">$total = 3\n$monitorInterval = 5\n<\/pre>\n<p>Now we need to print out a message to the users to let them know what is going on with the script. We use the <b>Write-Host<\/b> cmdlet to print out this confirmation message. The double quotation marks are expanding quotation marks, which means that variables placed inside them will be expanded to display the value contained in the variable and not print out the variable name itself. If we used literal quotation marks (single quotes), the variable names themselves would be displayed instead of printing out the values. Here is the <b>Write-Host<\/b>&nbsp;command: <\/p>\n<pre class=\"codeSample\">Write-Host \"Waiting for a process to be terminated ...\nYou will be notified within $monitorInterval seconds of process termination\nThis script will monitor for $total occurances\n        start time was $dteStart\"<\/pre>\n<p>Next we create our event query. The WMI class we are querying is the <b>__instanceDeletionEvent<\/b> class. This class allows us to determine when something goes away on our system. The <b>targetInstance<\/b> of the <b>__instanceDeletionEvent<\/b> class is a <b>win32_process<\/b>. So this means when a process goes away, we will receive an event. We could monitor for an <b>__instanceDeletionEvent<\/b> that involved other WMI classes using this same syntax. The query is shown here:<\/p>\n<pre class=\"codeSample\">$strQuery = \"Select * from __instanceDeletionEvent within $monitorInterval\n          where targetInstance isa 'win32_Process'\"\n<\/pre>\n<p>When we have created our event query, we can use the <b>New-Object<\/b> cmdlet to create a new <b>ManagementEventWatcher<\/b>. To do this, we specify the full name to the class and feed it the event query that was stored in the <b>$strQuery<\/b> variable. We use the <b>$objEventWatcher<\/b> variable to hold the returned instance of the <b>System.Management.ManagentEventWatcher<\/b> class. This is seen here: <\/p>\n<pre class=\"codeSample\">$objEventwatcher = New-Object system.management.managementEventWatcher $strQuery<\/pre>\n<p>Now we use a <b>for<\/b> loop to govern how long the script will run looking for events. The <b>for<\/b> statement is composed of three parts. The first part determines where we will start counting. The second part is used to tell us how far the loop will count, and the last position is used to increment the <b>$i<\/b> counter variable. In the example shown here, we initialize the <b>$i<\/b> variable inside the <b>for<\/b> loop. This is actually a best practice when using these types of limited scope, throw-away varivariables: <\/p>\n<pre class=\"codeSample\">for ($i = 1; $i -le $total ; $i++)\n{\n<\/pre>\n<p>Inside the for loop, we wait for the next event to arrive. To do this, we use the <b>WaitForNextEvent<\/b> method from the event watcher object. When the event arrives, we store the returned management base object in the <b>$objEvent<\/b> variable. We then print out the <b>name<\/b> property and the time the event arrived. The <b>targetinstance<\/b> property stores a copy of the <b>win32_process<\/b> class. We could therefore gain access to any of the properties from that class if we wanted to do so. In our code, shown here, we do not use the <b>Write-Host<\/b> cmdlet. We use expanding strings to print directly to the console. When we put the object in the quotation marks, it will become unraveled and will display all its properties on the line. To prevent that behavior, we use a Windows PowerShell subexpression. This causes the expression to be evaluated, and the results to be returned to the string. This is seen&nbsp;here: <\/p>\n<pre class=\"codeSample\">\n$objEvent = $objEventwatcher.waitForNextEvent()\n \"$($objEvent.targetInstance.name) was terminated at $(Get-Date)\"\n}\n<\/pre>\n<p>After the script has looped through the <b>for<\/b> loop the specified number of times, the script will exit. <\/p>\n<p>The output from the script is seen here:<\/p>\n<p><img decoding=\"async\" border=\"0\" alt=\"Image of the output of the script\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/january\/hey0120\/HSG_1_20_2009_01.jpg\" width=\"500\" height=\"409\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>RV, that is it for process monitoring. Hopefully, it will allow you to get your arms wrapped around your version of Crap App, at least until you can get it replaced or upgraded. Come back and see us tomorrow when we will continue exploring event monitoring scripts. Until then, peace. <\/p>\n<p>&nbsp;<\/p>\n<p><b>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! We have this application that is supposed to run all the time. It is constantly giving us headaches and crashing. The applications vendor evidently expects it to crash all the time and has a thread that will restart it three times. After that, it is pretty much down for the count. The [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[42,31,87,3,4,45],"class_list":["post-54573","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-events-and-monitoring","tag-operating-system","tag-processes","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! We have this application that is supposed to run all the time. It is constantly giving us headaches and crashing. The applications vendor evidently expects it to crash all the time and has a thread that will restart it three times. After that, it is pretty much down for the count. The [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54573","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=54573"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54573\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=54573"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=54573"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=54573"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}