// // Copyright (c) 2009 Microsoft Corporation. All rights reserved. // // DISCLAIMER OF WARRANTY: The software is licensed “as-is.” You // bear the risk of using it. Microsoft gives no express warranties, // guarantees or conditions. You may have additional consumer rights // under your local laws which this agreement cannot change. To the extent // permitted under your local laws, Microsoft excludes the implied warranties // of merchantability, fitness for a particular purpose and non-infringement. using System; using System.Diagnostics; using System.Collections; using Win32Exception = System.ComponentModel.Win32Exception; using System.Management.Automation; //Windows PowerShell namespace using System.Globalization; // This sample shows how to declare parameter sets, the input object, and // how to specify the default parameter set to use. // // To test this cmdlet, create a module folder that has the same name as // this assembly (StopProcesssample04), save the assembly in the module folder, and then run the // following command: // import-module stopprocesssample04 namespace Microsoft.Samples.PowerShell.Commands { #region StopProcCommand /// /// This class implements the stop-proc cmdlet. /// [Cmdlet(VerbsLifecycle.Stop, "Proc", DefaultParameterSetName = "ProcessId", SupportsShouldProcess = true)] public class StopProcCommand : PSCmdlet { #region Parameters /// /// This parameter provides the list of process names on /// which the Stop-Proc cmdlet will work. /// [Parameter( Position = 0, ParameterSetName = "ProcessName", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The name of one or more processes to stop. Wildcards are permitted." )] [Alias("ProcessName")] public string[] Name { get { return processNames; } set { processNames = value; } } private string[] processNames; /// /// This parameter overrides the ShouldContinue call to force /// the cmdlet to stop its operation. This parameter should always /// be used with caution. /// [Parameter] public SwitchParameter Force { get { return force; } set { force = value; } } private bool force; /// /// This parameter indicates that the cmdlet should return /// an object to the pipeline after the processing has been /// completed. /// [Parameter( HelpMessage = "If set the process(es) will be passed to the pipeline after stopped." )] public SwitchParameter PassThru { get { return passThru; } set { passThru = value; } } private bool passThru; /// This parameter provides the list of process identifiers on /// which the Stop-Proc cmdlet will work. [Parameter( ParameterSetName = "ProcessId", Mandatory = true, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true )] [Alias("ProcessId")] public int[] Id { get { return processIds; } set { processIds = value; } } private int[] processIds; /// /// This parameter accepts an array of Process objects from the /// the pipeline. This object contains the processes to stop. /// /// Process objects [Parameter( ParameterSetName = "InputObject", Mandatory = true, ValueFromPipeline = true)] public Process[] InputObject { get { return inputObject; } set { inputObject = value; } } private Process[] inputObject; #endregion Parameters #region CmdletOverrides /// /// The ProcessRecord method does the following for each of the /// requested process names: /// 1) Check that the process is not a critical process. /// 2) Attempt to stop that process. /// If no process is requested then nothing occurs. /// protected override void ProcessRecord() { switch (ParameterSetName) { case "ProcessName": ProcessByName(); break; case "ProcessId": ProcessById(); break; case "InputObject": foreach (Process process in inputObject) { SafeStopProcess(process); } break; default: throw new ArgumentException("Bad ParameterSet Name"); } // switch (ParameterSetName... } // ProcessRecord #endregion Cmdlet Overrides #region Helper Methods /// /// Returns all processes with matching names. /// /// /// The name of the processes to return. /// /// An array of all /// computer processes. /// An array of matching processes. internal ArrayList SafeGetProcessesByName(string processName, ref ArrayList allProcesses) { // Create and array to store the matching processes. ArrayList matchingProcesses = new ArrayList(); // Create the wildcard for pattern matching. WildcardOptions options = WildcardOptions.IgnoreCase | WildcardOptions.Compiled; WildcardPattern wildcard = new WildcardPattern(processName, options); // Walk all of the machine processes. foreach(Process process in allProcesses) { string processNameToMatch = null; try { processNameToMatch = process.ProcessName; } catch (Win32Exception e) { // Remove the process from the list so that it is not // checked again. allProcesses.Remove(process); string message = String.Format(CultureInfo.CurrentCulture, "The process \"{0}\" could not be found", processName); WriteVerbose(message); WriteError(new ErrorRecord(e, "ProcessNotFound", ErrorCategory.ObjectNotFound, processName)); continue; } if (!wildcard.IsMatch(processNameToMatch)) { continue; } matchingProcesses.Add(process); } // foreach(Process... return matchingProcesses; } // SafeGetProcessesByName /// /// Safely stops a named process. Used as standalone function /// to declutter the ProcessRecord method. /// /// The process to stop. private void SafeStopProcess(Process process) { string processName = null; try { processName = process.ProcessName; } catch (Win32Exception e) { WriteError(new ErrorRecord(e, "ProcessNotFound", ErrorCategory.OpenError, processName)); return; } // Confirm the operation first. // This is always false if the WhatIf parameter is specified. if (!ShouldProcess(string.Format(CultureInfo.CurrentCulture, "{0} ({1})", processName, process.Id))) { return; } // Make sure that the user really wants to stop a critical // process that can possibly stop the computer. bool criticalProcess = criticalProcessNames.Contains(processName.ToLower(CultureInfo.CurrentCulture)); string message = null; if (criticalProcess && !force) { message = String.Format(CultureInfo.CurrentCulture, "The process \"{0}\" is a critical process and should not be stopped. Are you sure you wish to stop the process?", processName); // It is possible that the ProcessRecord method is called // multiple times when objects are recieved as inputs from // the pipeline. So to retain YesToAll and NoToAll input that // the user may enter across mutilple calls to this function, // they are stored as private members of the cmdlet. if (!ShouldContinue(message, "Warning!", ref yesToAll, ref noToAll)) { return; } } // if (criticalProcess... // Display a warning message if stopping a critical // process. if (criticalProcess) { message = String.Format(CultureInfo.CurrentCulture, "Stopping the critical process \"{0}\".", processName); WriteWarning(message); } // if (criticalProcess... try { // Stop the process. process.Kill(); } catch (Exception e) { if ((e is Win32Exception) || (e is SystemException) || (e is InvalidOperationException)) { // This process could not be stopped so write // a non-terminating error. WriteError(new ErrorRecord(e, "CouldNotStopProcess", ErrorCategory.CloseError, process) ); return; } // if ((e is... else throw; } // catch // Write a user-level verbose message to the pipeline. These are // intended to give the user detailed information on the // operations performed by the cmdlet. These messages will // appear with the -Verbose option. message = String.Format(CultureInfo.CurrentCulture, "Stopped process \"{0}\", pid {1}.", processName, process.Id); WriteVerbose(message); // If the PassThru prameter is specified, return the terminated // process to the pipeline. if (passThru) { // Write a debug message to the host that can be used // when troubleshooting a problem. All debug messages // will appear with the -Debug option message = String.Format(CultureInfo.CurrentCulture, "Writing process \"{0}\" to pipeline", processName); WriteDebug(message); WriteObject(process); } // if (passThru.. } // SafeStopProcess /// /// Stop processes based on their names (using the /// ParameterSetName as ProcessName) /// private void ProcessByName() { ArrayList allProcesses = null; // Get a list of all processes. try { allProcesses = new ArrayList(Process.GetProcesses()); } catch (InvalidOperationException ioe) { base.ThrowTerminatingError(new ErrorRecord( ioe, "UnableToAccessProcessList", ErrorCategory.InvalidOperation, null)); } // If a process name is passed to the cmdlet, get // the associated processes. // Write a nonterminating error for failure to // retrieve a process. foreach (string name in processNames) { // The allProcesses array list is passed as a reference because // any process whose name cannot be obtained will be removed // from the list so that its not compared the next time. ArrayList processes = SafeGetProcessesByName(name, ref allProcesses); // If no processes were found write a non- // terminating error. if (processes.Count == 0) { WriteError(new ErrorRecord( new Exception("Process not found."), "ProcessNotFound", ErrorCategory.ObjectNotFound, name)); } // if (processes... // Otherwise terminate all processes in the list. else { foreach (Process process in processes) { SafeStopProcess(process); } // foreach (Process... } // else } // foreach (string... } // ProcessByName /// /// Stop processes based on their identifiers (using the /// ParameterSetName as ProcessIds) /// internal void ProcessById() { foreach (int processId in processIds) { Process process = null; try { process = Process.GetProcessById(processId); // Write a debug message to the host that can be used // when troubleshooting a problem. All debug messages // will appear with the -Debug option string message = String.Format(CultureInfo.CurrentCulture, "Acquired process for pid : {0}", process.Id); WriteDebug(message); } catch (ArgumentException ae) { string message = String.Format(CultureInfo.CurrentCulture, "The process id {0} could not be found", processId); WriteVerbose(message); WriteError(new ErrorRecord(ae, "ProcessIdNotFound", ErrorCategory.ObjectNotFound, processId)); continue; } SafeStopProcess(process); } // foreach (int... } // ProcessById #endregion Helper Methods #region Private Data private bool yesToAll, noToAll; /// /// Partial list of critical processes that should not be /// stopped. Lower case is used for case insensitive matching. /// private ArrayList criticalProcessNames = new ArrayList( new string[] { "system", "winlogon", "spoolsv", "calc" } ); #endregion Private Data } // StopProcCommand #endregion StopProcCommand }