创建用于修改系统的 Cmdlet

有时,cmdlet 必须修改系统的运行状态,而不只是Windows PowerShell的状态。 在这些情况下,cmdlet 应允许用户确认是否进行更改。

若要支持确认,cmdlet 必须执行两项操作。

通过支持确认,cmdlet 公开 Windows PowerShell 提供的 和 参数,并且还符合 cmdlet 的开发准则 (有关 cmdlet 开发准则的信息,请参阅 Confirm WhatIf Cmdlet开发指南 .) 。

更改系统

"更改系统"的行为是指可能更改系统状态的任何 cmdlet Windows PowerShell。 例如,停止进程、启用或禁用用户帐户或向数据库表添加行都是对系统进行的所有更改,应确认这些更改。 相反,读取数据或建立暂时性连接的操作不会更改系统,通常不需要确认。 对于效果限制为在运行时内部的操作(例如 Windows PowerShell,也不需要确认 set-variable 。 只有在即将进行永久性更改时,可能会或可能不会进行永久性更改的 Cmdlet 才应声明并调用 SupportsShouldProcess System.Management.Automation.Cmdlet.ShouldProcess。

备注

ShouldProcess 确认仅适用于 cmdlet。 如果命令或脚本通过直接调用 .NET 方法或属性,或者通过调用 Windows PowerShell 外部的应用程序来修改系统的运行状态,则这种确认形式将不可用。

The StopProc Cmdlet

本主题介绍一Stop-Proc cmdlet,该 cmdlet 尝试停止使用 Get-Proc cmdlet (创建第一个 cmdlet) 。

定义 Cmdlet

创建 cmdlet 的第一步是始终命名 cmdlet 并声明实现 cmdlet 的 .NET 类。 由于你正在编写 cmdlet 来更改系统,因此应相应地命名它。 此 cmdlet 会停止系统进程,因此此处选择的谓词名称为"Stop",由 System.Management.Automation.Verbslifecycle 类定义,名词"Proc"指示 cmdlet 停止进程。 有关已批准的 cmdlet 谓词详细信息,请参阅 Cmdlet 谓词名称

下面是此 cmdlet 的类Stop-Proc定义。

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

请注意,在 System.Management.Automation.CmdletAttribute 声明中,attribute 关键字设置为 ,以使 cmdlet 能够调用 SupportsShouldProcess true System.Management.Automation.Cmdlet.ShouldProcessSystem.Management.Automation.Cmdlet.ShouldContinue。 如果没有设置此关键字,则 和 Confirm WhatIf 参数将不可用。

极破坏性操作

某些操作具有极强破坏性,例如重新格式化活动的硬盘分区。 在这些情况下,应在声明 ConfirmImpact = ConfirmImpact.High System.Management.Automation.CmdletAttribute属性时设置 cmdlet。 此设置强制 cmdlet 请求用户确认,即使用户未指定 Confirm 参数。 但是,cmdlet 开发人员应避免过度使用可能破坏性的操作,例如 ConfirmImpact 删除用户帐户。 请记住,如果 ConfirmImpact 设置为System.Management.Automation.ConfirmImpact High

同样,某些操作不太可能具有破坏性,尽管它们在理论上会修改系统外部系统的运行Windows PowerShell。 此类 cmdlet 可以设置为 ConfirmImpact System.Management.Automation.Confirmimpact.Low。 这将绕过用户要求仅确认中等影响和影响大的操作的确认请求。

定义用于系统修改的参数

本部分介绍如何定义 cmdlet 参数,包括支持系统修改所需的参数。 如果需要 有关定义参数的一般信息 ,请参阅添加处理命令行输入的参数。

该Stop-Proc cmdlet 定义三个参数 Name :、 ForcePassThru

Name参数对应于进程 Name 输入对象的 属性。 请注意,此示例中的 参数是必需的,因为如果没有要停止的命名进程 Name ,cmdlet 将失败。

参数 Force 允许用户重写对 System.Management.Automation.Cmdlet.ShouldContinue 的调用。 事实上,调用 System.Management.Automation.Cmdlet.ShouldContinue 的任何 cmdlet 都应具有 参数,以便指定 时,cmdlet 将跳过对 Force Force System.Management.Automation.Cmdlet.ShouldContinue 的调用,并继续操作。 请注意,这不会影响对 System.Management.Automation.Cmdlet.ShouldProcess 的调用

参数允许用户指示 cmdlet 是否在进程停止后通过管道传递输出对象(本例 PassThru 中为 )。 请注意,此参数绑定到 cmdlet 本身,而不是与输入对象的属性绑定。

下面是 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;

重写输入处理方法

cmdlet 必须重写输入处理方法。 下面的代码演示了示例 cmdlet 中使用的 System.Management.Automation.Cmdlet.ProcessRecord Stop-Proc重写。 对于每个请求的进程名称,此方法可确保该进程不是特殊进程,尝试停止该进程,然后发送输出对象(如果指定了 PassThru 参数)。

protected override void ProcessRecord()
{
  foreach (string name in processNames)
  {
    // For every process name passed to the cmdlet, get the associated
    // process(es). For failures, write a non-terminating error
    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;
        }

        // 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)
        {
          string 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 the Name parameter receives objects as input from the
          // pipeline. So to retain YesToAll and NoToAll input that the
          // user may enter across multiple calls to ProcessRecord, this
          // information is stored as private members of the cmdlet.
          if (!ShouldContinue(message, "Warning!",
                              ref yesToAll,
                              ref noToAll))
          {
            continue;
          }
        } // 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.
            string 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

        // If the PassThru parameter argument is
        // True, pass the terminated process on.
        if (passThru)
        {
          WriteObject(process);
        }
    } // foreach (Process...
  } // foreach (string...
} // ProcessRecord

