// // 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 aliases and support wildcards. // // To test this cmdlet, create a module folder that has the same name as // this assembly (StopProcesssample03), save the assembly in the module folder, // and then run the following command: import-module stopprocesssample03 namespace Microsoft.Samples.PowerShell.Commands { #region StopProcCommand /// /// This class implements the stop-proc cmdlet. /// [Cmdlet(VerbsLifecycle.Stop, "Proc", SupportsShouldProcess = true)] public class StopProcCommand : Cmdlet { #region Parameters /// /// This parameter provides the list of process names on /// which the Stop-Proc cmdlet will work. /// [Parameter( Position = 0, 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( ValueFromPipelineByPropertyName = true, 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; #endregion Parameters #region Cmdlet Overrides /// /// 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() { Process[] processes = null; try { processes = Process.GetProcesses(); } catch (InvalidOperationException ioe) { base.ThrowTerminatingError(new ErrorRecord(ioe, "UnableToAcessProcessList", ErrorCategory.InvalidOperation, null)); } // For every process name passed to the cmdlet, get the associated // processes. // Write a nonterminating error for failure to retrieve // a process. foreach (string name in processNames) { // Write a user-friendly verbose message to the pipeline. These // messages are intended to give the user detailed information // on the operations performed by the cmdlet. These messages will // appear with the -Verbose option. string message = String.Format(CultureInfo.CurrentCulture, "Attempting to stop process \"{0}\".", name); WriteVerbose(message); // Validate the process name against a wildcard pattern. // If the name does not contain any wildcard patterns, it // will be treated as an exact match. WildcardOptions options = WildcardOptions.IgnoreCase | WildcardOptions.Compiled; WildcardPattern wildcard = new WildcardPattern(name,options); foreach (Process process in processes) { string processName; try { processName = process.ProcessName; } catch (Win32Exception e) { WriteError(new ErrorRecord( e, "ProcessNameNotFound", ErrorCategory.ObjectNotFound, process) ); continue; } // 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, "Acquired name for pid {0} : \"{1}\"", process.Id, processName); WriteDebug(message); // Check to see if this process matches the current process // name pattern. Skip this process if it does not. if (!wildcard.IsMatch(processName)) { continue; } // Stop the process. SafeStopProcess(process); } // foreach (Process... } // foreach (string... } // ProcessRecord #endregion Cmdlet Overrides #region Helper Methods /// /// 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, "ProcessNameNotFound", ErrorCategory.ObjectNotFound, process)); return; } string message = null; // Confirm the operation first. // This is always false if the WhatIf parametr 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 could possibly stop the computer. bool criticalProcess = criticalProcessNames.Contains(processName.ToLower(CultureInfo.CurrentCulture)); 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 ProcessRecord is called multiple // when objects are recieved as inputs from a 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 message = String.Format(CultureInfo.CurrentCulture, "Stopped process \"{0}\", pid {1}.", processName, process.Id); WriteVerbose(message); // If the PassThru parameter is specified, // return the terminated process to the pipeline. if (passThru) { message = String.Format(CultureInfo.CurrentCulture, "Writing process \"{0}\" to pipeline", processName); WriteDebug(message); WriteObject(process); } // if (passThru... } // SafeStopProcess #endregion Helper Methods #region Private Data private bool yesToAll, noToAll; /// /// Partial list of the 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" } ); #endregion Private Data } // StopProcCommand #endregion StopProcCommand } // namespace Microsoft.Samples.PowerShell.Commands