有时,cmdlet 必须修改系统的运行状态,而不仅仅是 Windows PowerShell 运行时的状态。 在这些情况下,cmdlet 应允许用户确认是否进行更改。
若要支持确认,cmdlet 必须执行两项作。
通过将 SupportsShouldProcess 关键字设置为
true
,声明 cmdlet 在指定 System.Management.Automation.CmdletAttribute 属性时支持确认。在执行 cmdlet 期间调用 System.Management.Automation.Cmdlet.ShouldProcess(如以下示例所示)。
通过支持确认,cmdlet 公开 Windows PowerShell 提供的 Confirm
和 WhatIf
参数,并满足 cmdlet 的开发准则(有关 cmdlet 开发指南的详细信息,请参阅 Cmdlet 开发指南.)。
更改系统
“更改系统”的行为是指任何可能更改 Windows PowerShell 外部系统状态的 cmdlet。 例如,停止进程、启用或禁用用户帐户或向数据库表添加行都是对应确认的系统所做的所有更改。
相比之下,读取数据或建立暂时性连接的作不会更改系统,通常不需要确认。 对于其效果仅限于 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 谓词名称。
下面是此 Stop-Proc cmdlet 的类定义。
[Cmdlet(VerbsLifecycle.Stop, "Proc",
SupportsShouldProcess = true)]
public class StopProcCommand : Cmdlet
请注意,在 System.Management.Automation.CmdletAttribute 声明中,SupportsShouldProcess
属性关键字设置为 true
,使 cmdlet 能够调用 System.Management.Automation.Cmdlet.ShouldProcess,System.Management.Automation.Cmdlet.ShouldContinue。
如果未设置此关键字,则 Confirm
和 WhatIf
参数将不适用于用户。
极其破坏性的作
某些作具有极其破坏性,例如重新格式化活动硬盘分区。 在这些情况下,在声明 System.Management.Automation.CmdletAttribute 属性时,cmdlet 应设置 ConfirmImpact
= ConfirmImpact.High
。 即使用户未指定 Confirm
参数,此设置也会强制 cmdlet 请求用户确认。 但是,cmdlet 开发人员应避免过度使用对可能具有破坏性的作 ConfirmImpact
,例如删除用户帐户。 请记住,如果 ConfirmImpact
设置为 System.Management.Automation.ConfirmImpactHigh。
同样,某些作不太可能具有破坏性,尽管它们理论上修改了 Windows PowerShell 外部系统的运行状态。 此类 cmdlet 可以将 ConfirmImpact
设置为 System.Management.Automation.ConfirmImpact.Low。
这将绕过用户要求仅确认中等影响和高影响作的确认请求。
定义用于系统修改的参数
本部分介绍如何定义 cmdlet 参数,包括支持系统修改所需的参数。 如果需要有关定义参数的常规信息,请参阅 添加处理 CommandLine 输入的参数。
Stop-Proc cmdlet 定义了三个参数:Name
、Force
和 PassThru
。
Name
参数对应于进程输入对象的 Name
属性。 请注意,此示例中的 Name
参数是必需的,因为如果 cmdlet 没有要停止的命名进程,该 cmdlet 将失败。
Force
参数允许用户替代 对 system.Management.Automation.Cmdlet.ShouldContinue的调用。
事实上,调用 System.Management.Automation.Cmdlet.ShouldContinue 的任何 cmdlet 都应具有 Force
参数,以便在指定 Force
时,cmdlet 将跳过调用 System.Management.Automation.Cmdlet.ShouldContinue 并继续执行作。 请注意,这不会影响对 System.Management.Automation.Cmdlet.ShouldProcess的调用。
PassThru
参数允许用户指示 cmdlet 在进程停止后是否通过管道传递输出对象。 请注意,此参数绑定到 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 必须重写输入处理方法。 以下代码演示了示例 Stop-Proc cmdlet 中使用的 System.Management.Automation.Cmdlet.ProcessRecord 替代。 对于每个请求的进程名称,此方法可确保进程不是特殊进程,尝试停止进程,然后在指定 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 作为对潜在危险系统修改的附加检查,或者想要向用户提供“是到全”和 no-to-all 选项。
以下示例演示了从示例 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 注册到 Windows PowerShell 后,可以通过在命令行上运行它来测试它。 下面是测试 Stop-Proc 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