MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Collections.ObjectModel;8using System.Collections.Specialized;9using System.ComponentModel; // Win32Exception10using System.Diagnostics; // Process class11using System.Diagnostics.CodeAnalysis;12using System.IO;13using System.Management.Automation;14using System.Management.Automation.Internal;15using System.Management.Automation.Language;16using System.Net;17using System.Runtime.InteropServices;18using System.Runtime.Serialization;19using System.Security.Principal;20using System.Text;21using System.Threading;22using Microsoft.Management.Infrastructure;23using Microsoft.PowerShell.Commands.Internal;24using Microsoft.Win32.SafeHandles;25 26namespace Microsoft.PowerShell.Commands27{28 #region ProcessBaseCommand29 /// <summary>30 /// This class implements the base for process commands.31 /// </summary>32 public abstract class ProcessBaseCommand : Cmdlet33 {34 #region Parameters35 /// <summary>36 /// The various process selection modes.37 /// </summary>38 internal enum MatchMode39 {40 /// <summary>41 /// Select all processes.42 /// </summary>43 All,44 /// <summary>45 /// Select processes matching the supplied names.46 /// </summary>47 ByName,48 /// <summary>49 /// Select the processes matching the id.50 /// </summary>51 ById,52 /// <summary>53 /// Select the processes specified as input.54 /// </summary>55 ByInput56 }57 /// <summary>58 /// The current process selection mode.59 /// </summary>60 internal MatchMode myMode = MatchMode.All;61 62 /// <remarks>63 /// The Name parameter is declared in subclasses,64 /// since it is optional for GetProcess and mandatory for StopProcess.65 /// </remarks>66 internal string[] processNames = null;67 68 // The Id parameter is declared in subclasses,69 // since it is positional for StopProcess but not for GetProcess.70 internal int[] processIds = null;71 72 /// <summary>73 /// If the input is a stream of [collections of]74 /// Process objects, we bypass the Name and75 /// Id parameters and read the Process objects76 /// directly. This allows us to deal with processes which77 /// have wildcard characters in their name.78 /// </summary>79 /// <value>Process objects</value>80 [Parameter(81 ParameterSetName = "InputObject",82 Mandatory = true,83 ValueFromPipeline = true)]84 public virtual Process[] InputObject85 {86 get87 {88 return _input;89 }90 91 set92 {93 myMode = MatchMode.ByInput;94 _input = value;95 }96 }97 98 private Process[] _input = null;99 #endregion Parameters100 101 #region Internal102 103 // We use a Dictionary to optimize the check whether the object104 // is already in the list.105 private List<Process> _matchingProcesses = new();106 private readonly Dictionary<int, Process> _keys = new();107 108 /// <summary>109 /// Retrieve the list of all processes matching the Name, Id110 /// and InputObject parameters, sorted by Id.111 /// </summary>112 /// <returns></returns>113 internal List<Process> MatchingProcesses()114 {115 _matchingProcesses.Clear();116 switch (myMode)117 {118 case MatchMode.ById:119 RetrieveMatchingProcessesById();120 break;121 122 case MatchMode.ByInput:123 RetrieveProcessesByInput();124 break;125 default:126 // Default is "Name":127 RetrieveMatchingProcessesByProcessName();128 break;129 }130 // 2004/12/16 Note that the processes will be sorted131 // before being stopped. PM confirms that this is fine.132 _matchingProcesses.Sort(ProcessComparison);133 return _matchingProcesses;134 }135 136 /// <summary>137 /// Sort function to sort by Name first, then Id.138 /// </summary>139 /// <param name="x">First Process object.</param>140 /// <param name="y">Second Process object.</param>141 /// <returns>142 /// As string.Compare: returns less than zero if x less than y,143 /// greater than 0 if x greater than y, 0 if x == y.144 /// </returns>145 private static int ProcessComparison(Process x, Process y)146 {147 int diff = string.Compare(148 SafeGetProcessName(x),149 SafeGetProcessName(y),150 StringComparison.OrdinalIgnoreCase);151 if (diff != 0)152 return diff;153 return SafeGetProcessId(x) - SafeGetProcessId(y);154 }155 156 /// <summary>157 /// Retrieves the list of all processes matching the Name158 /// parameter.159 /// Generates a non-terminating error for each specified160 /// process name which is not found even though it contains161 /// no wildcards.162 /// </summary>163 /// <returns></returns>164 private void RetrieveMatchingProcessesByProcessName()165 {166 if (processNames == null)167 {168 _matchingProcesses = new List<Process>(AllProcesses);169 return;170 }171 172 foreach (string pattern in processNames)173 {174 WildcardPattern wildcard =175 WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);176 bool found = false;177 foreach (Process process in AllProcesses)178 {179 if (!wildcard.IsMatch(SafeGetProcessName(process)))180 continue;181 found = true;182 AddIdempotent(process);183 }184 185 if (!found &&186 !WildcardPattern.ContainsWildcardCharacters(pattern))187 {188 string errorText = ProcessResources.NoProcessFoundForGivenName;189 string errorName = nameof(ProcessResources.NoProcessFoundForGivenName);190 191 if (int.TryParse(pattern, out int x) && x >= 0)192 {193 errorText = ProcessResources.RecommendIdTagForGivenName;194 errorName = nameof(ProcessResources.RecommendIdTagForGivenName);195 }196 197 WriteNonTerminatingError(198 processName: pattern,199 processId: 0,200 targetObject: pattern,201 innerException: null,202 resourceId: errorText,203 errorId: errorName,204 category: ErrorCategory.ObjectNotFound);205 }206 }207 }208 209 /// <summary>210 /// Retrieves the list of all processes matching the Id211 /// parameter.212 /// Generates a non-terminating error for each specified213 /// process ID which is not found.214 /// </summary>215 /// <returns></returns>216 private void RetrieveMatchingProcessesById()217 {218 if (processIds == null)219 {220 Diagnostics.Assert(false, "null processIds");221 throw PSTraceSource.NewInvalidOperationException();222 }223 224 foreach (int processId in processIds)225 {226 Process process;227 try228 {229 process = Process.GetProcessById(processId);230 AddIdempotent(process);231 }232 catch (ArgumentException)233 {234 WriteNonTerminatingError(235 string.Empty,236 processId,237 processId,238 null,239 ProcessResources.NoProcessFoundForGivenId,240 "NoProcessFoundForGivenId",241 ErrorCategory.ObjectNotFound);242 continue;243 }244 }245 }246 247 /// <summary>248 /// Retrieves the list of all processes matching the InputObject249 /// parameter.250 /// </summary>251 /// <returns></returns>252 private void RetrieveProcessesByInput()253 {254 if (InputObject == null)255 {256 Diagnostics.Assert(false, "null InputObject");257 throw PSTraceSource.NewInvalidOperationException();258 }259 260 foreach (Process process in InputObject)261 {262 SafeRefresh(process);263 AddIdempotent(process);264 }265 }266 267 /// <summary>268 /// Gets an array of all processes.269 /// </summary>270 /// <value>An array of <see cref="Process"/> components that represents all the process resources.</value>271 /// <exception cref="System.Security.SecurityException">272 /// MSDN does not document the list of exceptions,273 /// but it is reasonable to expect that SecurityException is274 /// among them. Errors here will terminate the cmdlet.275 /// </exception>276 internal Process[] AllProcesses => _allProcesses ??= Process.GetProcesses();277 278 private Process[] _allProcesses;279 280 /// <summary>281 /// Add <paramref name="process"/> to <see cref="_matchingProcesses"/>,282 /// but only if it is not already on <see cref="_matchingProcesses"/>.283 /// We use a Dictionary to optimize the check whether the object284 /// is already in the list.285 /// </summary>286 /// <param name="process">Process to add to list.</param>287 private void AddIdempotent(288 Process process)289 {290 int hashCode = SafeGetProcessName(process).GetHashCode()291 ^ SafeGetProcessId(process); // XOR292 if (!_keys.ContainsKey(hashCode))293 {294 _keys.Add(hashCode, process);295 _matchingProcesses.Add(process);296 }297 }298 299 /// <summary>300 /// Writes a non-terminating error.301 /// </summary>302 /// <param name="process"></param>303 /// <param name="innerException"></param>304 /// <param name="resourceId"></param>305 /// <param name="errorId"></param>306 /// <param name="category"></param>307 internal void WriteNonTerminatingError(308 Process process,309 Exception innerException,310 string resourceId, string errorId,311 ErrorCategory category)312 {313 WriteNonTerminatingError(314 SafeGetProcessName(process),315 SafeGetProcessId(process),316 process,317 innerException,318 resourceId,319 errorId,320 category);321 }322 323 /// <summary>324 /// Writes a non-terminating error.325 /// </summary>326 /// <param name="processName"></param>327 /// <param name="processId"></param>328 /// <param name="targetObject"></param>329 /// <param name="innerException"></param>330 /// <param name="resourceId"></param>331 /// <param name="errorId"></param>332 /// <param name="category"></param>333 internal void WriteNonTerminatingError(334 string processName,335 int processId,336 object targetObject,337 Exception innerException,338 string resourceId,339 string errorId,340 ErrorCategory category)341 {342 string message = StringUtil.Format(resourceId,343 processName,344 processId,345 (innerException == null) ? string.Empty : innerException.Message);346 ProcessCommandException exception =347 new(message, innerException);348 exception.ProcessName = processName;349 350 WriteError(new ErrorRecord(351 exception, errorId, category, targetObject));352 }353 354 // The Name property is not always available, even for355 // live processes (such as the Idle process).356 internal static string SafeGetProcessName(Process process)357 {358 try359 {360 return process.ProcessName;361 }362 catch (Win32Exception)363 {364 return string.Empty;365 }366 catch (InvalidOperationException)367 {368 return string.Empty;369 }370 }371 372 // 2004/12/17-JonN I saw this fail once too, so we'll play it safe373 internal static int SafeGetProcessId(Process process)374 {375 try376 {377 return process.Id;378 }379 catch (Win32Exception)380 {381 return int.MinValue;382 }383 catch (InvalidOperationException)384 {385 return int.MinValue;386 }387 }388 389 internal static void SafeRefresh(Process process)390 {391 try392 {393 process.Refresh();394 }395 catch (Win32Exception)396 {397 }398 catch (InvalidOperationException)399 {400 }401 }402 403 /// <summary>404 /// TryHasExited is a helper function used to detect if the process has aready exited or not.405 /// </summary>406 /// <param name="process">407 /// Process whose exit status has to be checked.408 /// </param>409 /// <returns>Tre if the process has exited or else returns false.</returns>410 internal static bool TryHasExited(Process process)411 {412 bool hasExited = true;413 414 try415 {416 hasExited = process.HasExited;417 }418 catch (Win32Exception)419 {420 hasExited = false;421 }422 catch (InvalidOperationException)423 {424 hasExited = false;425 }426 427 return hasExited;428 }429 430 #endregion Internal431 }432 #endregion ProcessBaseCommand433 434 #region GetProcessCommand435 /// <summary>436 /// This class implements the get-process command.437 /// </summary>438 [Cmdlet(VerbsCommon.Get, "Process", DefaultParameterSetName = NameParameterSet,439 HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096814", RemotingCapability = RemotingCapability.SupportedByCommand)]440 [OutputType(typeof(ProcessModule), typeof(FileVersionInfo), typeof(Process))]441 public sealed class GetProcessCommand : ProcessBaseCommand442 {443 #region ParameterSetStrings444 445 private const string NameParameterSet = "Name";446 private const string IdParameterSet = "Id";447 private const string InputObjectParameterSet = "InputObject";448 private const string NameWithUserNameParameterSet = "NameWithUserName";449 private const string IdWithUserNameParameterSet = "IdWithUserName";450 private const string InputObjectWithUserNameParameterSet = "InputObjectWithUserName";451 452 #endregion ParameterSetStrings453 454 #region Parameters455 456 /// <summary>457 /// Has the list of process names on which to this command will work.458 /// </summary>459 [Parameter(Position = 0, ParameterSetName = NameParameterSet, ValueFromPipelineByPropertyName = true)]460 [Parameter(Position = 0, ParameterSetName = NameWithUserNameParameterSet, ValueFromPipelineByPropertyName = true)]461 [Alias("ProcessName")]462 [ValidateNotNullOrEmpty]463 public string[] Name464 {465 get466 {467 return processNames;468 }469 470 set471 {472 myMode = MatchMode.ByName;473 processNames = value;474 }475 }476 477 /// <summary>478 /// Gets/sets an array of process IDs.479 /// </summary>480 [Parameter(ParameterSetName = IdParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)]481 [Parameter(ParameterSetName = IdWithUserNameParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)]482 [Alias("PID")]483 public int[] Id484 {485 get486 {487 return processIds;488 }489 490 set491 {492 myMode = MatchMode.ById;493 processIds = value;494 }495 }496 497 /// <summary>498 /// Input is a stream of [collections of] Process objects.499 /// </summary>500 [Parameter(ParameterSetName = InputObjectParameterSet, Mandatory = true, ValueFromPipeline = true)]501 [Parameter(ParameterSetName = InputObjectWithUserNameParameterSet, Mandatory = true, ValueFromPipeline = true)]502 public override Process[] InputObject503 {504 get505 {506 return base.InputObject;507 }508 509 set510 {511 base.InputObject = value;512 }513 }514 515 /// <summary>516 /// Include the UserName.517 /// </summary>518 [Parameter(ParameterSetName = NameWithUserNameParameterSet, Mandatory = true)]519 [Parameter(ParameterSetName = IdWithUserNameParameterSet, Mandatory = true)]520 [Parameter(ParameterSetName = InputObjectWithUserNameParameterSet, Mandatory = true)]521 public SwitchParameter IncludeUserName { get; set; }522 523 /// <summary>524 /// To display the modules of a process.525 /// </summary>526 [Parameter(ParameterSetName = NameParameterSet)]527 [Parameter(ParameterSetName = IdParameterSet)]528 [Parameter(ParameterSetName = InputObjectParameterSet)]529 [ValidateNotNull]530 public SwitchParameter Module { get; set; }531 532 /// <summary>533 /// To display the fileversioninfo of the main module of a process.534 /// </summary>535 [Parameter(ParameterSetName = NameParameterSet)]536 [Parameter(ParameterSetName = IdParameterSet)]537 [Parameter(ParameterSetName = InputObjectParameterSet)]538 [Alias("FV", "FVI")]539 [ValidateNotNull]540 public SwitchParameter FileVersionInfo { get; set; }541 542 #endregion Parameters543 544 #region Overrides545 546 /// <summary>547 /// Write the process objects.548 /// </summary>549 protected override void ProcessRecord()550 {551 foreach (Process process in MatchingProcesses())552 {553 if (Module.IsPresent && FileVersionInfo.IsPresent)554 {555 ProcessModule tempmodule = null;556 try557 {558 ProcessModuleCollection modules = process.Modules;559 foreach (ProcessModule pmodule in modules)560 {561 // Assigning to tempmodule to rethrow for exceptions on 64 bit machines562 tempmodule = pmodule;563 WriteObject(pmodule.FileVersionInfo, true);564 }565 }566 catch (InvalidOperationException exception)567 {568 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied);569 }570 catch (ArgumentException exception)571 {572 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied);573 }574 catch (Win32Exception exception)575 {576 try577 {578 if (exception.HResult == 299)579 {580 WriteObject(tempmodule.FileVersionInfo, true);581 }582 else583 {584 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied);585 }586 }587 catch (Win32Exception ex)588 {589 WriteNonTerminatingError(process, ex, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied);590 }591 }592 catch (Exception exception)593 {594 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied);595 }596 }597 else if (Module.IsPresent)598 {599 try600 {601 WriteObject(process.Modules, true);602 }603 catch (Win32Exception exception)604 {605 try606 {607 if (exception.HResult == 299)608 {609 WriteObject(process.Modules, true);610 }611 else612 {613 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModules, "CouldNotEnumerateModules", ErrorCategory.PermissionDenied);614 }615 }616 catch (Win32Exception ex)617 {618 WriteNonTerminatingError(process, ex, ProcessResources.CouldNotEnumerateModules, "CouldNotEnumerateModules", ErrorCategory.PermissionDenied);619 }620 }621 catch (PipelineStoppedException)622 {623 throw;624 }625 catch (Exception exception)626 {627 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModules, "CouldNotEnumerateModules", ErrorCategory.PermissionDenied);628 }629 }630 else if (FileVersionInfo.IsPresent)631 {632 try633 {634 ProcessModule mainModule = process.MainModule;635 if (mainModule != null)636 {637 WriteObject(mainModule.FileVersionInfo, true);638 }639 }640 catch (InvalidOperationException exception)641 {642 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied);643 }644 catch (ArgumentException exception)645 {646 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied);647 }648 catch (Win32Exception exception)649 {650 try651 {652 if (exception.HResult == 299)653 {654 WriteObject(process.MainModule?.FileVersionInfo, true);655 }656 else657 {658 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied);659 }660 }661 catch (Win32Exception ex)662 {663 WriteNonTerminatingError(process, ex, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied);664 }665 }666 catch (Exception exception)667 {668 WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied);669 }670 }671 else672 {673 WriteObject(IncludeUserName.IsPresent ? AddUserNameToProcess(process) : process);674 }675 }676 }677 678 #endregion Overrides679 680 #region Privates681 682 /// <summary>683 /// New PSTypeName added to the process object.684 /// </summary>685 private const string TypeNameForProcessWithUserName = "System.Diagnostics.Process#IncludeUserName";686 687 /// <summary>688 /// Add the 'UserName' NoteProperty to the Process object.689 /// </summary>690 /// <param name="process"></param>691 /// <returns></returns>692 private static PSObject AddUserNameToProcess(Process process)693 {694 // Return null if we failed to get the owner information695 string userName = RetrieveProcessUserName(process);696 697 PSObject processAsPsobj = PSObject.AsPSObject(process);698 PSNoteProperty noteProperty = new("UserName", userName);699 700 processAsPsobj.Properties.Add(noteProperty, true);701 processAsPsobj.TypeNames.Insert(0, TypeNameForProcessWithUserName);702 703 return processAsPsobj;704 }705 706 /// <summary>707 /// Retrieve the UserName through PInvoke.708 /// </summary>709 /// <param name="process"></param>710 /// <returns></returns>711 private static string RetrieveProcessUserName(Process process)712 {713 string userName = null;714#if UNIX715 userName = Platform.NonWindowsGetUserFromPid(process.Id);716#else717 IntPtr tokenUserInfo = IntPtr.Zero;718 IntPtr processTokenHandler = IntPtr.Zero;719 720 const uint TOKEN_QUERY = 0x0008;721 722 try723 {724 int error;725 if (!Win32Native.OpenProcessToken(process.Handle, TOKEN_QUERY, out processTokenHandler))726 {727 return null;728 }729 730 // Set the default length to be 256, so it will be sufficient for most cases.731 int tokenInfoLength = 256;732 tokenUserInfo = Marshal.AllocHGlobal(tokenInfoLength);733 if (!Win32Native.GetTokenInformation(processTokenHandler, Win32Native.TOKEN_INFORMATION_CLASS.TokenUser, tokenUserInfo, tokenInfoLength, out tokenInfoLength))734 {735 error = Marshal.GetLastWin32Error();736 if (error == Win32Native.ERROR_INSUFFICIENT_BUFFER)737 {738 Marshal.FreeHGlobal(tokenUserInfo);739 tokenUserInfo = Marshal.AllocHGlobal(tokenInfoLength);740 741 if (!Win32Native.GetTokenInformation(processTokenHandler, Win32Native.TOKEN_INFORMATION_CLASS.TokenUser, tokenUserInfo, tokenInfoLength, out tokenInfoLength))742 {743 return null;744 }745 }746 else747 {748 return null;749 }750 }751 752 var tokenUser = Marshal.PtrToStructure<Win32Native.TOKEN_USER>(tokenUserInfo);753 SecurityIdentifier sid = new SecurityIdentifier(tokenUser.User.Sid);754 userName = sid.Translate(typeof(System.Security.Principal.NTAccount)).Value;755 }756 catch (NotSupportedException)757 {758 // The Process not started yet, or it's a process from a remote machine.759 }760 catch (IdentityNotMappedException)761 {762 // SID cannot be mapped to a user763 }764 catch (InvalidOperationException)765 {766 // The Process has exited, Process.Handle will raise this exception.767 }768 catch (Win32Exception)769 {770 // We might get an AccessDenied error.771 }772 catch (Exception)773 {774 // I don't expect to get other exceptions.775 }776 finally777 {778 if (tokenUserInfo != IntPtr.Zero)779 {780 Marshal.FreeHGlobal(tokenUserInfo);781 }782 783 if (processTokenHandler != IntPtr.Zero)784 {785 Win32Native.CloseHandle(processTokenHandler);786 }787 }788#endif789 return userName;790 }791 792 #endregion Privates793 }794 #endregion GetProcessCommand795 796 #region WaitProcessCommand797 /// <summary>798 /// This class implements the Wait-process command.799 /// </summary>800 [Cmdlet(VerbsLifecycle.Wait, "Process", DefaultParameterSetName = "Name", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097146")]801 [OutputType(typeof(Process))]802 public sealed class WaitProcessCommand : ProcessBaseCommand803 {804 #region Parameters805 806 /// <summary>807 /// Specifies the process IDs of the processes to be waited on.808 /// </summary>809 [Parameter(810 ParameterSetName = "Id",811 Position = 0,812 Mandatory = true,813 ValueFromPipelineByPropertyName = true)]814 [ValidateNotNullOrEmpty]815 [Alias("PID", "ProcessId")]816 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]817 public int[] Id818 {819 get820 {821 return processIds;822 }823 824 set825 {826 myMode = MatchMode.ById;827 processIds = value;828 }829 }830 831 /// <summary>832 /// Name of the processes to wait on for termination.833 /// </summary>834 [Parameter(835 ParameterSetName = "Name",836 Position = 0,837 Mandatory = true,838 ValueFromPipelineByPropertyName = true)]839 [Alias("ProcessName")]840 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]841 public string[] Name842 {843 get844 {845 return processNames;846 }847 848 set849 {850 myMode = MatchMode.ByName;851 processNames = value;852 }853 }854 855 /// <summary>856 /// If specified, wait for this number of seconds.857 /// </summary>858 [Parameter(Position = 1)]859 [Alias("TimeoutSec")]860 [ValidateNotNullOrEmpty]861 [ValidateRange(0, 32767)]862 public int Timeout863 {864 get865 {866 return _timeout;867 }868 869 set870 {871 _timeout = value;872 _timeOutSpecified = true;873 }874 }875 876 /// <summary>877 /// Gets or sets a value indicating whether to return after any one process exits.878 /// </summary>879 [Parameter]880 public SwitchParameter Any { get; set; }881 882 /// <summary>883 /// Gets or sets a value indicating whether to return the Process objects after waiting.884 /// </summary>885 [Parameter]886 public SwitchParameter PassThru { get; set; }887 888 private int _timeout = 0;889 private bool _timeOutSpecified;890 891 #endregion Parameters892 893 private bool _disposed = false;894 895 #region IDisposable896 /// <summary>897 /// Dispose method of IDisposable interface.898 /// </summary>899 public void Dispose()900 {901 if (!_disposed)902 {903 if (_waitHandle != null)904 {905 _waitHandle.Dispose();906 _waitHandle = null;907 }908 909 _disposed = true;910 }911 }912 913 #endregion914 915 #region private methods916 // Handle Exited event and display process information.917 private void myProcess_Exited(object sender, System.EventArgs e)918 {919 if (Any || (Interlocked.Decrement(ref _numberOfProcessesToWaitFor) == 0))920 {921 _waitHandle?.Set();922 }923 }924 925 #endregion926 927 #region Overrides928 929 private readonly List<Process> _processList = new();930 931 // Wait handle which is used by thread to sleep.932 private ManualResetEvent _waitHandle;933 private int _numberOfProcessesToWaitFor;934 935 /// <summary>936 /// Gets the list of process.937 /// </summary>938 protected override void ProcessRecord()939 {940 // adding the processes into the list941 foreach (Process process in MatchingProcesses())942 {943 // Idle process has processid zero,so handle that because we cannot wait on it.944 if (process.Id == 0)945 {946 WriteNonTerminatingError(process, null, ProcessResources.WaitOnIdleProcess, "WaitOnIdleProcess", ErrorCategory.ObjectNotFound);947 continue;948 }949 950 // It cannot wait on itself951 if (process.Id.Equals(Environment.ProcessId))952 {953 WriteNonTerminatingError(process, null, ProcessResources.WaitOnItself, "WaitOnItself", ErrorCategory.ObjectNotFound);954 continue;955 }956 957 _processList.Add(process);958 }959 }960 961 /// <summary>962 /// Wait for the process to terminate.963 /// </summary>964 protected override void EndProcessing()965 {966 _waitHandle = new ManualResetEvent(false);967 foreach (Process process in _processList)968 {969 try970 {971 // Check for processes that exit too soon for us to add an event.972 if (Any && process.HasExited)973 {974 _waitHandle.Set();975 }976 else if (!process.HasExited)977 {978 process.EnableRaisingEvents = true;979 process.Exited += myProcess_Exited;980 if (!process.HasExited)981 {982 System.Threading.Interlocked.Increment(ref _numberOfProcessesToWaitFor);983 }984 }985 }986 catch (Win32Exception exception)987 {988 WriteNonTerminatingError(process, exception, ProcessResources.ProcessIsNotTerminated, "ProcessNotTerminated", ErrorCategory.CloseError);989 }990 }991 992 bool hasTimedOut = false;993 if (_numberOfProcessesToWaitFor > 0)994 {995 if (_timeOutSpecified)996 {997 hasTimedOut = !_waitHandle.WaitOne(_timeout * 1000);998 }999 else1000 {1001 _waitHandle.WaitOne();1002 }1003 }1004 1005 if (hasTimedOut || (!Any && _numberOfProcessesToWaitFor > 0))1006 {1007 foreach (Process process in _processList)1008 {1009 try1010 {1011 if (!process.HasExited)1012 {1013 string message = StringUtil.Format(ProcessResources.ProcessNotTerminated, new object[] { process.ProcessName, process.Id });1014 ErrorRecord errorRecord = new(new TimeoutException(message), "ProcessNotTerminated", ErrorCategory.CloseError, process);1015 WriteError(errorRecord);1016 }1017 }1018 catch (Win32Exception exception)1019 {1020 WriteNonTerminatingError(process, exception, ProcessResources.ProcessIsNotTerminated, "ProcessNotTerminated", ErrorCategory.CloseError);1021 }1022 }1023 }1024 1025 if (PassThru)1026 {1027 WriteObject(_processList, enumerateCollection: true);1028 }1029 }1030 1031 /// <summary>1032 /// StopProcessing.1033 /// </summary>1034 protected override void StopProcessing() => _waitHandle?.Set();1035 1036 #endregion Overrides1037 1038 }1039 #endregion WaitProcessCommand1040 1041 #region StopProcessCommand1042 /// <summary>1043 /// This class implements the stop-process command.1044 /// </summary>1045 /// <remarks>1046 /// Processes will be sorted before being stopped. PM confirms1047 /// that this should be fine.1048 /// </remarks>1049 [Cmdlet(VerbsLifecycle.Stop, "Process",1050 DefaultParameterSetName = "Id",1051 SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097058")]1052 [OutputType(typeof(Process))]1053 public sealed class StopProcessCommand : ProcessBaseCommand1054 {1055 #region Parameters1056 /// <summary>1057 /// Has the list of process names on which to this command will work.1058 /// </summary>1059 [Parameter(1060 ParameterSetName = "Name",1061 Mandatory = true,1062 ValueFromPipelineByPropertyName = true)]1063 [Alias("ProcessName")]1064 public string[] Name1065 {1066 get1067 {1068 return processNames;1069 }1070 1071 set1072 {1073 processNames = value;1074 myMode = MatchMode.ByName;1075 }1076 }1077 1078 /// <summary>1079 /// Gets/sets an array of process IDs.1080 /// </summary>1081 [Parameter(1082 Position = 0,1083 ParameterSetName = "Id",1084 Mandatory = true,1085 ValueFromPipelineByPropertyName = true)]1086 public int[] Id1087 {1088 get1089 {1090 return processIds;1091 }1092 1093 set1094 {1095 myMode = MatchMode.ById;1096 processIds = value;1097 }1098 }1099 1100 /// <summary>1101 /// Gets/sets an array of objects.1102 /// </summary>1103 [Parameter(1104 Position = 0,1105 ParameterSetName = "InputObject",1106 Mandatory = true,1107 ValueFromPipeline = true)]1108 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]1109 public new Process[] InputObject1110 {1111 get1112 {1113 return base.InputObject;1114 }1115 1116 set1117 {1118 base.InputObject = value;1119 }1120 }1121 1122 private bool _passThru;1123 /// <summary>1124 /// The updated process object should be passed down the pipeline.1125 /// </summary>1126 [Parameter]1127 public SwitchParameter PassThru1128 {1129 get { return _passThru; }1130 1131 set { _passThru = value; }1132 }1133 1134 /// <summary>1135 /// Specifies whether to force a process to kill1136 /// even if it has dependent services.1137 /// </summary>1138 /// <value></value>1139 [Parameter]1140 [ValidateNotNullOrEmpty]1141 public SwitchParameter Force { get; set; }1142 1143 #endregion Parameters1144 1145 #region Overrides1146 /// <summary>1147 /// Kill the processes.1148 /// It is a non-terminating error if the Process.Kill() operation fails.1149 /// </summary>1150 protected override void ProcessRecord()1151 {1152 if (myMode == MatchMode.All || (myMode == MatchMode.ByName && processNames == null))1153 {1154 Diagnostics.Assert(false, "trying to kill all processes");1155 throw PSTraceSource.NewInvalidOperationException();1156 }1157 1158 foreach (Process process in MatchingProcesses())1159 {1160 // confirm the operation first1161 // this is always false if WhatIf is set1162 // 2005-06-21 Moved this ahead of the hasExited check1163 string targetString = StringUtil.Format(1164 ProcessResources.ProcessNameForConfirmation,1165 SafeGetProcessName(process),1166 SafeGetProcessId(process));1167 1168 if (!ShouldProcess(targetString))1169 {1170 continue;1171 }1172 1173 try1174 {1175 // Many properties including Name are not available if the process has exited.1176 // If this is the case, we skip the process. If the process is from a remote1177 // machine, then we generate a non-terminating error because .NET doesn't support1178 // terminate a remote process.1179 if (process.HasExited)1180 {1181 if (PassThru)1182 WriteObject(process);1183 continue;1184 }1185 }1186 catch (NotSupportedException ex)1187 {1188 WriteNonTerminatingError(1189 process, ex, ProcessResources.CouldNotStopProcess,1190 "CouldNotStopProcess", ErrorCategory.InvalidOperation);1191 continue;1192 }1193 catch (Win32Exception ex)1194 {1195 WriteNonTerminatingError(1196 process, ex, ProcessResources.CouldNotStopProcess,1197 "CouldNotStopProcess", ErrorCategory.CloseError);1198 continue;1199 }1200 