调用 ShouldProcess 方法

cmdlet 的输入处理方法应调用 System.Management.Automation.Cmdlet.ShouldProcess 方法,以确认在更改之前执行某个操作 (例如,删除文件) 以更改系统的运行状态。 这样,Windows PowerShell运行时在 shell 中提供正确的"WhatIf"和"Confirm"行为。

备注

如果 cmdlet 指出它支持应处理,但无法进行 System.Management.Automation.Cmdlet.ShouldProcess 调用,则用户可能会意外修改系统。

调用System.Management.Automation.Cmdlet.ShouldProcess会向用户发送要更改的资源的名称,Windows PowerShell 运行时在确定应该向用户显示的内容时,会考虑任何命令行设置或首选项变量。

以下示例演示了从示例 Stop-Proc cmdlet 中System.Management.Automation.Cmdlet.ProcessRecord方法的重写对System.Management.Automation.Cmdlet.ShouldProcess的调用。

if (!ShouldProcess(string.Format("{0} ({1})", processName,
                   process.Id)))
{
  continue;
}

调用 ShouldContinue 方法

调用 System.Management.Automation.Cmdlet.ShouldContinue 方法将向用户发送辅助消息。 调用 System.Management.Automation.Cmdlet.ShouldProcess 后,如果 参数未设置为 ,则 true Force 进行此调用 true 。 然后,用户可以提供反馈,指示操作是否应该继续。 cmdlet 调用 System.Management.Automation.Cmdlet.ShouldContinue 作为可能危险系统修改的附加检查,或者当你想要向用户提供"是-全部"和"无到有"选项时。

以下示例演示了从示例 Stop-Proc cmdlet 中System.Management.Automation.Cmdlet.ProcessRecord方法的重写对System.Management.Automation.Cmdlet.ShouldContinue的调用。

if (criticalProcess &&!force)
{
  string 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 the Name parameter receives objects as input from the
  // pipeline. So to retain YesToAll and NoToAll input that the
  // user may enter across multiple calls to ProcessRecord, this
  // information is stored as private members of the cmdlet.
  if (!ShouldContinue(message, "Warning!",
                      ref yesToAll,
                      ref noToAll))
  {
    continue;
  }
} // if (criticalProcess...

停止输入处理

进行系统修改的 cmdlet 的输入处理方法必须提供一种停止输入处理的方法。 对于此 Stop-Proc cmdlet,从 System.Management.Automation.Cmdlet.ProcessRecord 方法调用 System.Diagnostics.Process.Kill* 方法。 由于 参数 PassThru 设置为 true ,因此 System.Management.Automation.Cmdlet.ProcessRecord 还调用 System.Management.Automation.Cmdlet.WriteObject 将进程对象发送到管道。

代码示例

有关完整的 C# 示例代码,请参阅 StopProcessSample01 示例

定义对象类型和格式设置

Windows PowerShell .Net 对象在 cmdlet 之间传递信息。 因此,cmdlet 可能需要定义自己的类型,或者 cmdlet 可能需要扩展另一个 cmdlet 提供的现有类型。 有关定义新类型或扩展现有类型的信息,请参阅 扩展对象类型和格式设置。

生成 Cmdlet

实现 cmdlet 后,必须通过Windows PowerShell管理单元Windows PowerShell注册该 cmdlet。 有关注册 cmdlet 的信息,请参阅 如何注册 Cmdlet、提供程序和主机应用程序

测试 Cmdlet

将 cmdlet 注册到 Windows PowerShell后,可以通过在命令行上运行它来测试它。 下面是几个测试 Stop-Proc cmdlet 的测试。 有关从命令行使用 cmdlet 的信息,请参阅入门cmdlet Windows PowerShell。

  • 启动Windows PowerShell并使用 Stop-Proc cmdlet 停止处理,如下所示。 由于 cmdlet 将 参数指定为 Name 必需参数,因此 cmdlet 会查询 参数。

    PS> stop-proc
    

    将显示以下输出。

    Cmdlet stop-proc at command pipeline position 1
    Supply values for the following parameters:
    Name[0]:
    
  • 现在,让我们使用 cmdlet 停止名为"NOTEPAD"的进程。 cmdlet 要求你确认操作。

    PS> stop-proc -Name notepad
    

    将显示以下输出。

    Confirm
    Are you sure you want to perform this action?
    Performing operation "stop-proc" on Target "notepad (4996)".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): Y
    
  • 使用Stop-Proc,停止名为"WINLOGON"的关键进程。 系统会提示你执行此操作并发出警告,因为它将导致操作系统重新启动。

    PS> stop-proc -Name Winlogon
    

    将显示以下输出。

    Confirm
    Are you sure you want to perform this action?
    Performing operation "stop-proc" on Target "winlogon (656)".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): Y
    Warning!
    The process " winlogon " is a critical process and should not be stopped. Are you sure you wish to stop the process?
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): N
    
  • 现在尝试停止 WINLOGON 进程,而不收到警告。 请注意,此命令条目使用 Force 参数替代警告。

    PS> stop-proc -Name winlogon -Force
    

    将显示以下输出。

    Confirm
    Are you sure you want to perform this action?
    Performing operation "stop-proc" on Target "winlogon (656)".
    [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): N
    

另请参阅

添加处理输入Command-Line参数

扩展对象类型和格式设置

如何注册 Cmdlet、提供程序和主机应用程序

Windows PowerShell SDK

Cmdlet 示例