Adding User Messages to Your Cmdlet

Cmdlets can write several kinds of messages that can be displayed to the user by the Windows PowerShell runtime. These messages include the following types:

  • Verbose messages that contain general user information.

  • Debug messages that contain troubleshooting information.

  • Warning messages that contain a notification that the cmdlet is about to perform an operation that can have unexpected results.

  • Progress report messages that contain information about how much work the cmdlet has completed when performing an operation that takes a long time.

There are no limits to the number of messages that your cmdlet can write or the type of messages that your cmdlet writes. Each message is written by making a specific call from within the input processing method of your cmdlet.

The StopProc Cmdlet

This section describes how to add writing verbose, warning, and debug messages to the Stop-Proc cmdlet (described in Creating a Cmdlet that Modifies the System), and provides an additional example that shows how to write a progress message. (The Stop-Proc cmdlet does not write progress messages, because stopping a process does not take an extended amount of time.)

noteNote:
You can download the C# source file (stopproc02.cs) for this Stop-Proc cmdlet using the Microsoft Windows Software Development Kit for Windows Vista and .NET Framework 3.0 Runtime Components. For download instructions, see How to Install Windows PowerShell.

The downloaded source files are available in the <PowerShell Samples> directory.

Topics in this section include the following:

Defining the Cmdlet

The first step in cmdlet creation is always naming the cmdlet and declaring the .NET class that implements the cmdlet. Any sort of cmdlet can write user notifications from its input processing methods; so, in general, you can name this cmdlet using any verb that indicates what system modifications the cmdlet performs. For more information about approved cmdlet verbs, see Approved Verbs for Windows PowerShell Commands.

The Stop-Proc cmdlet is designed to modify the system; therefore, the CmdletAttribute declaration for the .NET class must include the SupportsShouldProcess attribute keyword and be set to true.

The following code is the definition for this Stop-Proc cmdlet class. For more information about this definition, see Creating a Cmdlet that Modifies the System.

[Cmdlet(VerbsLifecycle.Stop, "proc",
        SupportsShouldProcess = true)]
public class StopProcCommand : Cmdlet

Defining Parameters for System Modification

The Stop-Proc cmdlet defines three parameters: Name, Force, and PassThru. For more information about defining these parameters, see Creating a Cmdlet that Modifies the System.

Here is the parameter declaration for the Stop-Proc cmdlet.

[Parameter(
           Position = 0,
           Mandatory = true,
           ValueFromPipeline = true,
           ValueFromPipelineByPropertyName = true
)]
public string[] Name
{
  get { return processNames; }
  set { processNames = value; }
}
private string[] processNames;

/// <summary>
/// Specify the Force parameter that allows the user to override 
/// the ShouldContinue call to force the stop operation. This 
/// parameter should always be used with caution.
/// </summary>
[Parameter]
public SwitchParameter Force
{
  get { return force; }
  set { force = value; }
}
private bool force;

/// <summary>
/// Specify the PassThru parameter that allows the user to specify 
/// that the cmdlet should pass the process object down the pipeline 
/// after the process has been stopped.
/// </summary>
[Parameter]
public SwitchParameter PassThru
{
  get { return passThru; }
  set { passThru = value; }
}
private bool passThru;

Overriding an Input Processing Method

Your cmdlet must override an input processing method, most often it will be ProcessRecord. This Stop-Proc cmdlet overrides the ProcessRecord input processing method. In this implementation of the Stop-Proc cmdlet, calls are made to write verbose messages, debug messages, and warning messages.

noteNote:
For more information about how this method calls the ShouldProcess and ShouldContinue methods, see Creating a Cmdlet that Modifies the System.

Writing a Verbose Message

The WriteVerbose method is used to write general user-level information that is unrelated to specific error conditions. The system administrator can then use that information to continue processing other commands. In addition, any information written using this method should be localized as needed.

The following code from this Stop-Proc cmdlet shows two calls to the WriteVerbose method from the override of the ProcessRecord method.

message = String.Format("Attempting to stop process \"{0}\".", name);             
WriteVerbose(message);

message = String.Format("Stopped process \"{0}\", pid {1}.",
                        processName, process.Id);

WriteVerbose(message);

Writing a Debug Message

The WriteDebug method is used to write debug messages that can be used to troubleshoot the operation of the cmdlet. The call is made from an input processing method.

noteNote:
Windows PowerShell also defines a Debug parameter that presents both verbose and debug information. If your cmdlet supports this parameter, it does not need to call WriteDebug in the same code that calls WriteVerbose.

The following two sections of code from the sample Stop-Proc cmdlet show calls to the WriteDebug method from the override of the ProcessRecord method.

This debug message is written immediately before ShouldProcess is called.

message = 
          String.Format("Acquired name for pid {0} : \"{1}\"", 
                       process.Id, processName);
WriteDebug(message);

This debug message is written immediately before WriteObject is called.

message =
         String.Format("Writing process \"{0}\" to pipeline",
         processName);
WriteDebug(message);
WriteObject(process);

