Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
EventManager.cs2654 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#pragma warning disable 1634, 16915 6using System.Collections;7using System.Collections.Generic;8using System.Diagnostics.CodeAnalysis;9using System.Linq;10using System.Management.Automation.Internal;11using System.Management.Automation.Runspaces;12using System.Reflection;13using System.Reflection.Emit;14using System.Threading;15 16namespace System.Management.Automation17{18    /// <summary>19    /// Represents the interface to the PowerShell eventing functionality.20    ///  This class allows you to subscribe to, and receive events.21    /// </summary>22    public abstract class PSEventManager23    {24        /// <summary>Sequential event ID</summary>25        private int _nextEventId = 1;26 27        /// <summary>28        /// Returns a sequential event ID.29        /// </summary>30        protected int GetNextEventId()31        {32            return _nextEventId++;33        }34 35        /// <summary>36        /// Represents the interface to the PowerShell event queue.37        /// </summary>38        public PSEventArgsCollection ReceivedEvents { get; } = new PSEventArgsCollection();39 40        /// <summary>41        /// Gets the list of event subscribers.42        /// </summary>43        public abstract List<PSEventSubscriber> Subscribers { get; }44 45        /// <summary>46        /// Creates a PowerShell event.47        /// </summary>48        /// <param name="sourceIdentifier">49        /// An optional identifier that identifies the source event50        /// </param>51        /// <param name="sender">52        /// The object that generated this event53        /// </param>54        /// <param name="args">55        /// Any event-specific data associated with the event.56        /// </param>57        /// <param name="extraData">58        /// Any additional data you wish to attach to the event59        /// </param>60        protected abstract PSEventArgs CreateEvent(string sourceIdentifier, object sender, object[] args, PSObject extraData);61 62        /// <summary>63        /// Generate a PowerShell event.64        /// </summary>65        /// <param name="sourceIdentifier">66        /// An optional identifier that identifies the source event67        /// </param>68        /// <param name="sender">69        /// The object that generated this event70        /// </param>71        /// <param name="args">72        /// Any event-specific data associated with the event.73        /// </param>74        /// <param name="extraData">75        /// Any additional data you wish to attach to the event76        /// </param>77        public PSEventArgs GenerateEvent(string sourceIdentifier, object sender, object[] args, PSObject extraData)78        {79            return this.GenerateEvent(sourceIdentifier, sender, args, extraData, false, false);80        }81 82        /// <summary>83        /// Generate a PowerShell event.84        /// </summary>85        /// <param name="sourceIdentifier">86        /// An optional identifier that identifies the source event87        /// </param>88        /// <param name="sender">89        /// The object that generated this event90        /// </param>91        /// <param name="args">92        /// Any event-specific data associated with the event.93        /// </param>94        /// <param name="extraData">95        /// Any additional data you wish to attach to the event96        /// </param>97        /// <param name="processInCurrentThread">98        /// True if the event should be triggered in current thread. False for the event99        /// to be triggered in a separate thread.100        /// </param>101        /// <param name="waitForCompletionInCurrentThread">102        /// Wait for the event and associated action to be processed and completed.103        /// </param>104        public PSEventArgs GenerateEvent(string sourceIdentifier, object sender, object[] args, PSObject extraData,105            bool processInCurrentThread, bool waitForCompletionInCurrentThread)106        {107            PSEventArgs newEvent = CreateEvent(sourceIdentifier, sender, args, extraData);108            ProcessNewEvent(newEvent, processInCurrentThread, waitForCompletionInCurrentThread);109 110            return newEvent;111        }112 113        /// <summary>114        /// Adds a forwarded event to the current event manager.115        /// </summary>116        internal abstract void AddForwardedEvent(PSEventArgs forwardedEvent);117 118        /// <summary>119        /// Processes new events (which have either been generated by this instance or forwarded to it)120        /// </summary>121        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "InCurrent")]122        protected abstract void ProcessNewEvent(PSEventArgs newEvent, bool processInCurrentThread);123 124        /// <summary>125        /// Processes new events (which have either been generated by this instance or forwarded to it)126        /// </summary>127        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "InCurrent")]128        protected internal virtual void ProcessNewEvent(PSEventArgs newEvent, bool processInCurrentThread,129                                                         bool waitForCompletionWhenInCurrentThread)130        {131            throw new NotImplementedException();132        }133 134        /// <summary>135        /// Get the event subscription that corresponds to an identifier136        /// </summary>137        /// <param name="sourceIdentifier">138        /// The identifier that identifies the source of the events139        /// </param>140        public abstract IEnumerable<PSEventSubscriber> GetEventSubscribers(string sourceIdentifier);141 142        /// <summary>143        /// Subscribes to an event on an object.144        /// </summary>145        /// <param name="source">146        /// The source object that defines the event147        /// </param>148        /// <param name="eventName">149        /// The event to subscribe150        /// </param>151        /// <param name="sourceIdentifier">152        /// An optional subscription identifier to help identify this event subscription153        /// </param>154        /// <param name="data">155        /// Any additional data you wish to attach to the event156        /// </param>157        /// <param name="action">158        /// Any action to be invoked when the event arrives159        /// </param>160        /// <param name="supportEvent">161        /// Any action to be invoked when the event arrives162        /// </param>163        /// <param name="forwardEvent">164        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions165        /// </param>166        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]167        public abstract PSEventSubscriber SubscribeEvent(object source, string eventName, string sourceIdentifier, PSObject data, ScriptBlock action, bool supportEvent, bool forwardEvent);168 169        /// <summary>170        /// Subscribes to an event on an object.171        /// </summary>172        /// <param name="source">173        /// The source object that defines the event174        /// </param>175        /// <param name="eventName">176        /// The event to subscribe177        /// </param>178        /// <param name="sourceIdentifier">179        /// An optional subscription identifier to help identify this event subscription180        /// </param>181        /// <param name="data">182        /// Any additional data you wish to attach to the event183        /// </param>184        /// <param name="action">185        /// Any action to be invoked when the event arrives186        /// </param>187        /// <param name="supportEvent">188        /// Any action to be invoked when the event arrives189        /// </param>190        /// <param name="forwardEvent">191        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions192        /// </param>193        /// <param name="maxTriggerCount">194        /// Indicate how many times the subscriber should be triggered before auto-unregister it195        /// If the value is equal or less than zero, there is no limit on the number of times the event can be triggered without being unregistered196        /// </param>197        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]198        public abstract PSEventSubscriber SubscribeEvent(object source, string eventName, string sourceIdentifier, PSObject data, ScriptBlock action, bool supportEvent, bool forwardEvent, int maxTriggerCount);199 200        /// <summary>201        /// Subscribes to an event on an object.202        /// </summary>203        /// <param name="source">204        /// The source object that defines the event205        /// </param>206        /// <param name="eventName">207        /// The event to subscribe208        /// </param>209        /// <param name="sourceIdentifier">210        /// An optional subscription identifier to help identify this event subscription211        /// </param>212        /// <param name="data">213        /// Any additional data you wish to attach to the event214        /// </param>215        /// <param name="handlerDelegate">216        /// Any action to be invoked when the event arrives217        /// </param>218        /// <param name="supportEvent">219        /// Any action to be invoked when the event arrives220        /// </param>221        /// <param name="forwardEvent">222        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions223        /// </param>224        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]225        public abstract PSEventSubscriber SubscribeEvent(object source, string eventName, string sourceIdentifier, PSObject data, PSEventReceivedEventHandler handlerDelegate, bool supportEvent, bool forwardEvent);226 227        /// <summary>228        /// Subscribes to an event on an object.229        /// </summary>230        /// <param name="source">231        /// The source object that defines the event232        /// </param>233        /// <param name="eventName">234        /// The event to subscribe235        /// </param>236        /// <param name="sourceIdentifier">237        /// An optional subscription identifier to help identify this event subscription238        /// </param>239        /// <param name="data">240        /// Any additional data you wish to attach to the event241        /// </param>242        /// <param name="handlerDelegate">243        /// Any action to be invoked when the event arrives244        /// </param>245        /// <param name="supportEvent">246        /// Any action to be invoked when the event arrives247        /// </param>248        /// <param name="forwardEvent">249        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions250        /// </param>251        /// <param name="maxTriggerCount">252        /// Indicate how many times the subscriber should be triggered before auto-unregister it253        /// If the value is equal or less than zero, there is no limit on the number of times the event can be triggered without being unregistered254        /// </param>255        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]256        public abstract PSEventSubscriber SubscribeEvent(object source, string eventName, string sourceIdentifier, PSObject data, PSEventReceivedEventHandler handlerDelegate, bool supportEvent, bool forwardEvent, int maxTriggerCount);257 258        /// <summary>259        /// Subscribes to an event on an object.260        /// </summary>261        /// <param name="source">262        /// The source object that defines the event263        /// </param>264        /// <param name="eventName">265        /// The event to subscribe266        /// </param>267        /// <param name="sourceIdentifier">268        /// An optional subscription identifier to help identify this event subscription269        /// </param>270        /// <param name="data">271        /// Any additional data you wish to attach to the event272        /// </param>273        /// <param name="handlerDelegate">274        /// Any action to be invoked when the event arrives275        /// </param>276        /// <param name="supportEvent">277        /// Any action to be invoked when the event arrives278        /// </param>279        /// <param name="forwardEvent">280        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions281        /// </param>282        /// <param name="shouldQueueAndProcessInExecutionThread">283        /// True, if the handlerDelegate should be processed in the pipeline execution thread (if possible).284        /// </param>285        /// <param name="maxTriggerCount">286        /// Indicate how many times the subscriber should be triggered before auto-unregister it287        /// If the value is equal or less than zero, there is no limit on the number of times the event can be triggered without being unregistered288        /// The default value is zero289        /// </param>290        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]291        internal virtual PSEventSubscriber SubscribeEvent(object source,292            string eventName,293            string sourceIdentifier,294            PSObject data,295            PSEventReceivedEventHandler handlerDelegate,296            bool supportEvent,297            bool forwardEvent,298            bool shouldQueueAndProcessInExecutionThread,299            int maxTriggerCount = 0)300        {301            return SubscribeEvent(source, eventName, sourceIdentifier, data, handlerDelegate, supportEvent, forwardEvent, maxTriggerCount);302        }303 304        /// <summary>305        /// Unsubscribes from an event on an object.306        /// </summary>307        /// <param name="subscriber">308        /// The subscriber associated with the event subscription309        /// </param>310        public abstract void UnsubscribeEvent(PSEventSubscriber subscriber);311 312        /// <summary>313        /// This event is raised by the event manager to forward events.314        /// </summary>315        internal abstract event EventHandler<PSEventArgs> ForwardEvent;316    }317 318    /// <summary>319    /// Implementation of the PSEventManager for local runspaces.320    /// </summary>321    internal class PSLocalEventManager : PSEventManager, IDisposable322    {323        /// <summary>324        /// Creates a new instance of the PSEventManager class.325        /// </summary>326        internal PSLocalEventManager(ExecutionContext context)327        {328            _eventSubscribers = new Dictionary<PSEventSubscriber, Delegate>();329            _engineEventSubscribers = new Dictionary<string, List<PSEventSubscriber>>(StringComparer.OrdinalIgnoreCase);330            _actionQueue = new Queue<EventAction>();331            _context = context;332        }333 334        private readonly Dictionary<PSEventSubscriber, Delegate> _eventSubscribers;335        private readonly Dictionary<string, List<PSEventSubscriber>> _engineEventSubscribers;336        private readonly Queue<EventAction> _actionQueue;337        private readonly ExecutionContext _context;338        private int _nextSubscriptionId = 1;339        private readonly double _throttleLimit = 1;340        private int _throttleChecks = 0;341 342        // The assembly and module to hold our event registrations343        private AssemblyBuilder _eventAssembly = null;344        private ModuleBuilder _eventModule = null;345        private int _typeId = 0;346 347        /// <summary>348        /// Gets the list of event subscribers.349        /// </summary>350        public override List<PSEventSubscriber> Subscribers351        {352            get353            {354                List<PSEventSubscriber> subscribers = new List<PSEventSubscriber>();355 356                lock (_eventSubscribers)357                {358                    foreach (PSEventSubscriber currentSubscriber in _eventSubscribers.Keys)359                    {360                        subscribers.Add(currentSubscriber);361                    }362                }363 364                return subscribers;365            }366        }367 368        /// <summary>369        /// Subscribes to an event on an object.370        /// </summary>371        /// <param name="source">372        /// The source object that defines the event373        /// </param>374        /// <param name="eventName">375        /// The event to subscribe376        /// </param>377        /// <param name="sourceIdentifier">378        /// An optional subscription identifier to help identify this event subscription379        /// </param>380        /// <param name="data">381        /// Any additional data you wish to attach to the event382        /// </param>383        /// <param name="action">384        /// Any action to be invoked when the event arrives385        /// </param>386        /// <param name="supportEvent">387        /// Any action to be invoked when the event arrives388        /// </param>389        /// <param name="forwardEvent">390        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions391        /// </param>392        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]393        public override PSEventSubscriber SubscribeEvent(object source, string eventName, string sourceIdentifier, PSObject data, ScriptBlock action, bool supportEvent, bool forwardEvent)394        {395            return SubscribeEvent(source, eventName, sourceIdentifier, data, action, supportEvent, forwardEvent, 0);396        }397 398        /// <summary>399        /// Subscribes to an event on an object.400        /// </summary>401        /// <param name="source">402        /// The source object that defines the event403        /// </param>404        /// <param name="eventName">405        /// The event to subscribe406        /// </param>407        /// <param name="sourceIdentifier">408        /// An optional subscription identifier to help identify this event subscription409        /// </param>410        /// <param name="data">411        /// Any additional data you wish to attach to the event412        /// </param>413        /// <param name="action">414        /// Any action to be invoked when the event arrives415        /// </param>416        /// <param name="supportEvent">417        /// Any action to be invoked when the event arrives418        /// </param>419        /// <param name="forwardEvent">420        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions421        /// </param>422        /// <param name="maxTriggerCount">423        /// Indicate how many times the subscriber should be triggered before auto-unregister it424        /// If the value is equal or less than zero, there is no limit on the number of times the event can be triggered without being unregistered425        /// </param>426        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]427        public override PSEventSubscriber SubscribeEvent(object source, string eventName, string sourceIdentifier, PSObject data, ScriptBlock action, bool supportEvent, bool forwardEvent, int maxTriggerCount)428        {429            // Record this subscriber. This may just be a registration for engine events.430            PSEventSubscriber subscriber = new PSEventSubscriber(_context, _nextSubscriptionId++, source, eventName, sourceIdentifier, action, supportEvent, forwardEvent, maxTriggerCount);431            ProcessNewSubscriber(subscriber, source, eventName, sourceIdentifier, data, supportEvent, forwardEvent);432            subscriber.RegisterJob();433 434            return subscriber;435        }436 437        /// <summary>438        /// Subscribes to an event on an object.439        /// </summary>440        /// <param name="source">441        /// The source object that defines the event442        /// </param>443        /// <param name="eventName">444        /// The event to subscribe445        /// </param>446        /// <param name="sourceIdentifier">447        /// An optional subscription identifier to help identify this event subscription448        /// </param>449        /// <param name="data">450        /// Any additional data you wish to attach to the event451        /// </param>452        /// <param name="handlerDelegate">453        /// Any action to be invoked when the event arrives454        /// </param>455        /// <param name="supportEvent">456        /// Any action to be invoked when the event arrives457        /// </param>458        /// <param name="forwardEvent">459        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions460        /// </param>461        /// <param name="shouldQueueAndProcessInExecutionThread">462        /// True, if the handlerDelegate should be processed in the pipeline execution thread (if possible).463        /// </param>464        /// <param name="maxTriggerCount">465        /// Indicate how many times the subscriber should be triggered before auto-unregister it466        /// If the value is equal or less than zero, there is no limit on the number of times the event can be triggered without being unregistered467        /// The default value is zero468        /// </param>469        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]470        internal override PSEventSubscriber SubscribeEvent(object source,471            string eventName,472            string sourceIdentifier,473            PSObject data,474            PSEventReceivedEventHandler handlerDelegate,475            bool supportEvent,476            bool forwardEvent,477            bool shouldQueueAndProcessInExecutionThread,478            int maxTriggerCount = 0)479        {480            PSEventSubscriber newSubscriber = SubscribeEvent(source, eventName, sourceIdentifier, data, handlerDelegate, supportEvent, forwardEvent, maxTriggerCount);481            newSubscriber.ShouldProcessInExecutionThread = shouldQueueAndProcessInExecutionThread;482            return newSubscriber;483        }484 485        /// <summary>486        /// Subscribes to an event on an object.487        /// </summary>488        /// <param name="source">489        /// The source object that defines the event490        /// </param>491        /// <param name="eventName">492        /// The event to subscribe493        /// </param>494        /// <param name="sourceIdentifier">495        /// An optional subscription identifier to help identify this event subscription496        /// </param>497        /// <param name="data">498        /// Any additional data you wish to attach to the event499        /// </param>500        /// <param name="handlerDelegate">501        /// Any action to be invoked when the event arrives502        /// </param>503        /// <param name="supportEvent">504        /// Any action to be invoked when the event arrives505        /// </param>506        /// <param name="forwardEvent">507        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions508        /// </param>509        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]510        public override PSEventSubscriber SubscribeEvent(object source, string eventName, string sourceIdentifier, PSObject data, PSEventReceivedEventHandler handlerDelegate, bool supportEvent, bool forwardEvent)511        {512            return SubscribeEvent(source, eventName, sourceIdentifier, data, handlerDelegate, supportEvent, forwardEvent, 0);513        }514 515        /// <summary>516        /// Subscribes to an event on an object.517        /// </summary>518        /// <param name="source">519        /// The source object that defines the event520        /// </param>521        /// <param name="eventName">522        /// The event to subscribe523        /// </param>524        /// <param name="sourceIdentifier">525        /// An optional subscription identifier to help identify this event subscription526        /// </param>527        /// <param name="data">528        /// Any additional data you wish to attach to the event529        /// </param>530        /// <param name="handlerDelegate">531        /// Any action to be invoked when the event arrives532        /// </param>533        /// <param name="supportEvent">534        /// Any action to be invoked when the event arrives535        /// </param>536        /// <param name="forwardEvent">537        /// Whether events in this subscriber should be forwarded to the client PowerShell during remote executions538        /// </param>539        /// <param name="maxTriggerCount">540        /// Indicate how many times the subscriber should be triggered before auto-unregister it541        /// If the value is equal or less than zero, there is no limit on the number of times the event can be triggered without being unregistered542        /// </param>543        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]544        public override PSEventSubscriber SubscribeEvent(object source, string eventName, string sourceIdentifier, PSObject data, PSEventReceivedEventHandler handlerDelegate, bool supportEvent, bool forwardEvent, int maxTriggerCount)545        {546            // Record this subscriber. This may just be a registration for engine events.547            PSEventSubscriber subscriber = new PSEventSubscriber(_context, _nextSubscriptionId++, source, eventName, sourceIdentifier, handlerDelegate, supportEvent, forwardEvent, maxTriggerCount);548            ProcessNewSubscriber(subscriber, source, eventName, sourceIdentifier, data, supportEvent, forwardEvent);549            subscriber.RegisterJob();550 551            return subscriber;552        }553 554        #region OnIdleProcessing555 556        private Timer _timer = null;557        private bool _timerInitialized = false;558        private bool _isTimerActive = false;559        /// <summary>560        /// We sample every 100ms to check if the engine is idle (currentlyRunningPipeline == null). If it's "idle"561        /// in four consecutive samples, then we believe it's actually idle. In this way we can avoid capturing possible562        /// pipeline transitions.563        /// </summary>564        private int _consecutiveIdleSamples = 0;565 566        /// <summary>567        /// Send on-idle event if the engine is idle. The property "AutoReset" of the timer is always false,568        /// so only one handler will be running at anytime. The timer will be enabled again if we can meet569        /// the following two conditions.570        ///   1. No PowerShell.OnIdle event is sent out571        ///   2. A PowerShell.OnIdle event is sent out, and there are still subscribers to the on-idle event.572        /// </summary>573        private void OnElapsedEvent(object source)574        {575            var localRunspace = _context.CurrentRunspace as LocalRunspace;576 577            if (localRunspace == null)578            {579                // This should never happen, the context should always reference to the local runspace580                _consecutiveIdleSamples = 0;581                return;582            }583 584            if (localRunspace.GetCurrentlyRunningPipeline() == null)585            {586                _consecutiveIdleSamples++;587            }588            else589            {590                _consecutiveIdleSamples = 0;591            }592 593            if (_consecutiveIdleSamples == 4)594            {595                _consecutiveIdleSamples = 0;596                lock (_engineEventSubscribers)597                {598                    List<PSEventSubscriber> subscribers = null;599                    if (_engineEventSubscribers.TryGetValue(PSEngineEvent.OnIdle, out subscribers) && subscribers.Count > 0)600                    {601                        // We send out on-idle event and keep enabling the timer only if there still are subscribers to the on-idle event602                        GenerateEvent(PSEngineEvent.OnIdle, null, Array.Empty<object>(), null, false, false);603                        EnableTimer();604                    }605                    else606                    {607                        _isTimerActive = false;608                    }609                }610            }611            else612            {613                EnableTimer();614            }615        }616 617        private void InitializeTimer()618        {619            try620            {621                _timer = new Timer(OnElapsedEvent, null, Timeout.Infinite, Timeout.Infinite);622            }623            catch (ObjectDisposedException)624            {625                // The PSLocalEventManager is disposed, do nothing626            }627        }628 629        private void EnableTimer()630        {631            try632            {633                _timer.Change(100, Timeout.Infinite);634            }635            catch (ObjectDisposedException)636            {637                // The PSLocalEventManager is disposed, do nothing638            }639        }640 641        #endregion OnIdleProcessing642 643        private static readonly Dictionary<string, Type> s_generatedEventHandlers = new Dictionary<string, Type>();644 645        private void ProcessNewSubscriber(PSEventSubscriber subscriber, object source, string eventName, string sourceIdentifier, PSObject data, bool supportEvent, bool forwardEvent)646        {647            Delegate handlerDelegate = null;648 649            if (_eventAssembly == null)650            {651                _eventAssembly = AssemblyBuilder.DefineDynamicAssembly(652                    new AssemblyName("PSEventHandler"),653                    AssemblyBuilderAccess.Run);654                _eventModule = _eventAssembly.DefineDynamicModule("PSGenericEventModule");655            }656 657            string engineEventSourceIdentifier = null;658            bool isOnIdleEvent = false;659            // If we are subscribing to an event on an object, generate the supporting delegate660            // for that object.661            if (source != null)662            {663                // If the identifier starts with "PowerShell.", then it will collide with engine664                // events665                if ((sourceIdentifier != null) &&666                    (sourceIdentifier.StartsWith("PowerShell.", StringComparison.OrdinalIgnoreCase)))667                {668                    string errorMessage = StringUtil.Format(EventingResources.ReservedIdentifier, sourceIdentifier);669 670                    throw new ArgumentException(errorMessage, nameof(sourceIdentifier));671                }672 673                EventInfo eventInfo = null;674                Type sourceType = source as Type ?? source.GetType();675 676                // PowerShell does not support WinRT events.677                if (WinRTHelper.IsWinRTType(sourceType))678                {679                    throw new InvalidOperationException(EventingResources.WinRTEventsNotSupported);680                }681 682                // Retrieve the event from the object683                const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.IgnoreCase;684                eventInfo = sourceType.GetEvent(eventName, bindingFlags);685 686                // If we can't find the event, throw an exception687                if (eventInfo == null)688                {689                    string errorMessage = StringUtil.Format(EventingResources.CouldNotFindEvent, eventName);690                    throw new ArgumentException(errorMessage, nameof(eventName));691                }692 693                // Try to set the EnableRaisingEvents property if it defines one694                PropertyInfo eventProperty = sourceType.GetProperty("EnableRaisingEvents");695                if (eventProperty != null && eventProperty.CanWrite)696                {697                    try698                    {699                        object targetObject = eventProperty.SetMethod.IsStatic ? null : source;700                        eventProperty.SetValue(targetObject, true);701                    }702                    catch (TargetInvocationException e)703                    {704                        if (e.InnerException != null)705                        {706                            throw e.InnerException;707                        }708                        else709                        {710                            throw;711                        }712                    }713                }714 715                // Get its invoke method, and register ourselves as a handler716                MethodInfo invokeMethod = eventInfo.EventHandlerType.GetMethod("Invoke");717 718                // We don't support non-void delegates, as the user has no way719                // to influence the result. In the .NET Framework, the only720                // events that return values are for extremely advanced scenarios:721                // System.ResolveEventHandler, and System.Reflection.ModuleResolveEventHandler.722                // For these, the Add-Type cmdlet will suffice.723                if (invokeMethod.ReturnType != typeof(void))724                {725                    string errorMessage = EventingResources.NonVoidDelegateNotSupported;726                    throw new ArgumentException(errorMessage, nameof(eventName));727                }728 729                // Cache generated event handlers (by type and event name) so that they don't bloat our730                // working set by recompiling event handlers for the same event.731 732                string eventHandlerKey = source.GetType().FullName + "|" + eventName;733                Type handlerType;734 735                lock (s_generatedEventHandlers)736                {737                    if (!s_generatedEventHandlers.TryGetValue(eventHandlerKey, out handlerType))738                    {739                        handlerType = GenerateEventHandler(invokeMethod);740                        s_generatedEventHandlers[eventHandlerKey] = handlerType;741                    }742                }743 744                // And create an instance of the type745                ConstructorInfo constructor =746                    handlerType.GetConstructor(new Type[] { typeof(PSEventManager), typeof(object), typeof(string), typeof(PSObject) });747                object handler = constructor.Invoke(new object[] { this, source, sourceIdentifier, data });748                MethodInfo eventDelegate = handlerType.GetMethod("EventDelegate", BindingFlags.Public | BindingFlags.Instance);749                handlerDelegate = eventDelegate.CreateDelegate(eventInfo.EventHandlerType, handler);750 751                eventInfo.AddEventHandler(source, handlerDelegate);752            }753            else754            {755                if (PSEngineEvent.EngineEvents.Contains(sourceIdentifier))756                {757                    engineEventSourceIdentifier = sourceIdentifier;758                    isOnIdleEvent = string.Equals(engineEventSourceIdentifier, PSEngineEvent.OnIdle, StringComparison.OrdinalIgnoreCase);759                }760            }761 762            lock (_eventSubscribers)763            {764                _eventSubscribers[subscriber] = handlerDelegate;765                if (engineEventSourceIdentifier == null)766                {767                    return;768                }769 770                lock (_engineEventSubscribers)771                {772                    if (isOnIdleEvent && !_timerInitialized)773                    {774                        InitializeTimer();775                        _timerInitialized = true;776                    }777 778                    List<PSEventSubscriber> subscribers = null;779                    if (!_engineEventSubscribers.TryGetValue(engineEventSourceIdentifier, out subscribers))780                    {781                        subscribers = new List<PSEventSubscriber>();782                        _engineEventSubscribers.Add(engineEventSourceIdentifier, subscribers);783                    }784 785                    subscribers.Add(subscriber);786 787                    // This subscriber is the only one in the idle event list, we enable the timer788                    // since the subscriber could be the first one.789                    if (isOnIdleEvent && !_isTimerActive)790                    {791                        EnableTimer();792                        _isTimerActive = true;793                    }794                }795            }796        }797 798        /// <summary>799        /// Unsubscribes from an event on an object.800        /// </summary>801        /// <param name="subscriber">802        /// The subscriber associated with the event subscription803        /// </param>804        public override void UnsubscribeEvent(PSEventSubscriber subscriber)805        {806            UnsubscribeEvent(subscriber, false);807        }808 809        /// <summary>810        /// Unsubscribes from an event on an object.811        /// </summary>812        /// <param name="subscriber">813        /// The subscriber associated with the event subscription814        /// </param>815        /// <param name="skipDraining">816        /// Indicate if we should skip draining817        /// </param>818        private void UnsubscribeEvent(PSEventSubscriber subscriber, bool skipDraining)819        {820            ArgumentNullException.ThrowIfNull(subscriber);821 822            Delegate existingSubscriber = null;823            lock (_eventSubscribers)824            {825                if (subscriber.IsBeingUnsubscribed || !_eventSubscribers.TryGetValue(subscriber, out existingSubscriber))826                {827                    // Already unsubscribed by another thread or the subscriber doesn't exist828                    return;829                }830 831                subscriber.IsBeingUnsubscribed = true;832            }833 834            if ((existingSubscriber != null) && (subscriber.SourceObject != null))835            {836                // Fire the unregistration handler837                subscriber.OnPSEventUnsubscribed(subscriber.SourceObject,838                    new PSEventUnsubscribedEventArgs(subscriber));839 840                EventInfo eventInfo = null;841 842                Type sourceType = subscriber.SourceObject as Type ?? subscriber.SourceObject.GetType();843 844                const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.IgnoreCase;845                eventInfo = sourceType.GetEvent(subscriber.EventName, bindingFlags);846 847                if ((eventInfo != null) && (existingSubscriber != null))848                {849                    eventInfo.RemoveEventHandler(subscriber.SourceObject, existingSubscriber);850                }851            }852 853            // We don't need to drain pending actions when remove an auto-unregister subscriber854            // from ProcessPendingAction method855            if (!skipDraining)856            {857                // Drain any actions pending for this subscriber858                DrainPendingActions(subscriber);859            }860 861            // Stop the job862            subscriber.Action?.NotifyJobStopped();863 864            lock (_eventSubscribers)865            {866                _eventSubscribers.Remove(subscriber);867                if (PSEngineEvent.EngineEvents.Contains(subscriber.SourceIdentifier))868                {869                    lock (_engineEventSubscribers)870                    {871                        _engineEventSubscribers[subscriber.SourceIdentifier].Remove(subscriber);872                    }873                }874            }875        }876 877        /// <summary>878        /// Creates a PowerShell event.879        /// </summary>880        /// <param name="sourceIdentifier">881        /// An optional identifier that identifies the source event882        /// </param>883        /// <param name="sender">884        /// The object that generated this event885        /// </param>886        /// <param name="args">887        /// Any event-specific data associated with the event.888        /// </param>889        /// <param name="extraData">890        /// Any additional data you wish to attach to the event891        /// </param>892        protected override PSEventArgs CreateEvent(string sourceIdentifier, object sender, object[] args, PSObject extraData)893        {894            return new PSEventArgs(null, _context.CurrentRunspace.InstanceId, GetNextEventId(), sourceIdentifier, sender, args, extraData);895        }896 897        /// <summary>898        /// Adds a forwarded event to the current event manager.899        /// </summary>900        internal override void AddForwardedEvent(PSEventArgs forwardedEvent)901        {902            forwardedEvent.EventIdentifier = GetNextEventId();903 904            ProcessNewEvent(forwardedEvent, false);905        }906 907        /// <summary>908        /// Processes new events (which have either been generated by this instance or forwarded to it)909        /// </summary>910        protected override void ProcessNewEvent(PSEventArgs newEvent, bool processInCurrentThread)911        {912            ProcessNewEvent(newEvent, processInCurrentThread, false);913        }914 915        /// <summary>916        /// Processes new events (which have either been generated by this instance or forwarded to it)917        /// </summary>918        protected internal override void ProcessNewEvent(PSEventArgs newEvent,919            bool processInCurrentThread,920            bool waitForCompletionWhenInCurrentThread)921        {922            if (processInCurrentThread)923            {924                ProcessNewEventImplementation(newEvent, true);925                ManualResetEventSlim waitHandle = newEvent.EventProcessed;926                if (waitHandle != null)927                {928                    // Win8: 738767 In Win7, the processInCurrentThread parameter was used to be929                    // called processSynchronously. Even though the parameter was called "processSynchronously",930                    // the event and associated action were not processed synchronously..the event manager931                    // just added the associated action into event queue. The action can get executed at a later932                    // time depending on the throttle checks etc. In Win8, to support routing of ScriptBlock933                    // invocation to the current runspace, we took dependency on eventing infrastructure and934                    // this required ensuring the event and associated action be processed in the current thread935                    // synchronously. The below while loop was added for that (win8: 530495). However, fix for936                    // 530495 resulted in not responding for icm | % { icm } case and dynamic event/subscriptions scenarios.937                    // To overcome that, changed "processSynchronously" parameter to "processInCurrentThread" and added938                    // a new parameter "waitForCompletionWhenInCurrentThread" to trigger blocking for ScriptBlock939                    // case.940                    while (waitForCompletionWhenInCurrentThread && !waitHandle.Wait(250))941                    {942                        this.ProcessPendingActions();943                    }944 945                    waitHandle.Dispose();946                }947            }948            else949            {950                ThreadPool.QueueUserWorkItem(new WaitCallback((_) => ProcessNewEventImplementation(newEvent, false)));951            }952        }953 954        /// <summary>955        /// Called from ProcessNewEvent to actually process the event.956        /// </summary>957        private void ProcessNewEventImplementation(PSEventArgs newEvent, bool processSynchronously)958        {959            // Get the subscriber(s) for this event960            bool capturedEvent = false;961            List<PSEventSubscriber> actionsHandledInCurrentThread = new List<PSEventSubscriber>();962            List<PSEventSubscriber> subscribersWithoutActionOrHandler = new List<PSEventSubscriber>();963            foreach (PSEventSubscriber subscriber in GetEventSubscribers(newEvent.SourceIdentifier, true))964            {965                newEvent.ForwardEvent = subscriber.ForwardEvent;966 967                // If we found a subscriber and it has an action, queue it up968                if (subscriber.Action != null)969                {970                    AddAction(new EventAction(subscriber, newEvent), processSynchronously);971                    capturedEvent = true;972                }973                else if (subscriber.HandlerDelegate != null)974                {975                    if (subscriber.ShouldProcessInExecutionThread)976                    {977                        AddAction(new EventAction(subscriber, newEvent), processSynchronously);978                    }979                    else980                    {981                        actionsHandledInCurrentThread.Add(subscriber);982                    }983 984                    capturedEvent = true;985                }986                else987                {988                    subscribersWithoutActionOrHandler.Add(subscriber);989                }990            }991 992            foreach (PSEventSubscriber subscriber in actionsHandledInCurrentThread)993            {994                subscriber.HandlerDelegate(newEvent.Sender, newEvent);995                AutoUnregisterEventIfNecessary(subscriber);996            }997 998            // Otherwise, add it to the queue of unprocessed items, unless we are forwarding the event999            if (!capturedEvent)1000            {1001                if (newEvent.ForwardEvent)1002                {1003                    OnForwardEvent(newEvent);1004                }1005                else1006                {1007                    lock (ReceivedEvents.SyncRoot)1008                    {1009                        ReceivedEvents.Add(newEvent);1010                    }1011                }1012 1013                foreach (PSEventSubscriber subscriber in subscribersWithoutActionOrHandler)1014                {1015                    AutoUnregisterEventIfNecessary(subscriber);1016                }1017            }1018        }1019 1020        // Add an action to the event queue1021        private void AddAction(EventAction action, bool processSynchronously)1022        {1023            if (processSynchronously)1024            {1025                // This mutex will get set after the event is processed.1026                action.Args.EventProcessed = new ManualResetEventSlim();1027            }1028 1029            lock (((System.Collections.ICollection)_actionQueue).SyncRoot)1030            {1031                // If the engine isn't active, pulse the pipeline.1032                // When the engine starts up, it will pick up the pending events1033                _actionQueue.Enqueue(action);1034            }1035 1036            PulseEngine();1037        }1038 1039        // PowerShell support for async notifications happen through the1040        // CheckForInterrupts() method on ParseTreeNode. These are only called when1041        // the engine is active (and processing,) so the Pulse() method1042        // executes the equivalent of a NOP so that async events1043        // can be processed when the engine is idle.1044        private void PulseEngine()1045        {1046            try1047            {1048                ((LocalRunspace)_context.CurrentRunspace).Pulse();1049            }1050            catch (ObjectDisposedException)1051            { }1052        }1053 1054        /// <summary>1055        /// Process any pending actions. The parser calls this method before it executes any1056        /// parse tree node. If we are processing pending actions, we must block the parser.1057        /// To prevent starvation of the foreground script, we throttle the number of events1058        /// that we process while the parser is waiting. If the parser is not waiting, we1059        /// do not throttle the event processing.1060        /// </summary>1061        internal void ProcessPendingActions()1062        {1063            // We do this check quickly outside of the lock so that the1064            // main-line scenario is as fast as possible.  Also, do the real work1065            // in a different method so that this method could be inlined.1066            if (_actionQueue.Count == 0)1067                return;1068 1069            ProcessPendingActionsImpl();1070        }1071 1072        private void ProcessPendingActionsImpl()1073        {1074            // Now, process pending actions1075            if (IsExecutingEventAction)1076                return;1077 1078            try1079            {1080                lock (_actionProcessingLock)1081                {1082                    if (IsExecutingEventAction)1083                        return;1084 1085                    int processed = 0;1086                    _throttleChecks++;1087                    EventAction nextAction;1088 1089                    while ((_throttleLimit * _throttleChecks) >= processed)1090                    {1091                        // Now, check for (and process) pending actions.1092                        // Lock the collection so that it doesn't change from1093                        // beneath us.1094                        lock (((System.Collections.ICollection)_actionQueue).SyncRoot)1095                        {1096                            int queueCount = _actionQueue.Count;1097 1098                            // Exit if the actions have already been processed1099                            if (queueCount == 0)1100                                return;1101 1102                            nextAction = _actionQueue.Dequeue();1103                        }1104 1105                        bool addActionBackToActionQueue = false;1106                        InvokeAction(nextAction, out addActionBackToActionQueue);1107                        processed++;1108 1109                        if (!addActionBackToActionQueue)1110                        {1111                            AutoUnregisterEventIfNecessary(nextAction.Sender);1112                        }1113                    }1114 1115                    if (processed > 0)1116                        _throttleChecks = 0;1117                }1118            }1119            finally1120            {1121                if (_actionQueue.Count > 0)1122                {1123                    // If we still have work remaining, sleep a bit (to give1124                    // other pipelines a chance to interrupt,) and try again.1125                    // This is done on another thread, since we own the runspace lock1126                    // at this point if we're being called from the pipeline1127                    // teardown event. That can result in starvation of1128                    // foreground threads that also want to use the runspace.1129                    ThreadPool.QueueUserWorkItem(new WaitCallback(1130                        (_) =>1131                        {1132                            System.Threading.Thread.Sleep(100);1133                            this.PulseEngine();1134                        }));1135                }1136            }1137        }1138 1139        /// <summary>1140        /// Auto unregister the subscriber if both 'RemainingTriggerCount' and 'RemainingActionsToProcess' become zero.1141        /// </summary>1142        private void AutoUnregisterEventIfNecessary(PSEventSubscriber subscriber)1143        {1144            bool removeSubscriber = false;1145            if (subscriber.AutoUnregister)1146            {1147                lock (subscriber)1148                {1149                    subscriber.RemainingActionsToProcess--;1150                    removeSubscriber = subscriber.RemainingTriggerCount == 0 &&1151                                       subscriber.RemainingActionsToProcess == 0;1152                }1153            }1154 1155            if (removeSubscriber)1156            {1157                UnsubscribeEvent(subscriber, true);1158            }1159        }1160 1161        private readonly object _actionProcessingLock = new object();1162        private EventAction _processingAction = null;1163 1164        /// <summary>1165        /// Drain any pending actions for a given subscriber.1166        /// This is a synchronous (and expensive) operation, but is1167        /// required so that unregistering for an event / stopping the1168        /// event job truly discontinues those event arrivals.1169        /// </summary>1170        internal void DrainPendingActions(PSEventSubscriber subscriber)1171        {1172            // We do this check quickly outside of the lock so that the1173            // main-line scenario is as fast as possible.1174            if (_actionQueue.Count == 0)1175                return;1176 1177            // Now, process pending actions1178            lock (_actionProcessingLock)1179            {1180                lock (((System.Collections.ICollection)_actionQueue).SyncRoot)1181                {1182                    int queueCount = _actionQueue.Count;1183 1184                    // Exit if the actions have already been processed1185                    if (queueCount == 0)1186                        return;1187 1188                    bool needToScanAgain = false;1189 1190                    do1191                    {1192                        EventAction[] pendingActions = _actionQueue.ToArray();1193                        _actionQueue.Clear();1194 1195                        foreach (EventAction pendingAction in pendingActions)1196                        {1197                            // Make sure an event action can unregister itself.1198                            if ((pendingAction.Sender == subscriber) &&1199                                (pendingAction != _processingAction))1200                            {

Showing the first 1,200 of 2654 lines. Download the file for the rest.