// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; // Win32Exception using System.Diagnostics; // Process class using System.Diagnostics.CodeAnalysis; using System.IO; using System.Management.Automation; using System.Management.Automation.Internal; using System.Net; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; using System.Security.Permissions; using System.Security.Principal; using System.Text; using System.Threading; using Microsoft.Management.Infrastructure; using Microsoft.PowerShell.Commands.Internal; using Microsoft.Win32.SafeHandles; using FileNakedHandle = System.IntPtr; using DWORD = System.UInt32; namespace Microsoft.PowerShell.Commands { #region ProcessBaseCommand /// /// This class implements the base for process commands. /// public abstract class ProcessBaseCommand : Cmdlet { #region Parameters /// /// The various process selection modes. /// internal enum MatchMode { /// /// Select all processes. /// All, /// /// Select processes matching the supplied names. /// ByName, /// /// Select the processes matching the id. /// ById, /// /// Select the processes specified as input. /// ByInput }; /// /// The current process selection mode. /// internal MatchMode myMode = MatchMode.All; /// /// The Name parameter is declared in subclasses, /// since it is optional for GetProcess and mandatory for StopProcess. /// internal string[] processNames = null; // The Id parameter is declared in subclasses, // since it is positional for StopProcess but not for GetProcess. internal int[] processIds = null; /// /// If the input is a stream of [collections of] /// Process objects, we bypass the Name and /// Id parameters and read the Process objects /// directly. This allows us to deal with processes which /// have wildcard characters in their name. /// /// Process objects [Parameter( ParameterSetName = "InputObject", Mandatory = true, ValueFromPipeline = true)] public virtual Process[] InputObject { get { return _input; } set { myMode = MatchMode.ByInput; _input = value; } } private Process[] _input = null; #endregion Parameters #region Internal // We use a Dictionary to optimize the check whether the object // is already in the list. private List _matchingProcesses = new List(); private Dictionary _keys = new Dictionary(); /// /// Retrieve the list of all processes matching the Name, Id /// and InputObject parameters, sorted by Id. /// /// internal List MatchingProcesses() { _matchingProcesses.Clear(); switch (myMode) { case MatchMode.ById: RetrieveMatchingProcessesById(); break; case MatchMode.ByInput: RetrieveProcessesByInput(); break; default: // Default is "Name": RetrieveMatchingProcessesByProcessName(); break; } // 2004/12/16 Note that the processes will be sorted // before being stopped. PM confirms that this is fine. _matchingProcesses.Sort(ProcessComparison); return _matchingProcesses; } /// /// Sort function to sort by Name first, then Id. /// /// First Process object. /// Second Process object. /// /// As string.Compare: returns less than zero if x less than y, /// greater than 0 if x greater than y, 0 if x == y. /// private static int ProcessComparison(Process x, Process y) { int diff = string.Compare( SafeGetProcessName(x), SafeGetProcessName(y), StringComparison.OrdinalIgnoreCase); if (0 != diff) return diff; return SafeGetProcessId(x) - SafeGetProcessId(y); } /// /// Retrieves the list of all processes matching the Name /// parameter. /// Generates a non-terminating error for each specified /// process name which is not found even though it contains /// no wildcards. /// /// private void RetrieveMatchingProcessesByProcessName() { if (processNames == null) { _matchingProcesses = new List(AllProcesses); return; } foreach (string pattern in processNames) { WildcardPattern wildcard = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase); bool found = false; foreach (Process process in AllProcesses) { if (!wildcard.IsMatch(SafeGetProcessName(process))) continue; found = true; AddIdempotent(process); } if (!found && !WildcardPattern.ContainsWildcardCharacters(pattern)) { string errorText = ProcessResources.NoProcessFoundForGivenName; string errorName = nameof(ProcessResources.NoProcessFoundForGivenName); if (int.TryParse(pattern, out int x) && x >= 0) { errorText = ProcessResources.RecommendIdTagForGivenName; errorName = nameof(ProcessResources.RecommendIdTagForGivenName); } WriteNonTerminatingError( processName: pattern, processId: 0, targetObject: pattern, innerException: null, resourceId: errorText, errorId: errorName, category: ErrorCategory.ObjectNotFound); } } } /// /// Retrieves the list of all processes matching the Id /// parameter. /// Generates a non-terminating error for each specified /// process ID which is not found. /// /// private void RetrieveMatchingProcessesById() { if (processIds == null) { Diagnostics.Assert(false, "null processIds"); throw PSTraceSource.NewInvalidOperationException(); } foreach (int processId in processIds) { Process process; try { process = Process.GetProcessById(processId); AddIdempotent(process); } catch (ArgumentException) { WriteNonTerminatingError( "", processId, processId, null, ProcessResources.NoProcessFoundForGivenId, "NoProcessFoundForGivenId", ErrorCategory.ObjectNotFound); continue; } } } /// /// Retrieves the list of all processes matching the InputObject /// parameter. /// /// private void RetrieveProcessesByInput() { if (InputObject == null) { Diagnostics.Assert(false, "null InputObject"); throw PSTraceSource.NewInvalidOperationException(); } foreach (Process process in InputObject) { SafeRefresh(process); AddIdempotent(process); } } /// /// Retrieve the master list of all processes. /// /// /// /// MSDN does not document the list of exceptions, /// but it is reasonable to expect that SecurityException is /// among them. Errors here will terminate the cmdlet. /// internal Process[] AllProcesses { get { if (_allProcesses == null) { List processes = new List(); processes.AddRange(Process.GetProcesses()); _allProcesses = processes.ToArray(); } return _allProcesses; } } private Process[] _allProcesses = null; /// /// Add to , /// but only if it is not already on . /// We use a Dictionary to optimize the check whether the object /// is already in the list. /// /// Process to add to list. private void AddIdempotent( Process process) { int hashCode = SafeGetProcessName(process).GetHashCode() ^ SafeGetProcessId(process); // XOR if (!_keys.ContainsKey(hashCode)) { _keys.Add(hashCode, process); _matchingProcesses.Add(process); } } /// /// Writes a non-terminating error. /// /// /// /// /// /// internal void WriteNonTerminatingError( Process process, Exception innerException, string resourceId, string errorId, ErrorCategory category) { WriteNonTerminatingError( SafeGetProcessName(process), SafeGetProcessId(process), process, innerException, resourceId, errorId, category); } /// /// Writes a non-terminating error. /// /// /// /// /// /// /// /// internal void WriteNonTerminatingError( string processName, int processId, object targetObject, Exception innerException, string resourceId, string errorId, ErrorCategory category) { string message = StringUtil.Format(resourceId, processName, processId, (innerException == null) ? string.Empty : innerException.Message); ProcessCommandException exception = new ProcessCommandException(message, innerException); exception.ProcessName = processName; WriteError(new ErrorRecord( exception, errorId, category, targetObject)); } // The Name property is not always available, even for // live processes (such as the Idle process). internal static string SafeGetProcessName(Process process) { try { return process.ProcessName; } catch (Win32Exception) { return string.Empty; } catch (InvalidOperationException) { return string.Empty; } } // 2004/12/17-JonN I saw this fail once too, so we'll play it safe internal static int SafeGetProcessId(Process process) { try { return process.Id; } catch (Win32Exception) { return int.MinValue; } catch (InvalidOperationException) { return int.MinValue; } } internal static void SafeRefresh(Process process) { try { process.Refresh(); } catch (Win32Exception) { } catch (InvalidOperationException) { } } /// /// TryHasExited is a helper function used to detect if the process has aready exited or not. /// /// /// Process whose exit status has to be checked. /// /// Tre if the process has exited or else returns false. internal static bool TryHasExited(Process process) { bool hasExited = true; try { hasExited = process.HasExited; } catch (Win32Exception) { hasExited = false; } catch (InvalidOperationException) { hasExited = false; } return hasExited; } #endregion Internal } #endregion ProcessBaseCommand #region GetProcessCommand /// /// This class implements the get-process command. /// [Cmdlet(VerbsCommon.Get, "Process", DefaultParameterSetName = NameParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096814", RemotingCapability = RemotingCapability.SupportedByCommand)] [OutputType(typeof(ProcessModule), typeof(FileVersionInfo), typeof(Process))] public sealed class GetProcessCommand : ProcessBaseCommand { #region ParameterSetStrings private const string NameParameterSet = "Name"; private const string IdParameterSet = "Id"; private const string InputObjectParameterSet = "InputObject"; private const string NameWithUserNameParameterSet = "NameWithUserName"; private const string IdWithUserNameParameterSet = "IdWithUserName"; private const string InputObjectWithUserNameParameterSet = "InputObjectWithUserName"; #endregion ParameterSetStrings #region Parameters /// /// Has the list of process names on which to this command will work. /// [Parameter(Position = 0, ParameterSetName = NameParameterSet, ValueFromPipelineByPropertyName = true)] [Parameter(Position = 0, ParameterSetName = NameWithUserNameParameterSet, ValueFromPipelineByPropertyName = true)] [Alias("ProcessName")] [ValidateNotNullOrEmpty] public string[] Name { get { return processNames; } set { myMode = MatchMode.ByName; processNames = value; } } /// /// Gets/sets an array of process IDs. /// [Parameter(ParameterSetName = IdParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Parameter(ParameterSetName = IdWithUserNameParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Alias("PID")] public int[] Id { get { return processIds; } set { myMode = MatchMode.ById; processIds = value; } } /// /// Input is a stream of [collections of] Process objects. /// [Parameter(ParameterSetName = InputObjectParameterSet, Mandatory = true, ValueFromPipeline = true)] [Parameter(ParameterSetName = InputObjectWithUserNameParameterSet, Mandatory = true, ValueFromPipeline = true)] public override Process[] InputObject { get { return base.InputObject; } set { base.InputObject = value; } } /// /// Include the UserName. /// [Parameter(ParameterSetName = NameWithUserNameParameterSet, Mandatory = true)] [Parameter(ParameterSetName = IdWithUserNameParameterSet, Mandatory = true)] [Parameter(ParameterSetName = InputObjectWithUserNameParameterSet, Mandatory = true)] public SwitchParameter IncludeUserName { get { return _includeUserName; } set { _includeUserName = value; } } private bool _includeUserName = false; /// /// To display the modules of a process. /// [Parameter(ParameterSetName = NameParameterSet)] [Parameter(ParameterSetName = IdParameterSet)] [Parameter(ParameterSetName = InputObjectParameterSet)] [ValidateNotNull] public SwitchParameter Module { get; set; } /// /// To display the fileversioninfo of the main module of a process. /// [Parameter(ParameterSetName = NameParameterSet)] [Parameter(ParameterSetName = IdParameterSet)] [Parameter(ParameterSetName = InputObjectParameterSet)] [Alias("FV", "FVI")] [ValidateNotNull] public SwitchParameter FileVersionInfo { get; set; } #endregion Parameters #region Overrides /// /// Check the elevation mode if IncludeUserName is specified. /// protected override void BeginProcessing() { // The parameter 'IncludeUserName' requires administrator privilege if (IncludeUserName.IsPresent && !Utils.IsAdministrator()) { var ex = new InvalidOperationException(ProcessResources.IncludeUserNameRequiresElevation); var er = new ErrorRecord(ex, "IncludeUserNameRequiresElevation", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } } /// /// Write the process objects. /// protected override void ProcessRecord() { foreach (Process process in MatchingProcesses()) { if (Module.IsPresent && FileVersionInfo.IsPresent) { ProcessModule tempmodule = null; try { ProcessModuleCollection modules = process.Modules; foreach (ProcessModule pmodule in modules) { // Assigning to tempmodule to rethrow for exceptions on 64 bit machines tempmodule = pmodule; WriteObject(pmodule.FileVersionInfo, true); } } catch (InvalidOperationException exception) { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied); } catch (ArgumentException exception) { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied); } catch (Win32Exception exception) { try { if (exception.HResult == 299) { WriteObject(tempmodule.FileVersionInfo, true); } else { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied); } } catch (Win32Exception ex) { WriteNonTerminatingError(process, ex, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied); } } catch (Exception exception) { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModuleFileVer, "CouldNotEnumerateModuleFileVer", ErrorCategory.PermissionDenied); } } else if (Module.IsPresent) { try { WriteObject(process.Modules, true); } catch (Win32Exception exception) { try { if (exception.HResult == 299) { WriteObject(process.Modules, true); } else { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModules, "CouldNotEnumerateModules", ErrorCategory.PermissionDenied); } } catch (Win32Exception ex) { WriteNonTerminatingError(process, ex, ProcessResources.CouldNotEnumerateModules, "CouldNotEnumerateModules", ErrorCategory.PermissionDenied); } } catch (Exception exception) { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModules, "CouldNotEnumerateModules", ErrorCategory.PermissionDenied); } } else if (FileVersionInfo.IsPresent) { try { ProcessModule mainModule = PsUtils.GetMainModule(process); if (mainModule != null) { WriteObject(mainModule.FileVersionInfo, true); } } catch (InvalidOperationException exception) { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied); } catch (ArgumentException exception) { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied); } catch (Win32Exception exception) { try { if (exception.HResult == 299) { WriteObject(PsUtils.GetMainModule(process).FileVersionInfo, true); } else { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied); } } catch (Win32Exception ex) { WriteNonTerminatingError(process, ex, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied); } } catch (Exception exception) { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateFileVer, "CouldNotEnumerateFileVer", ErrorCategory.PermissionDenied); } } else { WriteObject(IncludeUserName.IsPresent ? AddUserNameToProcess(process) : (object)process); } } } #endregion Overrides #region Privates /// /// New PSTypeName added to the process object. /// private const string TypeNameForProcessWithUserName = "System.Diagnostics.Process#IncludeUserName"; /// /// Add the 'UserName' NoteProperty to the Process object. /// /// /// private static PSObject AddUserNameToProcess(Process process) { // Return null if we failed to get the owner information string userName = RetrieveProcessUserName(process); PSObject processAsPsobj = PSObject.AsPSObject(process); PSNoteProperty noteProperty = new PSNoteProperty("UserName", userName); processAsPsobj.Properties.Add(noteProperty, true); processAsPsobj.TypeNames.Insert(0, TypeNameForProcessWithUserName); return processAsPsobj; } /// /// Retrieve the UserName through PInvoke. /// /// /// private static string RetrieveProcessUserName(Process process) { string userName = null; #if UNIX userName = Platform.NonWindowsGetUserFromPid(process.Id); #else IntPtr tokenUserInfo = IntPtr.Zero; IntPtr processTokenHandler = IntPtr.Zero; const uint TOKEN_QUERY = 0x0008; try { do { int error; if (!Win32Native.OpenProcessToken(process.Handle, TOKEN_QUERY, out processTokenHandler)) { break; } // Set the default length to be 256, so it will be sufficient for most cases. int tokenInfoLength = 256; tokenUserInfo = Marshal.AllocHGlobal(tokenInfoLength); if (!Win32Native.GetTokenInformation(processTokenHandler, Win32Native.TOKEN_INFORMATION_CLASS.TokenUser, tokenUserInfo, tokenInfoLength, out tokenInfoLength)) { error = Marshal.GetLastWin32Error(); if (error == Win32Native.ERROR_INSUFFICIENT_BUFFER) { Marshal.FreeHGlobal(tokenUserInfo); tokenUserInfo = Marshal.AllocHGlobal(tokenInfoLength); if (!Win32Native.GetTokenInformation(processTokenHandler, Win32Native.TOKEN_INFORMATION_CLASS.TokenUser, tokenUserInfo, tokenInfoLength, out tokenInfoLength)) { break; } } else { break; } } var tokenUser = Marshal.PtrToStructure(tokenUserInfo); // Max username is defined as UNLEN = 256 in lmcons.h // Max domainname is defined as DNLEN = CNLEN = 15 in lmcons.h // The buffer length must be +1, last position is for a null string terminator. int userNameLength = 257; int domainNameLength = 16; #pragma warning disable CA2014 Span userNameStr = stackalloc char[userNameLength]; Span domainNameStr = stackalloc char[domainNameLength]; #pragma warning restore CA2014 Win32Native.SID_NAME_USE accountType; // userNameLength and domainNameLength will be set to actual lengths. if (!Win32Native.LookupAccountSid(null, tokenUser.User.Sid, userNameStr, ref userNameLength, domainNameStr, ref domainNameLength, out accountType)) { break; } userName = string.Concat(domainNameStr.Slice(0, domainNameLength), "\\", userNameStr.Slice(0, userNameLength)); } while (false); } catch (NotSupportedException) { // The Process not started yet, or it's a process from a remote machine. } catch (InvalidOperationException) { // The Process has exited, Process.Handle will raise this exception. } catch (Win32Exception) { // We might get an AccessDenied error. } catch (Exception) { // I don't expect to get other exceptions. } finally { if (tokenUserInfo != IntPtr.Zero) { Marshal.FreeHGlobal(tokenUserInfo); } if (processTokenHandler != IntPtr.Zero) { Win32Native.CloseHandle(processTokenHandler); } } #endif return userName; } #endregion Privates } #endregion GetProcessCommand #region WaitProcessCommand /// /// This class implements the Wait-process command. /// [Cmdlet(VerbsLifecycle.Wait, "Process", DefaultParameterSetName = "Name", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097146")] public sealed class WaitProcessCommand : ProcessBaseCommand { #region Parameters /// /// Specifies the process IDs of the processes to be waited on. /// [Parameter( ParameterSetName = "Id", Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] [Alias("PID", "ProcessId")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public int[] Id { get { return processIds; } set { myMode = MatchMode.ById; processIds = value; } } /// /// Name of the processes to wait on for termination. /// [Parameter( ParameterSetName = "Name", Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Alias("ProcessName")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] Name { get { return processNames; } set { myMode = MatchMode.ByName; processNames = value; } } /// /// If specified, wait for this number of seconds. /// [Parameter(Position = 1)] [Alias("TimeoutSec")] [ValidateNotNullOrEmpty] [ValidateRange(0, 32767)] public int Timeout { get { return _timeout; } set { _timeout = value; _timeOutSpecified = true; } } private int _timeout = 0; private bool _timeOutSpecified; #endregion Parameters private bool _disposed = false; #region IDisposable /// /// Dispose method of IDisposable interface. /// public void Dispose() { if (_disposed == false) { if (_waitHandle != null) { _waitHandle.Dispose(); _waitHandle = null; } _disposed = true; } } #endregion #region private methods // Handle Exited event and display process information. private void myProcess_Exited(object sender, System.EventArgs e) { if (0 == System.Threading.Interlocked.Decrement(ref _numberOfProcessesToWaitFor)) { if (_waitHandle != null) { _waitHandle.Set(); } } } #endregion #region Overrides private List _processList = new List(); // Wait handle which is used by thread to sleep. private ManualResetEvent _waitHandle; private int _numberOfProcessesToWaitFor; /// /// Gets the list of process. /// protected override void ProcessRecord() { // adding the processes into the list foreach (Process process in MatchingProcesses()) { // Idle process has processid zero,so handle that because we cannot wait on it. if (process.Id == 0) { WriteNonTerminatingError(process, null, ProcessResources.WaitOnIdleProcess, "WaitOnIdleProcess", ErrorCategory.ObjectNotFound); continue; } // It cannot wait on itself if (process.Id.Equals(System.Diagnostics.Process.GetCurrentProcess().Id)) { WriteNonTerminatingError(process, null, ProcessResources.WaitOnItself, "WaitOnItself", ErrorCategory.ObjectNotFound); continue; } _processList.Add(process); } } /// /// Wait for the process to terminate. /// protected override void EndProcessing() { _waitHandle = new ManualResetEvent(false); foreach (Process process in _processList) { try { if (!process.HasExited) { process.EnableRaisingEvents = true; process.Exited += myProcess_Exited; if (!process.HasExited) { System.Threading.Interlocked.Increment(ref _numberOfProcessesToWaitFor); } } } catch (Win32Exception exception) { WriteNonTerminatingError(process, exception, ProcessResources.ProcessIsNotTerminated, "ProcessNotTerminated", ErrorCategory.CloseError); } } if (_numberOfProcessesToWaitFor > 0) { if (_timeOutSpecified) { _waitHandle.WaitOne(_timeout * 1000); } else { _waitHandle.WaitOne(); } } foreach (Process process in _processList) { try { if (!process.HasExited) { string message = StringUtil.Format(ProcessResources.ProcessNotTerminated, new object[] { process.ProcessName, process.Id }); ErrorRecord errorRecord = new ErrorRecord(new TimeoutException(message), "ProcessNotTerminated", ErrorCategory.CloseError, process); WriteError(errorRecord); } } catch (Win32Exception exception) { WriteNonTerminatingError(process, exception, ProcessResources.ProcessIsNotTerminated, "ProcessNotTerminated", ErrorCategory.CloseError); } } } /// /// StopProcessing. /// protected override void StopProcessing() { if (_waitHandle != null) { _waitHandle.Set(); } } #endregion Overrides } #endregion WaitProcessCommand #region StopProcessCommand /// /// This class implements the stop-process command. /// /// /// Processes will be sorted before being stopped. PM confirms /// that this should be fine. /// [Cmdlet(VerbsLifecycle.Stop, "Process", DefaultParameterSetName = "Id", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097058")] [OutputType(typeof(Process))] public sealed class StopProcessCommand : ProcessBaseCommand { #region Parameters /// /// Has the list of process names on which to this command will work. /// [Parameter( ParameterSetName = "Name", Mandatory = true, ValueFromPipelineByPropertyName = true)] [Alias("ProcessName")] public string[] Name { get { return processNames; } set { processNames = value; myMode = MatchMode.ByName; } } /// /// Gets/sets an array of process IDs. /// [Parameter( Position = 0, ParameterSetName = "Id", Mandatory = true, ValueFromPipelineByPropertyName = true)] public int[] Id { get { return processIds; } set { myMode = MatchMode.ById; processIds = value; } } /// /// Gets/sets an array of objects. /// [Parameter( Position = 0, ParameterSetName = "InputObject", Mandatory = true, ValueFromPipeline = true)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public new Process[] InputObject { get { return base.InputObject; } set { base.InputObject = value; } } private bool _passThru; /// /// The updated process object should be passed down the pipeline. /// [Parameter] public SwitchParameter PassThru { get { return _passThru; } set { _passThru = value; } } /// /// Specifies whether to force a process to kill /// even if it has dependent services. /// /// [Parameter] [ValidateNotNullOrEmpty] public SwitchParameter Force { get; set; } #endregion Parameters #region Overrides /// /// Kill the processes. /// It is a non-terminating error if the Process.Kill() operation fails. /// protected override void ProcessRecord() { if (myMode == MatchMode.All || (myMode == MatchMode.ByName && processNames == null)) { Diagnostics.Assert(false, "trying to kill all processes"); throw PSTraceSource.NewInvalidOperationException(); } foreach (Process process in MatchingProcesses()) { // confirm the operation first // this is always false if WhatIf is set // 2005-06-21 Moved this ahead of the hasExited check string targetString = StringUtil.Format( ProcessResources.ProcessNameForConfirmation, SafeGetProcessName(process), SafeGetProcessId(process)); if (!ShouldProcess(targetString)) { continue; } try { // Many properties including Name are not available if the process has exited. // If this is the case, we skip the process. If the process is from a remote // machine, then we generate a non-terminating error because .NET doesn't support // terminate a remote process. if (process.HasExited) { if (PassThru) WriteObject(process); continue; } } catch (NotSupportedException ex) { WriteNonTerminatingError( process, ex, ProcessResources.CouldNotStopProcess, "CouldNotStopProcess", ErrorCategory.InvalidOperation); continue; } catch (Win32Exception ex) { WriteNonTerminatingError( process, ex, ProcessResources.CouldNotStopProcess, "CouldNotStopProcess", ErrorCategory.CloseError); continue; } try { if (Process.GetCurrentProcess().Id == SafeGetProcessId(process)) { _shouldKillCurrentProcess = true; continue; } if (Platform.IsWindows && !Force) { if (!IsProcessOwnedByCurrentUser(process)) { string message = StringUtil.Format( ProcessResources.ConfirmStopProcess, SafeGetProcessName(process), SafeGetProcessId(process)); // caption: null = default caption if (!ShouldContinue(message, null, ref _yesToAll, ref _noToAll)) continue; } } // If the process is svchost stop all the dependent services before killing process if (string.Equals(SafeGetProcessName(process), "SVCHOST", StringComparison.OrdinalIgnoreCase)) { StopDependentService(process); } if (!process.HasExited) { process.Kill(); } } catch (Win32Exception exception) { if (!TryHasExited(process)) { WriteNonTerminatingError( process, exception, ProcessResources.CouldNotStopProcess, "CouldNotStopProcess", ErrorCategory.CloseError); continue; } } catch (InvalidOperationException exception) { if (!TryHasExited(process)) { WriteNonTerminatingError( process, exception, ProcessResources.CouldNotStopProcess, "CouldNotStopProcess", ErrorCategory.CloseError); continue; } } if (PassThru) WriteObject(process); } } /// /// Kill the current process here. /// protected override void EndProcessing() { if (_shouldKillCurrentProcess) { StopProcess(Process.GetCurrentProcess()); } } #endregion Overrides #region Private /// /// Should the current powershell process to be killed. /// private bool _shouldKillCurrentProcess; /// /// Boolean variables to display the warning using ShouldContinue. /// private bool _yesToAll, _noToAll; /// /// Current windows user name. /// private string _currentUserName; /// /// Gets the owner of the process. /// /// /// Returns the owner. private bool IsProcessOwnedByCurrentUser(Process process) { const uint TOKEN_QUERY = 0x0008; IntPtr ph = IntPtr.Zero; try { if (Win32Native.OpenProcessToken(process.Handle, TOKEN_QUERY, out ph)) { if (_currentUserName == null) { using (var currentUser = WindowsIdentity.GetCurrent()) { _currentUserName = currentUser.Name; } } using (var processUser = new WindowsIdentity(ph)) { return string.Equals(processUser.Name, _currentUserName, StringComparison.OrdinalIgnoreCase); } } } catch (IdentityNotMappedException) { // Catching IdentityMappedException // Need not throw error. } catch (ArgumentException) { // Catching ArgumentException. In Win2k3 Token is zero // Need not throw error. } finally { if (ph != IntPtr.Zero) { Win32Native.CloseHandle(ph); } } return false; } /// /// Stop the service that depends on the process and its child services. /// /// private void StopDependentService(Process process) { string queryString = "Select * From Win32_Service Where ProcessId=" + SafeGetProcessId(process) + " And State !='Stopped'"; try { using (CimSession cimSession = CimSession.Create(null)) { IEnumerable serviceList = cimSession.QueryInstances("root/cimv2", "WQL", queryString); foreach (CimInstance oService in serviceList) { string serviceName = oService.CimInstanceProperties["Name"].Value.ToString(); using (var service = new System.ServiceProcess.ServiceController(serviceName)) { try { service.Stop(); // Wait 2 sec for the status to become 'Stopped' service.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 2)); } catch (Win32Exception) { } catch (InvalidOperationException) { } catch (System.ServiceProcess.TimeoutException) { } } } } } catch (CimException ex) { var errorRecord = new ErrorRecord(ex, "GetCimException", ErrorCategory.InvalidOperation, null); WriteError(errorRecord); } } /// /// Stops the given process throws non terminating error if can't. /// Process to be stopped. /// True if process stopped successfully else false. /// private void StopProcess(Process process) { Exception exception = null; try { if (!process.HasExited) { process.Kill(); } } catch (Win32Exception e) { exception = e; } catch (InvalidOperationException e) { exception = e; } if (exception != null) { if (!TryHasExited(process)) { // This process could not be stopped, // so write a non-terminating error. WriteNonTerminatingError( process, exception, ProcessResources.CouldNotStopProcess, "CouldNotStopProcess", ErrorCategory.CloseError); } } } #endregion Private } #endregion StopProcessCommand #region DebugProcessCommand /// /// This class implements the Debug-process command. /// [Cmdlet(VerbsDiagnostic.Debug, "Process", DefaultParameterSetName = "Name", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096809")] public sealed class DebugProcessCommand : ProcessBaseCommand { #region Parameters /// /// Specifies the process IDs of the processes to be waited on. /// [Parameter( ParameterSetName = "Id", Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] [Alias("PID", "ProcessId")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public int[] Id { get { return processIds; } set { myMode = MatchMode.ById; processIds = value; } } /// /// Name of the processes to wait on for termination. /// [Parameter( ParameterSetName = "Name", Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Alias("ProcessName")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] Name { get { return processNames; } set { myMode = MatchMode.ByName; processNames = value; } } #endregion Parameters #region Overrides /// /// Gets the list of process and attach the debugger to the processes. /// protected override void ProcessRecord() { foreach (Process process in MatchingProcesses()) { string targetMessage = StringUtil.Format( ProcessResources.ProcessNameForConfirmation, SafeGetProcessName(process), SafeGetProcessId(process)); if (!ShouldProcess(targetMessage)) { continue; } // Sometimes Idle process has processid zero,so handle that because we cannot attach debugger to it. if (process.Id == 0) { WriteNonTerminatingError( process, null, ProcessResources.NoDebuggerFound, "NoDebuggerFound", ErrorCategory.ObjectNotFound); continue; } try { // If the process has exited, we skip it. If the process is from a remote // machine, then we generate a non-terminating error. if (process.HasExited) { continue; } } catch (NotSupportedException ex) { WriteNonTerminatingError( process, ex, ProcessResources.CouldNotDebugProcess, "CouldNotDebugProcess", ErrorCategory.InvalidOperation); continue; } catch (Win32Exception ex) { // This process could not be stopped, so write a non-terminating error. WriteNonTerminatingError( process, ex, ProcessResources.CouldNotDebugProcess, "CouldNotDebugProcess", ErrorCategory.CloseError); continue; } AttachDebuggerToProcess(process); } } #endregion Overrides /// /// Attach debugger to the process. /// private void AttachDebuggerToProcess(Process process) { string searchQuery = "Select * From Win32_Process Where ProcessId=" + SafeGetProcessId(process); using (CimSession cimSession = CimSession.Create(null)) { IEnumerable processCollection = cimSession.QueryInstances("root/cimv2", "WQL", searchQuery); foreach (CimInstance processInstance in processCollection) { try { // Call the AttachDebugger method CimMethodResult result = cimSession.InvokeMethod(processInstance, "AttachDebugger", null); int returnCode = Convert.ToInt32(result.ReturnValue.Value, System.Globalization.CultureInfo.CurrentCulture); if (returnCode != 0) { var ex = new InvalidOperationException(MapReturnCodeToErrorMessage(returnCode)); WriteNonTerminatingError( process, ex, ProcessResources.CouldNotDebugProcess, "CouldNotDebugProcess", ErrorCategory.InvalidOperation); } } catch (CimException e) { string message = e.Message; if (!string.IsNullOrEmpty(message)) { message = message.Trim(); } var errorRecord = new ErrorRecord( new InvalidOperationException(StringUtil.Format(ProcessResources.DebuggerError, message)), "GetCimException", ErrorCategory.InvalidOperation, null); WriteError(errorRecord); } } } } /// /// Map the return code from 'AttachDebugger' to error message. /// private string MapReturnCodeToErrorMessage(int returnCode) { string errorMessage = string.Empty; switch (returnCode) { case 2: errorMessage = ProcessResources.AttachDebuggerReturnCode2; break; case 3: errorMessage = ProcessResources.AttachDebuggerReturnCode3; break; case 8: errorMessage = ProcessResources.AttachDebuggerReturnCode8; break; case 9: errorMessage = ProcessResources.AttachDebuggerReturnCode9; break; case 21: errorMessage = ProcessResources.AttachDebuggerReturnCode21; break; default: Diagnostics.Assert(false, "Unreachable code."); break; } return errorMessage; } } #endregion DebugProcessCommand #region StartProcessCommand /// /// This class implements the Start-process command. /// [Cmdlet(VerbsLifecycle.Start, "Process", DefaultParameterSetName = "Default", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097141")] [OutputType(typeof(Process))] public sealed class StartProcessCommand : PSCmdlet, IDisposable { private ManualResetEvent _waithandle = null; private bool _isDefaultSetParameterSpecified = false; #region Parameters /// /// Path/FileName of the process to start. /// [Parameter(Mandatory = true, Position = 0)] [ValidateNotNullOrEmpty] [Alias("PSPath", "Path")] public string FilePath { get; set; } /// /// Arguments for the process. /// [Parameter(Position = 1)] [Alias("Args")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] ArgumentList { get; set; } /// /// Credentials for the process. /// [Parameter(ParameterSetName = "Default")] [Alias("RunAs")] [ValidateNotNullOrEmpty] [Credential] public PSCredential Credential { get { return _credential; } set { _credential = value; _isDefaultSetParameterSpecified = true; } } private PSCredential _credential; /// /// Working directory of the process. /// [Parameter] [ValidateNotNullOrEmpty] public string WorkingDirectory { get; set; } /// /// Load user profile from registry. /// [Parameter(ParameterSetName = "Default")] [Alias("Lup")] public SwitchParameter LoadUserProfile { get { return _loaduserprofile; } set { _loaduserprofile = value; _isDefaultSetParameterSpecified = true; } } private SwitchParameter _loaduserprofile = SwitchParameter.Present; /// /// Starts process in a new window. /// [Parameter(ParameterSetName = "Default")] [Alias("nnw")] public SwitchParameter NoNewWindow { get { return _nonewwindow; } set { _nonewwindow = value; _isDefaultSetParameterSpecified = true; } } private SwitchParameter _nonewwindow; /// /// PassThru parameter. /// [Parameter] public SwitchParameter PassThru { get; set; } /// /// Redirect error. /// [Parameter(ParameterSetName = "Default")] [Alias("RSE")] [ValidateNotNullOrEmpty] public string RedirectStandardError { get { return _redirectstandarderror; } set { _redirectstandarderror = value; _isDefaultSetParameterSpecified = true; } } private string _redirectstandarderror; /// /// Redirect input. /// [Parameter(ParameterSetName = "Default")] [Alias("RSI")] [ValidateNotNullOrEmpty] public string RedirectStandardInput { get { return _redirectstandardinput; } set { _redirectstandardinput = value; _isDefaultSetParameterSpecified = true; } } private string _redirectstandardinput; /// /// Redirect output. /// [Parameter(ParameterSetName = "Default")] [Alias("RSO")] [ValidateNotNullOrEmpty] public string RedirectStandardOutput { get { return _redirectstandardoutput; } set { _redirectstandardoutput = value; _isDefaultSetParameterSpecified = true; } } private string _redirectstandardoutput; /// /// Verb. /// /// /// The 'Verb' parameter is only supported on Windows Desktop. /// [Parameter(ParameterSetName = "UseShellExecute")] [ValidateNotNullOrEmpty] public string Verb { get; set; } /// /// Window style of the process window. /// [Parameter] [ValidateNotNullOrEmpty] public ProcessWindowStyle WindowStyle { get { return _windowstyle; } set { _windowstyle = value; _windowstyleSpecified = true; } } private ProcessWindowStyle _windowstyle = ProcessWindowStyle.Normal; private bool _windowstyleSpecified = false; /// /// Wait for the process to terminate. /// [Parameter] public SwitchParameter Wait { get; set; } /// /// Default Environment. /// [Parameter(ParameterSetName = "Default")] public SwitchParameter UseNewEnvironment { get { return _UseNewEnvironment; } set { _UseNewEnvironment = value; _isDefaultSetParameterSpecified = true; } } private SwitchParameter _UseNewEnvironment; #endregion #region overrides /// /// BeginProcessing. /// protected override void BeginProcessing() { string message = string.Empty; // -Verb and -WindowStyle are not supported on non-Windows platforms as well as Windows headless SKUs if (Platform.IsWindowsDesktop) { // Parameters '-NoNewWindow' and '-WindowStyle' are both valid on full windows SKUs. if (_nonewwindow && _windowstyleSpecified) { message = StringUtil.Format(ProcessResources.ContradictParametersSpecified, "-NoNewWindow", "-WindowStyle"); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } } else { if (this.ParameterSetName.Equals("UseShellExecute")) { message = StringUtil.Format(ProcessResources.ParameterNotSupportedOnPSEdition, "-Verb", "Start-Process"); } else if (_windowstyleSpecified) { message = StringUtil.Format(ProcessResources.ParameterNotSupportedOnPSEdition, "-WindowStyle", "Start-Process"); } if (!string.IsNullOrEmpty(message)) { ErrorRecord er = new ErrorRecord(new NotSupportedException(message), "NotSupportedException", ErrorCategory.NotImplemented, null); ThrowTerminatingError(er); } } ProcessStartInfo startInfo = new ProcessStartInfo(); // Use ShellExecute by default if we are running on full windows SKUs startInfo.UseShellExecute = Platform.IsWindowsDesktop; // Path = Mandatory parameter -> Will not be empty. try { CommandInfo cmdinfo = CommandDiscovery.LookupCommandInfo( FilePath, CommandTypes.Application | CommandTypes.ExternalScript, SearchResolutionOptions.None, CommandOrigin.Internal, this.Context); startInfo.FileName = cmdinfo.Definition; } catch (CommandNotFoundException) { startInfo.FileName = FilePath; #if UNIX // Arguments are passed incorrectly to the executable used for ShellExecute and not to filename https://github.com/dotnet/corefx/issues/30718 // so don't use ShellExecute if arguments are specified // Linux relies on `xdg-open` and macOS relies on `open` which behave differently than Windows ShellExecute when running console commands // as a new console will be opened. So to avoid that, we only use ShellExecute on non-Windows if the filename is not an actual command (like a URI) startInfo.UseShellExecute = (ArgumentList == null); #endif } if (ArgumentList != null) { startInfo.Arguments = string.Join(' ', ArgumentList); } if (WorkingDirectory != null) { // WorkingDirectory -> Not Exist -> Throw Error WorkingDirectory = ResolveFilePath(WorkingDirectory); if (!Directory.Exists(WorkingDirectory)) { message = StringUtil.Format(ProcessResources.InvalidInput, "WorkingDirectory"); ErrorRecord er = new ErrorRecord(new DirectoryNotFoundException(message), "DirectoryNotFoundException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } startInfo.WorkingDirectory = WorkingDirectory; } else { // Working Directory not specified -> Assign Current Path. startInfo.WorkingDirectory = PathUtils.ResolveFilePath(this.SessionState.Path.CurrentFileSystemLocation.Path, this, isLiteralPath: true); } if (this.ParameterSetName.Equals("Default")) { if (_isDefaultSetParameterSpecified) { startInfo.UseShellExecute = false; } if (_UseNewEnvironment) { startInfo.EnvironmentVariables.Clear(); LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine)); LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User)); } startInfo.WindowStyle = _windowstyle; if (_nonewwindow) { startInfo.CreateNoWindow = _nonewwindow; } #if !UNIX startInfo.LoadUserProfile = _loaduserprofile; #endif if (_credential != null) { NetworkCredential nwcredential = _credential.GetNetworkCredential(); startInfo.UserName = nwcredential.UserName; if (string.IsNullOrEmpty(nwcredential.Domain)) { startInfo.Domain = "."; } else { startInfo.Domain = nwcredential.Domain; } startInfo.Password = _credential.Password; } // RedirectionInput File Check -> Not Exist -> Throw Error if (_redirectstandardinput != null) { _redirectstandardinput = ResolveFilePath(_redirectstandardinput); if (!File.Exists(_redirectstandardinput)) { message = StringUtil.Format(ProcessResources.InvalidInput, "RedirectStandardInput '" + this.RedirectStandardInput + "'"); ErrorRecord er = new ErrorRecord(new FileNotFoundException(message), "FileNotFoundException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } } // RedirectionInput == RedirectionOutput -> Throw Error if (_redirectstandardinput != null && _redirectstandardoutput != null) { _redirectstandardinput = ResolveFilePath(_redirectstandardinput); _redirectstandardoutput = ResolveFilePath(_redirectstandardoutput); if (_redirectstandardinput.Equals(_redirectstandardoutput, StringComparison.OrdinalIgnoreCase)) { message = StringUtil.Format(ProcessResources.DuplicateEntry, "RedirectStandardInput", "RedirectStandardOutput"); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } } // RedirectionInput == RedirectionError -> Throw Error if (_redirectstandardinput != null && _redirectstandarderror != null) { _redirectstandardinput = ResolveFilePath(_redirectstandardinput); _redirectstandarderror = ResolveFilePath(_redirectstandarderror); if (_redirectstandardinput.Equals(_redirectstandarderror, StringComparison.OrdinalIgnoreCase)) { message = StringUtil.Format(ProcessResources.DuplicateEntry, "RedirectStandardInput", "RedirectStandardError"); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } } // RedirectionOutput == RedirectionError -> Throw Error if (_redirectstandardoutput != null && _redirectstandarderror != null) { _redirectstandarderror = ResolveFilePath(_redirectstandarderror); _redirectstandardoutput = ResolveFilePath(_redirectstandardoutput); if (_redirectstandardoutput.Equals(_redirectstandarderror, StringComparison.OrdinalIgnoreCase)) { message = StringUtil.Format(ProcessResources.DuplicateEntry, "RedirectStandardOutput", "RedirectStandardError"); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } } } else if (ParameterSetName.Equals("UseShellExecute")) { if (Verb != null) { startInfo.Verb = Verb; } startInfo.WindowStyle = _windowstyle; } string targetMessage = StringUtil.Format(ProcessResources.StartProcessTarget, startInfo.FileName, startInfo.Arguments.Trim()); if (!ShouldProcess(targetMessage)) { return; } Process process = Start(startInfo); if (PassThru.IsPresent) { if (process != null) { WriteObject(process); } else { message = StringUtil.Format(ProcessResources.CannotStarttheProcess); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } } if (Wait.IsPresent) { if (process != null) { if (!process.HasExited) { #if UNIX process.WaitForExit(); #else _waithandle = new ManualResetEvent(false); // Create and start the job object ProcessCollection jobObject = new ProcessCollection(); if (jobObject.AssignProcessToJobObject(process)) { // Wait for the job object to finish jobObject.WaitOne(_waithandle); } else if (!process.HasExited) { // WinBlue: 27537 Start-Process -Wait doesn't work in a remote session on Windows 7 or lower. process.Exited += myProcess_Exited; process.EnableRaisingEvents = true; process.WaitForExit(); } #endif } } else { message = StringUtil.Format(ProcessResources.CannotStarttheProcess); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } } } /// /// Implements ^c, after creating a process. /// protected override void StopProcessing() { if (_waithandle != null) { _waithandle.Set(); } } #endregion #region IDisposable Overrides /// /// Dispose WaitHandle used to honor -Wait parameter. /// public void Dispose() { Dispose(true); System.GC.SuppressFinalize(this); } private void Dispose(bool isDisposing) { if (_waithandle != null) { _waithandle.Dispose(); _waithandle = null; } } #endregion #region Private Methods /// /// When Process exits the wait handle is set. /// private void myProcess_Exited(object sender, System.EventArgs e) { if (_waithandle != null) { _waithandle.Set(); } } private string ResolveFilePath(string path) { string filepath = PathUtils.ResolveFilePath(path, this); return filepath; } private void LoadEnvironmentVariable(ProcessStartInfo startinfo, IDictionary EnvironmentVariables) { var processEnvironment = startinfo.EnvironmentVariables; foreach (DictionaryEntry entry in EnvironmentVariables) { if (processEnvironment.ContainsKey(entry.Key.ToString())) { processEnvironment.Remove(entry.Key.ToString()); } if (entry.Key.ToString().Equals("PATH")) { processEnvironment.Add(entry.Key.ToString(), Environment.GetEnvironmentVariable(entry.Key.ToString(), EnvironmentVariableTarget.Machine) + ";" + Environment.GetEnvironmentVariable(entry.Key.ToString(), EnvironmentVariableTarget.User)); } else { processEnvironment.Add(entry.Key.ToString(), entry.Value.ToString()); } } } private Process Start(ProcessStartInfo startInfo) { Process process = null; if (startInfo.UseShellExecute) { process = StartWithShellExecute(startInfo); } else { #if UNIX process = new Process() { StartInfo = startInfo }; SetupInputOutputRedirection(process); process.Start(); if (process.StartInfo.RedirectStandardOutput) { process.BeginOutputReadLine(); } if (process.StartInfo.RedirectStandardError) { process.BeginErrorReadLine(); } if (process.StartInfo.RedirectStandardInput) { WriteToStandardInput(process); } #else process = StartWithCreateProcess(startInfo); #endif } return process; } #if UNIX private StreamWriter _outputWriter; private StreamWriter _errorWriter; private void StdOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { if (!string.IsNullOrEmpty(outLine.Data)) { _outputWriter.WriteLine(outLine.Data); _outputWriter.Flush(); } } private void StdErrorHandler(object sendingProcess, DataReceivedEventArgs outLine) { if (!string.IsNullOrEmpty(outLine.Data)) { _errorWriter.WriteLine(outLine.Data); _errorWriter.Flush(); } } private void ExitHandler(object sendingProcess, System.EventArgs e) { // To avoid a race condition with Std*Handler, let's wait a bit before closing the streams // System.Timer is not supported in CoreCLR, so let's spawn a new thread to do the wait Thread delayedStreamClosing = new Thread(StreamClosing); delayedStreamClosing.Start(); } private void StreamClosing() { Thread.Sleep(1000); if (_outputWriter != null) { _outputWriter.Dispose(); } if (_errorWriter != null) { _errorWriter.Dispose(); } } private void SetupInputOutputRedirection(Process p) { if (_redirectstandardinput != null) { p.StartInfo.RedirectStandardInput = true; _redirectstandardinput = ResolveFilePath(_redirectstandardinput); } else { p.StartInfo.RedirectStandardInput = false; } if (_redirectstandardoutput != null) { p.StartInfo.RedirectStandardOutput = true; _redirectstandardoutput = ResolveFilePath(_redirectstandardoutput); p.OutputDataReceived += new DataReceivedEventHandler(StdOutputHandler); // Can't do StreamWriter(string) in coreCLR _outputWriter = new StreamWriter(new FileStream(_redirectstandardoutput, FileMode.Create)); } else { p.StartInfo.RedirectStandardOutput = false; _outputWriter = null; } if (_redirectstandarderror != null) { p.StartInfo.RedirectStandardError = true; _redirectstandarderror = ResolveFilePath(_redirectstandarderror); p.ErrorDataReceived += new DataReceivedEventHandler(StdErrorHandler); // Can't do StreamWriter(string) in coreCLR _errorWriter = new StreamWriter(new FileStream(_redirectstandarderror, FileMode.Create)); } else { p.StartInfo.RedirectStandardError = false; _errorWriter = null; } p.EnableRaisingEvents = true; p.Exited += new EventHandler(ExitHandler); } private void WriteToStandardInput(Process p) { StreamWriter writer = p.StandardInput; using (StreamReader reader = new StreamReader(new FileStream(_redirectstandardinput, FileMode.Open))) { string line = reader.ReadToEnd(); writer.WriteLine(line); } writer.Dispose(); } #else private SafeFileHandle GetSafeFileHandleForRedirection(string RedirectionPath, uint dwCreationDisposition) { System.IntPtr hFileHandle = System.IntPtr.Zero; ProcessNativeMethods.SECURITY_ATTRIBUTES lpSecurityAttributes = new ProcessNativeMethods.SECURITY_ATTRIBUTES(); hFileHandle = ProcessNativeMethods.CreateFileW(RedirectionPath, ProcessNativeMethods.GENERIC_READ | ProcessNativeMethods.GENERIC_WRITE, ProcessNativeMethods.FILE_SHARE_WRITE | ProcessNativeMethods.FILE_SHARE_READ, lpSecurityAttributes, dwCreationDisposition, ProcessNativeMethods.FILE_ATTRIBUTE_NORMAL, System.IntPtr.Zero); if (hFileHandle == System.IntPtr.Zero) { int error = Marshal.GetLastWin32Error(); Win32Exception win32ex = new Win32Exception(error); string message = StringUtil.Format(ProcessResources.InvalidStartProcess, win32ex.Message); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } SafeFileHandle sf = new SafeFileHandle(hFileHandle, true); return sf; } private static StringBuilder BuildCommandLine(string executableFileName, string arguments) { StringBuilder builder = new StringBuilder(); string str = executableFileName.Trim(); bool flag = str.StartsWith('"') && str.EndsWith('"'); if (!flag) { builder.Append('"'); } builder.Append(str); if (!flag) { builder.Append('"'); } if (!string.IsNullOrEmpty(arguments)) { builder.Append(' '); builder.Append(arguments); } return builder; } private static byte[] ConvertEnvVarsToByteArray(StringDictionary sd) { string[] array = new string[sd.Count]; byte[] bytes = null; sd.Keys.CopyTo(array, 0); string[] strArray2 = new string[sd.Count]; sd.Values.CopyTo(strArray2, 0); Array.Sort(array, strArray2, StringComparer.OrdinalIgnoreCase); StringBuilder builder = new StringBuilder(); for (int i = 0; i < sd.Count; i++) { builder.Append(array[i]);// builder.Append('='); builder.Append(strArray2[i]); builder.Append('\0'); } builder.Append('\0'); // Use Unicode encoding bytes = Encoding.Unicode.GetBytes(builder.ToString()); if (bytes.Length > 0xffff) { throw new InvalidOperationException("EnvironmentBlockTooLong"); } return bytes; } private void SetStartupInfo(ProcessStartInfo startinfo, ref ProcessNativeMethods.STARTUPINFO lpStartupInfo, ref int creationFlags) { // RedirectionStandardInput if (_redirectstandardinput != null) { startinfo.RedirectStandardInput = true; _redirectstandardinput = ResolveFilePath(_redirectstandardinput); lpStartupInfo.hStdInput = GetSafeFileHandleForRedirection(_redirectstandardinput, ProcessNativeMethods.OPEN_EXISTING); } else { lpStartupInfo.hStdInput = new SafeFileHandle(ProcessNativeMethods.GetStdHandle(-10), false); } // RedirectionStandardOutput if (_redirectstandardoutput != null) { startinfo.RedirectStandardOutput = true; _redirectstandardoutput = ResolveFilePath(_redirectstandardoutput); lpStartupInfo.hStdOutput = GetSafeFileHandleForRedirection(_redirectstandardoutput, ProcessNativeMethods.CREATE_ALWAYS); } else { lpStartupInfo.hStdOutput = new SafeFileHandle(ProcessNativeMethods.GetStdHandle(-11), false); } // RedirectionStandardError if (_redirectstandarderror != null) { startinfo.RedirectStandardError = true; _redirectstandarderror = ResolveFilePath(_redirectstandarderror); lpStartupInfo.hStdError = GetSafeFileHandleForRedirection(_redirectstandarderror, ProcessNativeMethods.CREATE_ALWAYS); } else { lpStartupInfo.hStdError = new SafeFileHandle(ProcessNativeMethods.GetStdHandle(-12), false); } // STARTF_USESTDHANDLES lpStartupInfo.dwFlags = 0x100; if (startinfo.CreateNoWindow) { // No new window: Inherit the parent process's console window creationFlags = 0x00000000; } else { // CREATE_NEW_CONSOLE creationFlags |= 0x00000010; // STARTF_USESHOWWINDOW lpStartupInfo.dwFlags |= 0x00000001; // On headless SKUs like NanoServer and IoT, window style can only be the default value 'Normal'. switch (startinfo.WindowStyle) { case ProcessWindowStyle.Normal: // SW_SHOWNORMAL lpStartupInfo.wShowWindow = 1; break; case ProcessWindowStyle.Minimized: // SW_SHOWMINIMIZED lpStartupInfo.wShowWindow = 2; break; case ProcessWindowStyle.Maximized: // SW_SHOWMAXIMIZED lpStartupInfo.wShowWindow = 3; break; case ProcessWindowStyle.Hidden: // SW_HIDE lpStartupInfo.wShowWindow = 0; break; } } // Create the new process suspended so we have a chance to get a corresponding Process object in case it terminates quickly. creationFlags |= 0x00000004; } /// /// This method will be used on all windows platforms, both full desktop and headless SKUs. /// private Process StartWithCreateProcess(ProcessStartInfo startinfo) { ProcessNativeMethods.STARTUPINFO lpStartupInfo = new ProcessNativeMethods.STARTUPINFO(); SafeNativeMethods.PROCESS_INFORMATION lpProcessInformation = new SafeNativeMethods.PROCESS_INFORMATION(); int error = 0; GCHandle pinnedEnvironmentBlock = new GCHandle(); IntPtr AddressOfEnvironmentBlock = IntPtr.Zero; string message = string.Empty; // building the cmdline with the file name given and it's arguments StringBuilder cmdLine = BuildCommandLine(startinfo.FileName, startinfo.Arguments); try { int creationFlags = 0; SetStartupInfo(startinfo, ref lpStartupInfo, ref creationFlags); // We follow the logic: // - Ignore `UseNewEnvironment` when we run a process as another user. // Setting initial environment variables makes sense only for current user. // - Set environment variables if they present in ProcessStartupInfo. if (!UseNewEnvironment) { var environmentVars = startinfo.EnvironmentVariables; if (environmentVars != null) { // All Windows Operating Systems that we support are Windows NT systems, so we use Unicode for environment. creationFlags |= 0x400; pinnedEnvironmentBlock = GCHandle.Alloc(ConvertEnvVarsToByteArray(environmentVars), GCHandleType.Pinned); AddressOfEnvironmentBlock = pinnedEnvironmentBlock.AddrOfPinnedObject(); } } bool flag; if (_credential != null) { // Run process as another user. ProcessNativeMethods.LogonFlags logonFlags = 0; if (startinfo.LoadUserProfile) { logonFlags = ProcessNativeMethods.LogonFlags.LOGON_WITH_PROFILE; } IntPtr password = IntPtr.Zero; try { password = (startinfo.Password == null) ? Marshal.StringToCoTaskMemUni(string.Empty) : Marshal.SecureStringToCoTaskMemUnicode(startinfo.Password); flag = ProcessNativeMethods.CreateProcessWithLogonW(startinfo.UserName, startinfo.Domain, password, logonFlags, null, cmdLine, creationFlags, AddressOfEnvironmentBlock, startinfo.WorkingDirectory, lpStartupInfo, lpProcessInformation); if (!flag) { error = Marshal.GetLastWin32Error(); ErrorRecord er = null; if (error == 0xc1) { message = StringUtil.Format(ProcessResources.InvalidApplication, FilePath); } else if (error == 0x424) { // The API 'CreateProcessWithLogonW' depends on the 'Secondary Logon' service, but the component 'Microsoft-Windows-SecondaryLogonService' // is not installed in OneCoreUAP. We will get error code 0x424 when the service is not available. message = StringUtil.Format(ProcessResources.ParameterNotSupported, "-Credential", "Start-Process"); er = new ErrorRecord(new NotSupportedException(message), "NotSupportedException", ErrorCategory.NotInstalled, null); } else { Win32Exception win32ex = new Win32Exception(error); message = StringUtil.Format(ProcessResources.InvalidStartProcess, win32ex.Message); } er = er ?? new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } goto Label_03AE; } finally { if (password != IntPtr.Zero) { Marshal.ZeroFreeCoTaskMemUnicode(password); } } } // Run process as current user. if (UseNewEnvironment) { // All Windows Operating Systems that we support are Windows NT systems, so we use Unicode for environment. creationFlags |= 0x400; IntPtr token = WindowsIdentity.GetCurrent().Token; if (!ProcessNativeMethods.CreateEnvironmentBlock(out AddressOfEnvironmentBlock, token, false)) { Win32Exception win32ex = new Win32Exception(error); message = StringUtil.Format(ProcessResources.InvalidStartProcess, win32ex.Message); var errorRecord = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(errorRecord); } } ProcessNativeMethods.SECURITY_ATTRIBUTES lpProcessAttributes = new ProcessNativeMethods.SECURITY_ATTRIBUTES(); ProcessNativeMethods.SECURITY_ATTRIBUTES lpThreadAttributes = new ProcessNativeMethods.SECURITY_ATTRIBUTES(); flag = ProcessNativeMethods.CreateProcess(null, cmdLine, lpProcessAttributes, lpThreadAttributes, true, creationFlags, AddressOfEnvironmentBlock, startinfo.WorkingDirectory, lpStartupInfo, lpProcessInformation); if (!flag) { error = Marshal.GetLastWin32Error(); Win32Exception win32ex = new Win32Exception(error); message = StringUtil.Format(ProcessResources.InvalidStartProcess, win32ex.Message); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } Label_03AE: // At this point, we should have a suspended process. Get the .Net Process object, resume the process, and return. Process result = Process.GetProcessById(lpProcessInformation.dwProcessId); ProcessNativeMethods.ResumeThread(lpProcessInformation.hThread); return result; } finally { if (pinnedEnvironmentBlock.IsAllocated) { pinnedEnvironmentBlock.Free(); } else { ProcessNativeMethods.DestroyEnvironmentBlock(AddressOfEnvironmentBlock); } lpStartupInfo.Dispose(); lpProcessInformation.Dispose(); } } #endif /// /// This method will be used only on Windows full desktop. /// private Process StartWithShellExecute(ProcessStartInfo startInfo) { Process result = null; try { result = Process.Start(startInfo); } catch (Win32Exception ex) { string message = StringUtil.Format(ProcessResources.InvalidStartProcess, ex.Message); ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } return result; } #endregion } #if !UNIX /// /// ProcessCollection is a helper class used by Start-Process -Wait cmdlet to monitor the /// child processes created by the main process hosted by the Start-process cmdlet. /// internal class ProcessCollection { /// /// JobObjectHandle is a reference to the job object used to track /// the child processes created by the main process hosted by the Start-Process cmdlet. /// private Microsoft.PowerShell.Commands.SafeJobHandle _jobObjectHandle; /// /// ProcessCollection constructor. /// internal ProcessCollection() { IntPtr jobObjectHandleIntPtr = NativeMethods.CreateJobObject(IntPtr.Zero, null); _jobObjectHandle = new SafeJobHandle(jobObjectHandleIntPtr); } /// /// Start API assigns the process to the JobObject and starts monitoring /// the child processes hosted by the process created by Start-Process cmdlet. /// internal bool AssignProcessToJobObject(Process process) { // Add the process to the job object bool result = NativeMethods.AssignProcessToJobObject(_jobObjectHandle, process.Handle); return result; } /// /// Checks to see if the JobObject is empty (has no assigned processes). /// If job is empty the auto reset event supplied as input would be set. /// internal void CheckJobStatus(object stateInfo) { ManualResetEvent emptyJobAutoEvent = (ManualResetEvent)stateInfo; int dwSize = 0; const int JOB_OBJECT_BASIC_PROCESS_ID_LIST = 3; JOBOBJECT_BASIC_PROCESS_ID_LIST JobList = new JOBOBJECT_BASIC_PROCESS_ID_LIST(); dwSize = Marshal.SizeOf(JobList); if (NativeMethods.QueryInformationJobObject(_jobObjectHandle, JOB_OBJECT_BASIC_PROCESS_ID_LIST, ref JobList, dwSize, IntPtr.Zero)) { if (JobList.NumberOfAssignedProcess == 0) { emptyJobAutoEvent.Set(); } } } /// /// WaitOne blocks the current thread until the current instance receives a signal, using /// a System.TimeSpan to measure the time interval and specifying whether to /// exit the synchronization domain before the wait. /// /// /// WaitHandle to use for waiting on the job object. /// internal void WaitOne(ManualResetEvent waitHandleToUse) { TimerCallback jobObjectStatusCb = this.CheckJobStatus; using (Timer stateTimer = new Timer(jobObjectStatusCb, waitHandleToUse, 0, 1000)) { waitHandleToUse.WaitOne(); } } } /// /// JOBOBJECT_BASIC_PROCESS_ID_LIST Contains the process identifier list for a job object. /// If the job is nested, the process identifier list consists of all /// processes associated with the job and its child jobs. /// [StructLayout(LayoutKind.Sequential)] internal struct JOBOBJECT_BASIC_PROCESS_ID_LIST { /// /// The number of process identifiers to be stored in ProcessIdList. /// public uint NumberOfAssignedProcess; /// /// The number of process identifiers returned in the ProcessIdList buffer. /// If this number is less than NumberOfAssignedProcesses, increase /// the size of the buffer to accommodate the complete list. /// public uint NumberOfProcessIdsInList; /// /// A variable-length array of process identifiers returned by this call. /// Array elements 0 through NumberOfProcessIdsInList minus 1 /// contain valid process identifiers. /// public IntPtr ProcessIdList; } internal static class ProcessNativeMethods { // Fields internal static readonly UInt32 GENERIC_READ = 0x80000000; internal static readonly UInt32 GENERIC_WRITE = 0x40000000; internal static readonly UInt32 FILE_ATTRIBUTE_NORMAL = 0x80000000; internal static readonly UInt32 CREATE_ALWAYS = 2; internal static readonly UInt32 FILE_SHARE_WRITE = 0x00000002; internal static readonly UInt32 FILE_SHARE_READ = 0x00000001; internal static readonly UInt32 OF_READWRITE = 0x00000002; internal static readonly UInt32 OPEN_EXISTING = 3; // Methods [DllImport(PinvokeDllNames.GetStdHandleDllName, SetLastError = true)] public static extern IntPtr GetStdHandle(int whichHandle); [DllImport(PinvokeDllNames.CreateProcessWithLogonWDllName, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CreateProcessWithLogonW(string userName, string domain, IntPtr password, LogonFlags logonFlags, [MarshalAs(UnmanagedType.LPWStr)] string appName, StringBuilder cmdLine, int creationFlags, IntPtr environmentBlock, [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory, STARTUPINFO lpStartupInfo, SafeNativeMethods.PROCESS_INFORMATION lpProcessInformation); [DllImport(PinvokeDllNames.CreateProcessDllName, CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CreateProcess([MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory, STARTUPINFO lpStartupInfo, SafeNativeMethods.PROCESS_INFORMATION lpProcessInformation); [DllImport(PinvokeDllNames.ResumeThreadDllName, CharSet = CharSet.Unicode, SetLastError = true)] public static extern uint ResumeThread(IntPtr threadHandle); [DllImport(PinvokeDllNames.CreateFileDllName, CharSet = CharSet.Unicode, SetLastError = true)] public static extern FileNakedHandle CreateFileW( [In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, ProcessNativeMethods.SECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, System.IntPtr hTemplateFile ); [DllImport("userenv.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit); [DllImport("userenv.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment); [Flags] internal enum LogonFlags { LOGON_NETCREDENTIALS_ONLY = 2, LOGON_WITH_PROFILE = 1 } [StructLayout(LayoutKind.Sequential)] internal class SECURITY_ATTRIBUTES { public int nLength; public SafeLocalMemHandle lpSecurityDescriptor; public bool bInheritHandle; public SECURITY_ATTRIBUTES() { this.nLength = 12; this.bInheritHandle = true; this.lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, true); } } internal sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid { // Methods internal SafeLocalMemHandle() : base(true) { } [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] internal SafeLocalMemHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle) { base.SetHandle(existingHandle); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(PinvokeDllNames.LocalFreeDllName)] private static extern IntPtr LocalFree(IntPtr hMem); protected override bool ReleaseHandle() { return (LocalFree(base.handle) == IntPtr.Zero); } } [StructLayout(LayoutKind.Sequential)] internal class STARTUPINFO { public int cb; public IntPtr lpReserved; public IntPtr lpDesktop; public IntPtr lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwYSize; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public SafeFileHandle hStdInput; public SafeFileHandle hStdOutput; public SafeFileHandle hStdError; public STARTUPINFO() { this.lpReserved = IntPtr.Zero; this.lpDesktop = IntPtr.Zero; this.lpTitle = IntPtr.Zero; this.lpReserved2 = IntPtr.Zero; this.hStdInput = new SafeFileHandle(IntPtr.Zero, false); this.hStdOutput = new SafeFileHandle(IntPtr.Zero, false); this.hStdError = new SafeFileHandle(IntPtr.Zero, false); this.cb = Marshal.SizeOf(this); } public void Dispose(bool disposing) { if (disposing) { if ((this.hStdInput != null) && !this.hStdInput.IsInvalid) { this.hStdInput.Dispose(); this.hStdInput = null; } if ((this.hStdOutput != null) && !this.hStdOutput.IsInvalid) { this.hStdOutput.Dispose(); this.hStdOutput = null; } if ((this.hStdError != null) && !this.hStdError.IsInvalid) { this.hStdError.Dispose(); this.hStdError = null; } } } public void Dispose() { Dispose(true); } } } internal static class SafeNativeMethods { [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(PinvokeDllNames.CloseHandleDllName, SetLastError = true, ExactSpelling = true)] public static extern bool CloseHandle(IntPtr handle); [StructLayout(LayoutKind.Sequential)] internal class PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; public PROCESS_INFORMATION() { this.hProcess = IntPtr.Zero; this.hThread = IntPtr.Zero; } /// /// Dispose. /// public void Dispose() { Dispose(true); } /// /// Dispose. /// /// private void Dispose(bool disposing) { if (disposing) { if (this.hProcess != IntPtr.Zero) { CloseHandle(this.hProcess); this.hProcess = IntPtr.Zero; } if (this.hThread != IntPtr.Zero) { CloseHandle(this.hThread); this.hThread = IntPtr.Zero; } } } } } [SuppressUnmanagedCodeSecurity] internal sealed class SafeJobHandle : SafeHandleZeroOrMinusOneIsInvalid { internal SafeJobHandle(IntPtr jobHandle) : base(true) { base.SetHandle(jobHandle); } protected override bool ReleaseHandle() { return SafeNativeMethods.CloseHandle(base.handle); } } #endif #endregion #region ProcessCommandException /// /// Non-terminating errors occurring in the process noun commands. /// [Serializable] public class ProcessCommandException : SystemException { #region ctors /// /// Unimplemented standard constructor. /// /// Doesn't return. public ProcessCommandException() : base() { throw new NotImplementedException(); } /// /// Standard constructor. /// /// /// Constructed object. public ProcessCommandException(string message) : base(message) { } /// /// Standard constructor. /// /// /// public ProcessCommandException(string message, Exception innerException) : base(message, innerException) { } #endregion ctors #region Serialization /// /// Serialization constructor. /// /// /// /// Constructed object. protected ProcessCommandException( SerializationInfo info, StreamingContext context) : base(info, context) { _processName = info.GetString("ProcessName"); } /// /// Serializer. /// /// Serialization information. /// Streaming context. [SecurityPermissionAttribute( SecurityAction.Demand, SerializationFormatter = true)] public override void GetObjectData( SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); if (info == null) throw new ArgumentNullException(nameof(info)); info.AddValue("ProcessName", _processName); } #endregion Serialization #region Properties /// /// Name of the process which could not be found or operated upon. /// /// public string ProcessName { get { return _processName; } set { _processName = value; } } private string _processName = string.Empty; #endregion Properties } #endregion ProcessCommandException }