Windows PowerShell automatically routes any WriteDebug calls to the tracing infrastructure and cmdlets. This allows the method calls to be traced to the hosting application, a file, or a debugger without your having to do any extra development work within the cmdlet. The following command-line entry implements a tracing operation.

PS> trace-expression stop-proc –file proc.log –command stop-proc notepad

Writing a Warning Message

The WriteWarning method is used to write a warning when the cmdlet is about to perform an operation that might have an unexpected result, for example, overwriting a read-only file.

The following code from the sample Stop-Proc cmdlet shows the call to the WriteWarning method from the override of the ProcessRecord method.

 if (criticalProcess)
 {
   message =
             String.Format("Stopping the critical process \"{0}\".",
                           processName);
   WriteWarning(message);
} // if (criticalProcess...

Writing a Progress Message

The WriteProgress is used to write progress messages when cmdlet operations take an extended amount of time to complete. A call to WriteProgress passes a ProgressRecord object that is sent to the hosting application for rendering to the user.

noteNote:
This Stop-Proc cmdlet does not include a call to the WriteProgress method.

The following code is an example of a progress message written by a cmdlet that is attempting to copy an item.

int myId = 0;
string myActivity = "Copy-item: Copying *.* to c:\abc";
string myStatus = "Copying file bar.txt";
ProgressRecord pr = new ProgressRecord(myId, myActivity, myStatus);
WriteProgress(pr);

pr.RecordType = ProgressRecordType.Completed;
WriteProgress(pr);

Code Sample

The following code shows the implementation of this Stop-Proc cmdlet.

// Copyright (c) 2005 Microsoft Corporation. All rights reserved.
// 
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//

using System;
using System.Diagnostics;
using System.Collections;
using Win32Exception = System.ComponentModel.Win32Exception;
using System.Management.Automation;             // Windows PowerShell namespace.
using System.ComponentModel;

// This sample introduces WriteDebug, WriteVerbose and WriteWarning.
namespace Microsoft.Samples.PowerShell.Commands
{
 
  #region StopProcCommand

  /// <summary>
  // This class implements the stop-proc cmdlet.
  // </summary>
  [Cmdlet(VerbsLifecycle.Stop, "proc",
          SupportsShouldProcess = true)]
  public class StopProcCommand : Cmdlet
  {
    #region Parameters
    
    /// <summary>
    /// Specify the Name parameter the provides the list of 
    /// processes that the cmdlet works on.
    /// </summary>
    [Parameter(
               Position = 0,
               Mandatory = true,
               ValueFromPipeline = true,
               ValueFromPipelineByPropertyName = true
    )]
    public string[] Name
    {
      get { return processNames; }
      set { processNames = value; }
    }
    private string[] processNames;

    /// <summary>
    /// Specify the Force parameter that allows the user to override 
    /// the ShouldContinue call to force the stop operation. This 
    /// parameter should always be used with caution.
    /// </summary>
    [Parameter]
    public SwitchParameter Force
    {
      get { return force; }
      set { force = value; }
    }
    private bool force;
    
    /// <summary>
    /// Specify the PassThru parameter that allows the user to specify 
    /// that the cmdlet should pass the process object down the pipeline 
    /// after the process has been stopped.
    /// </summary>
    [Parameter]
    public SwitchParameter PassThru
    {
      get { return passThru; }
      set { passThru = value; }
    }
    private bool passThru;
    #endregion Parameters

    #region Cmdlet Overrides

    /// <summary>
    /// Override of the ProcessRecord input processing method. For each 
    /// of the requested process names, the following actions are taken:
    ///   1) checks that the process is not a critical process
    ///   2) attempts to stop that process.
    /// If no process is requested, then nothing occurs.
    /// </summary>     
    protected override void ProcessRecord()
    {           
      foreach (string name in processNames)
      {
        string message = null;

        // For every process name passed to cmdlet, get the associated 
        // process(es). Write a non-terminating error for failure to 
        //retrieve a process.
        // Write a user-level 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("Attempting to stop process \"{0}\".", name);             
        WriteVerbose(message);
        Process[] processes;

        try 
        { 
          processes = Process.GetProcessesByName(name); 
        }
        catch (InvalidOperationException ioe)
        {
          WriteError(new ErrorRecord(ioe, 
                     "Unable to access the target process by name",
                     ErrorCategory.InvalidOperation,
                     name));
         continue;
       }

       // Try to stop the process(es) that have been retrieved for a name
       foreach (Process process in processes)
       {
         string processName;

         try 
         { 
           processName = process.ProcessName; 
         }
         catch (Win32Exception e)
         {
           WriteError(new ErrorRecord(e, "ProcessNameNotFound", 
                      ErrorCategory.ReadError, process));
         continue;
         }

         // Write a debug message to the host which will be helpful
         // in troubleshooting a problem. All debug messages
         // will appear with the -Debug option
         message = 
                   String.Format("Acquired name for pid {0} : \"{1}\"", 
                                process.Id, processName);
         WriteDebug(message);

         // Call Should Process to confirm the operation first.
         // This is always false if WhatIf is set.
           if (!ShouldProcess(string.Format("{0} ({1})",
                            processName, process.Id)))
         {
           continue;
         }

         // Call ShouldContinue to make sure the user really does want
         // to stop a critical process that could possibly stop the computer.
         bool criticalProcess = criticalProcessNames.Contains(processName.ToLower());
         if (criticalProcess && !force)
         {
           message = String.Format
                     ("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 
           // times when objects are received 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))
           {
             continue;
           }
         } // if (criticalProcess...
         
         // Display a warning message if stopping a critical 
         // process
         if (criticalProcess)
         {
           message =
                     String.Format("Stopping the critical process \"{0}\".",
                                   processName);
           WriteWarning(message);
        } // if (criticalProcess...
        
        // Stop the named process.
        try
        {
          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.
            message = String.Format("{0} {1} {2}",
                      "Could not stop process \"", 
                      processName,"\".");
            WriteError(new ErrorRecord(
                                       e, 
                                       message,
                                       ErrorCategory.CloseError, 
                                       process)
                      );
            continue;
          } // if ((e is...
          else throw;
        } // catch 

        message = String.Format("Stopped process \"{0}\", pid {1}.",
                                processName, process.Id);

        WriteVerbose(message);

        // If the -PassThru command line argument is
        // specified, pass the terminated process on.
        if (passThru)
        {
          message =
                   String.Format("Writing process \"{0}\" to pipeline",
                   processName);
          WriteDebug(message);
          WriteObject(process);
        } // End if(passThru...).
      } // End foreach(Process...).
    } // End foreach(string...).
  } // End ProcessRecord.
  #endregion Cmdlet Overrides

