{"id":13611,"date":"2011-06-16T00:01:00","date_gmt":"2011-06-16T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/06\/16\/use-asynchronous-event-handling-in-powershell\/"},"modified":"2022-11-03T03:25:27","modified_gmt":"2022-11-03T10:25:27","slug":"use-asynchronous-event-handling-in-powershell","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-asynchronous-event-handling-in-powershell\/","title":{"rendered":"Use Asynchronous Event Handling in PowerShell"},"content":{"rendered":"<p><strong>Summary<\/strong>: Bruce Payette shows how to use asynchronous event handling in Windows PowerShell.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, here. I am really excited about the idea I had for this week, and I hope you will be too. I asked Candace Gillhoolley at Manning Press about posting some sample works from some of the Manning Press library of books. She responded enthusiastically and shared five samples that we will post this week. Today is part one of two parts from Bruce Payette and <em>Windows PowerShell in Action<\/em>.<\/p>\n<h2><a target=\"_blank\" href=\"http:\/\/www.manning.com\/payette2\/\" rel=\"noopener\">Windows PowerShell in Action, Second Edition<\/a><\/h2>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2867.hsg-6-16-11-1_0FA45293.jpg\"><img decoding=\"async\" height=\"193\" width=\"154\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8228.hsg-6-16-11-1_thumb_16575C16.jpg\" alt=\"Image of book cover\" border=\"0\" title=\"Image of book cover\" style=\"padding-left: 0px;padding-right: 0px;padding-top: 0px;border: 0px\" \/><\/a><\/p>\n<p>By Bruce Payette<\/p>\n<p>The key difference between event-based scripting and traditional procedural scripting is that, instead of an activity being executed as a result of an action in the script, a script (or at least a portion of it) is executed as a result of an action by the system.. In this article based on chapter 20 of <a target=\"_blank\" href=\"http:\/\/www.manning.com\/payette2\/\" rel=\"noopener\">Windows PowerShell in Action, Second Edition<\/a>, author Bruce Payette discusses asynchronous event-handling models in PowerShell. To save 35% on your next purchase use Promotional Code <strong>payette22035<\/strong> when you check out at <a target=\"_blank\" href=\"https:\/\/www.manning.com\/\" rel=\"noopener\">www.manning.com<\/a>.<\/p>\n<p><strong><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5417.manning_534AC468.png\"><img decoding=\"async\" height=\"26\" width=\"154\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8535.manning_thumb_7A18DAA8.png\" alt=\"manning\" border=\"0\" title=\"manning\" style=\"padding-left: 0px;padding-right: 0px;padding-top: 0px;border-width: 0px\" \/><\/a><\/strong><\/p>\n<h2><strong>Asynchronous Event Handling****\u00a0<\/strong><\/h2>\n<p>The key difference between event-based scripting and traditional procedural scripting is that, instead of an activity being executed as a result of an action in the script, a script (or at least a portion of it) is executed as a result of an action by the system. This pattern is sometimes called inversion of control, but it can be expressed more colorfully as \u201cDon\u2019t call me, I\u2019ll call you.\u201d<\/p>\n<p>**NOTE **This way of characterizing event-based programming captures the essence of the model perfectly. Crispin Cowan (Linux Security and now Windows Security Guru Extraordinaire) suggested this definition as we were hiking through the Cougar Mountains in Washington. Clearly, inspiration can arrive anywhere.<\/p>\n<p>The traditional and event-driven flow control patterns are shown in figure 1.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3022.hsg-6-16-11-2_5621429B.jpg\"><img decoding=\"async\" height=\"391\" width=\"404\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8637.hsg-6-16-11-2_thumb_67FDD668.jpg\" alt=\"Image of flow chart\" border=\"0\" title=\"Image of flow chart\" style=\"padding-left: 0px;padding-right: 0px;padding-top: 0px;border: 0px\" \/><\/a><\/p>\n<p><strong>Figure 1<\/strong> The normal flow of control in a script is compared to the flow in an event-based script. In the normal flow of control, the main thread of execution always retains control, calling library routines as needed. In event-based programming, the mainline registers a set of callback actions that will be executed when the specified event occurs. The event service then controls the flow of execution.<\/p>\n<p>Take a look at the traditional flow of control illustrated in the figure. In the traditional model, the flow of control always belongs to the mainline of the program. If an action is required, the mainline program directly invokes that action. In contrast, with the eventing pattern, rather than directly initiating actions, the mainline program registers the set of actions with an event source and then goes to sleep. It never initiates any actions on its own. Instead, the event source is responsible for initiating actions as required. In this scenario, you are, in effect, turning control over to the event service.<\/p>\n<p>Sometimes this event service is a library routine that the mainline calls and allows this library to handle dispatching events to the callbacks. This model is frequently used in GUI programming.<\/p>\n<p><strong>NOTE **In practice, we\u2019ve been using this callback pattern all along, not just in GUIs. This is how the **ForEach-Object<\/strong> and <strong>Where-Object<\/strong> cmdlets work: you pass action scriptblocks to the cmdlets and the cmdlets take care of calling your code when it\u2019s needed.<\/p>\n<p>In other situations, the event service may be an active entity like another thread or process. In practice, programs rarely restrict themselves to a single model but instead use different models at different times as appropriate. We\u2019ll explore these models in more detail and you\u2019ll see how to apply them in PowerShell.<\/p>\n<h3>Asynchronous events<\/h3>\n<p>Asynchronous events are much trickier to deal with than their synchronous cousins. A synchronous event effectively runs on the same thread of execution as everything else. By analogy, this is like attending a formal lecture where the speaker conducts the main thread of conversation but periodically takes questions from the audience. By following this synchronous question-and-answer policy, at no point are there ever two actions (that is, two conversations) occurring at the same. This makes following the flow of conversation much easier. Everything happens deterministically, eliminating any collisions or consistency\/coherency issues. Unfortunately that model doesn\u2019t match the way much of the real world works. Real-world events don\u2019t occur in a strict deterministic order\u2014they happen when they happen, interrupting whatever else might be going on at that time. In the lecture analogy, this is like the audience spontaneously yelling out questions, interrupting the speaker, and possibly confusing everyone. This type of concurrent operation makes life difficult for scripters because it means that things may possibly get changed out of order or in unanticipated ways, resulting in inconsistencies and errors.<\/p>\n<p>In Windows PowerShell v1, there was no support for the asynchronous pattern, which made it pretty much impossible to handle asynchronous events. In fact, out of concern over the possibility that things might happen out of order, PowerShell actively checks to see if you\u2019re trying to perform asynchronous actions and shuts down (that is, calls the <strong>FailFast()<\/strong> API, causing a crash) if it detects them.<\/p>\n<p>**NOTE **The rationale behind this behavior was, essentially, that it\u2019s better to be absolutely useless than to be possibly wrong. This is a somewhat extreme view and not everyone agrees with this line of reasoning. On the other hand, crashing and thereby halting an operation rather than, say, possibly launching a rocket at the wrong target does make a certain amount of sense. You can\u2019t un-launch a rocket. Saying \u201cSorry, my bad\u201d after blowing up a city just doesn\u2019t cover it.<\/p>\n<p>To allow for robust handling of asynchronous events, Windows PowerShell v2 added an eventing subsystem that uses a centralized event manager to ensure that this occurs in a rational sequence. This subsystem takes care of all the bookkeeping and synchronization needed to ensure a stable and consistent system without a lot of work on the part of the script author. In the next section, we\u2019ll introduce the model PowerShell uses for doing this.<\/p>\n<h3>Subscriptions, registrations, and actions<\/h3>\n<p>The scripting model PowerShell uses for handling asynchronous events involves a few core concepts. The first concept is the idea of an event subscription, where you select the type of events you want to know about and then subscribe to be notified when they occur. These subscriptions are registered with a source identifier, which allows you to give a friendly name to each subscription. Once registered, the event subscription will be notified about relevant events as soon as they occur and will continue to receive notifications until the subscription is cancelled by explicitly unregistering it.<\/p>\n<p>Each event subscription may optionally specify an action to be taken. With these concepts in mind, we\u2019ll look at the eventing cmdlets in the next section.<\/p>\n<h3>The eventing cmdlets<\/h3>\n<p>The PowerShell eventing cmdlets are shown in table 1. These cmdlets allow you to register and unregister event subscriptions and list the existing subscriptions. You can also list pending events (as opposed to subscriptions) and handle or remove them as desired. There is also a cmdlet that allows scripts to generate their own events.<\/p>\n<p><strong>Table 1<\/strong> The PowerShell eventing cmdlets<\/p>\n<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n<tbody>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          <strong>Cmdlet name<\/strong>\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          <strong>Description<\/strong>\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          Register-ObjectEvent\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          This cmdlet registers an event subscription for events generated by .NET objects.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          Register-WmiEvent\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          Registers an event subscription for events generated by WMI objects.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          Register-EngineEvent\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          Registers an event subscription for events generated by PowerShell itself.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          Get-EventSubscriber\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          Gets a list of the registered event subscriptions in the session.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          Unregister-Event\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          Removes one or more of the registered event subscriptions.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          Wait-Event\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          Waits for an event to occur. This cmdlet can wait for a specific event or any event. It also allows a timeout to be specified, limiting how long it will wait for the event. The default is to wait forever.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          Get-Event\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          Gets pending unhandled events from the event queue.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          Remove-Event\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          Removes a pending event from the event queue.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"163\" valign=\"top\">\n<p>\n          New-Event\n        <\/p>\n<\/td>\n<td width=\"468\" valign=\"top\">\n<p>\n          This cmdlet is called in a script to allow the script to add its own events to the event queue.\n        <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>When handling events, you need to be able to register actions in response to these events. You do so using cmdlets but, because there are several types or sources of events, there are also several event registration cmdlets, as you saw in the table. The three event subscription registration cmdlets are <strong>Register-EngineEvent<\/strong>, <strong>Register-ObjectEvent<\/strong>, and <strong>Register-WmiEvent<\/strong>. PowerShell-specific events are handled using the <strong>Register-EngineEvent<\/strong> cmdlet, asynchronous events on .NET objects are handled using <strong>Register-ObjectEvent<\/strong>, and WMI events are addressed with <strong>Register-WmiEvent<\/strong>.<\/p>\n<p>Next, we\u2019ll focus on .NET events\u2014the so-called object events. In the process of doing this, we\u2019ll also cover most of the core eventing concepts that apply when working with any of the event sources.<\/p>\n<h3>Working with asynchronous .NET events<\/h3>\n<p>You use the <strong>Register-ObjectEvent<\/strong> cmdlet to create subscriptions for asynchronous events on .NET objects. The signature for this cmdlet is shown in figure 2.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5008.hsg-6-16-11-3_5C681929.jpg\"><img decoding=\"async\" height=\"204\" width=\"448\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7635.hsg-6-16-11-3_thumb_6E44ACF6.jpg\" alt=\"Image of cmdlet signature\" border=\"0\" title=\"Image of cmdlet signature\" style=\"padding-left: 0px;padding-right: 0px;padding-top: 0px;border: 0px\" \/><\/a><\/p>\n<p><strong>Figure 2<\/strong> The signature of the Register-ObjectEvent cmdlet. This cmdlet is used to set up event handling for asynchronous events generated by .NET objects.<\/p>\n<p>Let\u2019s see how these parameters are used. First you need to identify the event you\u2019re interested in. For .NET events, this means that you need an object and the name of the event member on that object to bind. This is the same pattern you\u2019ve already seen with Windows Forms and WPF, where, for example, a Button object has a <strong>Click<\/strong> event accessed through the <strong>add_Click()<\/strong> member.<\/p>\n<p>Once you\u2019ve decided on the event to handle, you need to specify what to do with the event. The <strong>-Action<\/strong> parameter on the cmdlet allows you to provide a scriptblock to execute when an event fires. This scriptblock will receive a lot of information about the event when it\u2019s run, but there may be some additional, custom data that you want to pass to the event handler. You can do this with the <strong>MessageData<\/strong> parameter.<\/p>\n<p>Finally, when you have a number of events that you\u2019re working with, the ability to attach a friendly name to the subscription will make things easier to manage. This is what <strong>-SourceIdentifier<\/strong> is for: it allows you to name the event registration or event source.<\/p>\n<p>There\u2019s one last parameter that we haven\u2019t discussed yet: <strong>-SupportEvent<\/strong>. In larger event-driven scripts, there may be a number of event registrations that only exist to support higher-level constructs within the application. In these scenarios, it\u2019s useful to be able to hide these supporting events much like the rationale behind the way you hide supporting functions in modules. This event-handler hiding is accomplished using the <strong>-SupportEvent<\/strong> switch. As in the case of modules, if you do want to see the hidden events, you can specify the <strong>-Force<\/strong> switch on <strong>Get-EventSubscriber<\/strong>.<\/p>\n<h3>Writing a timer event handler<\/h3>\n<p>Okay, enough talk\u2014let\u2019s start doing something with .NET events. One of the most obvious examples of an asynchronous event is a timer. A timer event fires at regular intervals regardless of what else is going on. Let\u2019s see how you can set up a subscription events generated by the .NET <strong>System.Timers.Timer<\/strong> class.<\/p>\n<p>**NOTE **These cmdlets can *only *be used for asynchronous .NET events. It\u2019s not possible to set up event handlers for synchronous events using the PowerShell eventing cmdlets. This is because synchronous events all execute on the same thread and the cmdlets expect (require) that the events will happen on another thread. Without the second thread, the PowerShell engine will simply block the main thread and nothing will ever get executed.<\/p>\n<h4><strong>Creating the Timer object<\/strong><\/h4>\n<p>The first thing you need for our example is a <strong>Timer **object. You use **New-Object<\/strong> to create it:<\/p>\n<pre><code>PS (1) > $timer = New-Object System.Timers.Timer ><\/code><\/pre>\n<p>Because events are first-class and exist as members on a class, you can use <strong>Get-Member<\/strong>, filtering the results on the **Event **member type, to see what events this object exposes:<\/p>\n<pre><code>\nPS (2) > $timer | Get-Member -MemberType Event >\n  >\n   TypeName: System.Timers.Timer >\n  >\nName      MemberType    Definition >\n----       ----------    ---------- >\nDisposed  Event         System.EventHandler Disposed(System.Objec... >\nElapsed   Event         System.Timers.ElapsedEventHandler Elapsed... >\n<\/code><\/pre>\n<p>From this output, you can see that the Elapsed event is what you\u2019re looking for\u2014it fires when the timer period has elapsed.<\/p>\n<h4><strong>Setting the timer event parameters<\/strong><\/h4>\n<p>But, you need to know more about this object than just the events\u2014you need to know how to set the timer interval, and start and stop the timer. Again you can use <strong>Get-Member<\/strong> to find this information. (Note that the output shown here has been trimmed to the interesting members for brevity\u2019s sake.)<\/p>\n<pre><code>\nPS (3) > $timer | Get-Member >\n  >\n   TypeName: System.Timers.Timer >\n  >\nName      MemberType    Definition >\n----      ----------    ---------- >\nDisposed  Event         System.EventHandler Disp... >\nElapsed   Event         System.Timers.ElapsedEve... >\nClose     Method        System.Void Close() >\nStart     Method        System.Void Start() >\nStop      Method        System.Void Stop() >\nToString  Method        string ToString() >\nAutoReset Property      System.Boolean AutoReset... >\nEnabled   Property      System.Boolean Enabled {... >\nInterval  Property      System.Double Interval {... >\n<\/code><\/pre>\n<p>When you look at the output, the way to start and stop the timer is obvious. The <strong>AutoReset<\/strong> property determines if the timer only fires once (<strong>AutoReset<\/strong> = $false) or fires repeatedly every interval (<strong>AutoReset<\/strong> = $true). Finally, the <strong>Interval<\/strong> property controls the firing interval. Because the value is a double, you can guess that it\u2019s specified in milliseconds.<\/p>\n<p><strong>NOTE **Yes, you could\u2019ve gone to the MSDN documentation. But, really, why bother? With Get-Member and a reasonably decent understanding of .NET, **Get-Member<\/strong> is frequently all you need. This makes PowerShell a useful tool for developers as well as IT professionals. Even in Visual Studio, sometimes we\u2019ll still flip over to a PowerShell window to search for information about a type. Simple text and typing is still faster sometimes.<\/p>\n<h4><strong>Binding the event action<\/strong><\/h4>\n<p>Let\u2019s register for an event on this object, which you do with the following command:<\/p>\n<pre><code>\nPS (4) > Register-ObjectEvent -InputObject $timer ` >\n>> -EventName Elapsed -Action { Write-Host \"\" } >\n  >\nId             Name             State      HasMoreData      Location >\n--             ----             -----      -----------      -------- >\n2              605793a1-1af...  NotStarted False >\n<\/code><\/pre>\n<p>This command attaches a scriptblock to the event that will write out the phrase &#8220;<timer>&#8221; when it fires. You have to use <strong>Write-Host<\/strong> in this scriptblock because the output from a triggered event action is simply discarded.<\/timer><\/p>\n<p>Now you\u2019ll wait a minute\u2026and\u2026nothing happens. This is because you haven\u2019t done all of the other things to the <strong>Timer<\/strong> object to make it start firing (though obviously, binding the event handler beforehand is usually a good idea).<\/p>\n<h4><strong>Enabling the event<\/strong><\/h4>\n<p>Let\u2019s complete the remaining steps needed to start the timer triggering. Set the interval to 500 milliseconds so the timer will fire in half a second:<\/p>\n<pre><code>PS (5) > $timer.Interval = 500 ><\/code><\/pre>\n<p>You want to fire repeatedly, so set the <strong>AutoReset<\/strong> property to $true:<\/p>\n<pre><code>PS (6) > $timer.AutoReset = $true ><\/code><\/pre>\n<p>Next you enable the timer by setting the <strong>Enabled<\/strong> property to $true (or by calling the <strong>Start()<\/strong> method, which also sets Enabled to $true):<\/p>\n<pre><code>PS (7) > $timer.Enabled = $true<\/code><\/pre>\n<p>The timer starts running and you see the output you expected. Next comes the hard part: getting it to stop. The command is easy; just type **$timer.Stop() **and press ENTER. But in the console shell, the timer is writing to the screen at the same time you\u2019re typing. This results in scrambled output, looking something like this:<\/p>\n<pre><code>$timer.Elapsed = { Write-Host \"\" } ><\/code><\/pre>\n<h3>Using Register-ObjectEvent<\/h3>\n<p>As a handy way to remember how to use the <strong>Register-ObjectEvent<\/strong> cmdlet, think of assigning the scriptblock to the event member. If PowerShell supported this, it\u2019d look something like this:<\/p>\n<pre><code>$timer.Elapsed = { Write-Host \"<timer>\" }<\/code><\/pre>\n<p>The <strong>Register-ObjectEvent<\/strong> command allows positional parameters in the same order, so the command would look like<\/p>\n<pre><code>Register-ObjectEvent $timer Elapsed { Write-Host \"<timer2>\" }<\/code><\/pre>\n<p>where the order of the elements is the same: object\/member\/action.<\/p>\n<p>(Here\u2019s another place where the ISE just works better\u2014the timer output doesn\u2019t interfere with the ability to run commands.) Once you\u2019ve stopped the timer, you can restart it by calling the <strong>Start()<\/strong> method a second time:<\/p>\n<pre><code>\nPS (9) > $timer.Start() >\nPS (10) > >\n>\n>\n>\n$timer.Stop() >\n  >\nPS (12) > >\n<\/code><\/pre>\n<p>Now that you know how to register a basic event subscription, we\u2019ll look at how to manage these subscriptions. Come back tomorrow as Bruce continues and shows us how to manage subscriptions. Thank you, Bruce.<\/p>\n<p>I invite you to follow me on <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingguystwitter\" rel=\"noopener\">Twitter<\/a> Facebook. If you have any questions, send email to me at <a href=\"&#109;&#97;&#x69;&#x6c;&#116;&#111;&#x3a;&#x73;c&#114;&#105;&#x70;&#x74;&#101;&#114;&#x40;&#x6d;&#105;&#99;&#x72;&#x6f;s&#111;&#102;&#x74;&#x2e;&#99;&#111;&#x6d;\">&#x73;c&#114;&#105;&#x70;&#x74;&#101;&#114;&#x40;&#x6d;&#105;&#99;&#x72;&#x6f;s&#111;&#102;&#x74;&#x2e;&#99;&#111;&#x6d;<\/a>, or post your questions on the <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingforum\" rel=\"noopener\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><strong>Ed Wilson, Microsoft Scripting Guy<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Bruce Payette shows how to use asynchronous event handling in Windows PowerShell. Microsoft Scripting Guy, Ed Wilson, here. I am really excited about the idea I had for this week, and I hope you will be too. I asked Candace Gillhoolley at Manning Press about posting some sample works from some of the Manning [&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":[266,42,56,3,4,45],"class_list":["post-13611","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-bruce-payette","tag-events-and-monitoring","tag-guest-blogger","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Bruce Payette shows how to use asynchronous event handling in Windows PowerShell. Microsoft Scripting Guy, Ed Wilson, here. I am really excited about the idea I had for this week, and I hope you will be too. I asked Candace Gillhoolley at Manning Press about posting some sample works from some of the Manning [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/13611","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=13611"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/13611\/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=13611"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=13611"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=13611"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}