有時候 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 attribute 關鍵詞會設定為 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 運行時間在殼層內提供正確的 “WhatIf” 和 “Confirm” 行為。
備註
如果 Cmdlet 指出其支援應處理且無法讓 System.Management.Automation.Cmdlet.ShouldProcess 呼叫,使用者可能會意外修改系統。
呼叫 System.Management.Automation.Cmdlet.ShouldProcess 會將要變更的資源名稱傳送給使用者,而 Windows PowerShell 運行時間會考慮任何命令行設定或喜好設定變數,以判斷應該向使用者顯示的內容。
下列範例顯示從範例 Stop-Proc Cmdlet 中 System.Management.Automation.Cmdlet.ProcessRecor d 方法的覆寫 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 做為潛在危險系統修改的額外檢查,或當您想要為使用者提供 yes-to-all 和 no-to-all 選項時。
下列範例顯示從範例 Stop-Proc Cmdlet 中 System.Management.Automation.Cmdlet.ProcessRecor d 方法的覆寫 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