  #region Private Data
       
   private bool yesToAll, noToAll;
      
   /// <summary>
   /// Partial list of critical processes that should not be 
   /// stopped.  Lower case is used for case insensitive matching.
   /// </summary>
   private ArrayList criticalProcessNames = new ArrayList(
          new string[] { "system", "winlogon", "spoolsv" }
       );

  #endregion Private Data

  } // StopProcCommand

  #endregion StopProcCommand

  #region SnapIn

  /// <summary>
  /// Create the PowerShell snap-in used to register the 
  /// Stop-Proc cmdlet. Declaring the PSSnapIn class identifies
  /// this .cs file as a PowerShell snap-in.
  /// </summary>
  [RunInstaller(true)]
  public class StopProcPSSnapIn02 : PSSnapIn
  {
    /// <summary>
    /// Create an instance of the StopProcPSSnapIn02 class.
    /// </summary>
    public StopProcPSSnapIn02()
          : base()
    {
    }

    /// <summary>
    /// Specify the name of the PowerShell snap-in.
    /// </summary>
    public override string Name
    {
      get
      {
        return "StopProcPSSnapIn02";
      }
    }

    /// <summary>
    /// Specify the vendor of the PowerShell snap-in.
    /// </summary>
    public override string Vendor
    {
      get
      {
        return "Microsoft";
      }
    }

    /// <summary>
    /// Specify the localization resource information for the vendor. 
    /// Use the format: SnapinName,VendorName. 
    /// </summary>
    public override string VendorResource
    {
      get
      {
        return "StopProcPSSnapIn02,Microsoft";
      }
    }

    /// <summary>
    /// Specify a description of the PowerShell snap-in.
    /// </summary>
    public override string Description
    {
      get
      {
        return "This is a PowerShell snap-in that registers the stop-proc cmdlet.";
      }
    }

    /// <summary>
    /// Specify the localization resource information for the description. 
    /// Use the format: SnapInName,Description.  
    /// </summary>
    public override string DescriptionResource
    {
      get
      {
        return "StopProcPSSnapIn02,This is a PowerShell snap-in that registers the stop-proc cmdlet.";
      }
    }
  }
  #endregion SnapIn

}

Define Object Types and Formatting

Windows PowerShell passes information between cmdlets using .NET objects. Consequently, a cmdlet might need to define its own type, or the cmdlet might need to extend an existing type provided by another cmdlet. For more information about defining new types or extending existing types, see Extending Object Types and Formatting.

Building the Cmdlet

After implementing a cmdlet, it must be registered with Windows PowerShell through a Windows PowerShell snap-in. For more information about registering cmdlets, see Registering a Program Module with Windows PowerShell.

Testing the Cmdlet

When your cmdlet has been registered with Windows PowerShell, you can test it by running it on the command line. Let's test the sample Stop-Proc cmdlet. For more information about using cmdlets from the command line, see the Getting Started with Windows PowerShell.

  • The following command-line entry uses Stop-Proc to stop the process named "NOTEPAD", provide verbose notifications, and print debug information.

    PS> stop-proc -Name notepad -Verbose -Debug
    
    The following output appears.

    VERBOSE: Attempting to stop process " notepad ".
    DEBUG: Acquired name for pid 5584 : "notepad"
    
    Confirm
    Continue with this operation?
    [Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"): Y
    
    Confirm
    Are you sure you want to perform this action?
    Performing operation "stop-proc" on Target "notepad (5584)".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): Y
    VERBOSE: Stopped process "notepad", pid 5584.
    

See Also



Community Additions

ADD
Show:
© 2014 Microsoft