在 C#(CSharp)中執行 PowerShell 並以物件形式接收結果
· 小村 豪 · C#, CSharp, PowerShell, Windows, .NET, 自動化, 既有資產活用
在業務應用程式或內部工具中,經常會遇到想從 C# 執行 PowerShell 的場景,例如以下這些處理:
- 取得 Windows 的服務清單
- 查詢處理程序或事件記錄
- 從 C# 應用程式呼叫既有的 PowerShell 腳本
- 從供管理員使用的小型 GUI 工具執行 PowerShell 命令
- 把 PowerShell 端既有的自動化資產,逐步納入 .NET 應用程式
如果只是單純執行,把 powershell.exe 或 pwsh.exe 當作外部程序啟動、把標準輸出當成字串讀取的方法也能動。但這種做法會失去 PowerShell 的優點——「物件管線(object pipeline)」。
PowerShell 的結果原本就不只是純文字。Get-Process 的結果是處理程序物件,Get-Service 的結果是服務物件。如果在 C# 端也能保持這種結構來接收,就不需要解析字串,處理也會變得相當安全。
本文將整理從 C# 執行 PowerShell,並以 PSObject 形式接收結果的基本做法。
另外,本文出現的程式碼已整理成一套可建置、可執行的範例(執行包裝器與轉換處理的函式庫、演示文章各章節的主控台 demo,以及驗證 PSObject 接收與錯誤處理的單元測試),公開在 GitHub 上。
csharp-run-powershell-receive-objects - komurasoft-blog-samples (GitHub)
1. 不用啟動外部程序,改用 PowerShell SDK
從 C# 呼叫 PowerShell 的方法,大致可以分成兩種。
| 方法 | 特徵 | 適合的場景 |
|---|---|---|
用 ProcessStartInfo 啟動 powershell.exe / pwsh.exe |
把標準輸出、標準錯誤當成字串讀取 | 單純執行既有批次作業、只需要留下記錄的處理 |
使用 System.Management.Automation.PowerShell |
可以以 PSObject 的形式接收結果 |
需要在 C# 端加工結果的處理、管理工具、業務應用程式 |
本文要探討的是後者。使用 System.Management.Automation.PowerShell,就能從 C# 程式碼組裝並執行 PowerShell 的管線。重點在於,回傳值不是字串,基本上會是 Collection<PSObject>。
換句話說,思路會變成這樣:
執行 PowerShell 命令
↓
以 PSObject 集合的形式接收結果
↓
從 BaseObject 或 Properties 中取出值
↓
必要時轉換成 C# 的 DTO / record / class
重點不是把 PowerShell 的輸出拆解成字串,而是從一開始就以物件的形式來處理。
2. 前提環境
本文以 .NET 8 的主控台應用程式為例。PowerShell SDK 依版本不同,對應的 .NET 也不同,因此要配合專案的目標框架來選擇。
以 2026 年 6 月的時間點來看,可以這樣理解:
| C# 應用程式的目標框架 | 範例使用的 PowerShell SDK | 備註 |
|---|---|---|
| .NET 8 | Microsoft.PowerShell.SDK 7.4 系列 |
適合 .NET 8 應用程式使用 |
| .NET 10 | Microsoft.PowerShell.SDK 7.6 系列 |
想使用較新版 PowerShell SDK 時的候選 |
| .NET Framework | Microsoft.PowerShell.5.1.ReferenceAssemblies |
適用於 Windows PowerShell 5.1。新專案開發時請先確認需求 |
這裡以 .NET 8 為例,使用 Microsoft.PowerShell.SDK 7.4.16。
dotnet new console -n PowerShellObjectSample
cd PowerShellObjectSample
dotnet add package Microsoft.PowerShell.SDK --version 7.4.16
.csproj 大致會像下面這樣。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.16" />
</ItemGroup>
</Project>
建議把版本固定下來。PowerShell SDK 雖然方便,但會受到應用程式執行環境、目標 .NET、PowerShell 模組相容性的影響。在業務應用程式中,與其直接沿用「開發環境中可動的最新版本」,明確標示已驗證過的版本會更安全。
3. 最小程式碼:執行 PowerShell 並接收 PSObject
首先試著透過 PowerShell 取得目前這個 C# 應用程式自身的處理程序。
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Management.Automation;
int currentProcessId = Environment.ProcessId;
using PowerShell ps = PowerShell.Create();
Collection<PSObject> results = ps
.AddCommand("Get-Process")
.AddParameter("Id", currentProcessId)
.Invoke();
foreach (PSObject item in results)
{
Console.WriteLine($"PSObject type: {item.GetType().FullName}");
Console.WriteLine($"BaseObject type: {item.BaseObject.GetType().FullName}");
if (item.BaseObject is Process process)
{
Console.WriteLine($"Id: {process.Id}");
Console.WriteLine($"Name: {process.ProcessName}");
Console.WriteLine($"Memory: {process.WorkingSet64:N0} bytes");
}
}
這裡有 3 個重點:用 PowerShell.Create() 建立 PowerShell 的執行物件;用 AddCommand("Get-Process") 與 AddParameter("Id", currentProcessId) 組裝命令與參數;以及 Invoke() 的回傳值是 Collection<PSObject>。
PSObject 是包裝 PowerShell 輸出值的包裝器(wrapper)。想查看內部原始的 .NET 物件時,就看 BaseObject。在這個範例中,Get-Process 結果的內容可以用 System.Diagnostics.Process 的形式取出。
4. BaseObject 與 Properties 的使用區分
在 C# 中處理 PowerShell 的結果時,最先讓人猶豫的就是下面這兩種寫法。
item.BaseObject
item.Properties["Name"]?.Value
該用哪一種,大致可以參考下表。
| 取值方式 | 使用場景 |
|---|---|
BaseObject |
想直接使用 PowerShell 傳回的原始 .NET 物件時 |
Properties["..."] |
想取出經由 Select-Object 或 [pscustomobject] 建立的欄位時 |
若直接執行像 Get-Process 這樣的命令,BaseObject 裡通常會放著原始的 .NET 物件。另一方面,若在 PowerShell 端使用 Select-Object 整理過欄位,結果多半會以 PowerShell 的自訂物件形式傳回。這種情況下,改用 Properties 依欄位名稱取值會更自然。
5. 在 C# 中讀取 Select-Object 後的結果
在實務上,很少會需要 PowerShell 傳回的所有屬性。如果只想把需要的欄位傳到 C# 端,可以在 PowerShell 管線中使用 Select-Object。
using System.Collections.ObjectModel;
using System.Globalization;
using System.Management.Automation;
using PowerShell ps = PowerShell.Create();
Collection<PSObject> rows = ps
.AddCommand("Get-Process")
.AddCommand("Sort-Object")
.AddParameter("Property", "CPU")
.AddParameter("Descending", true)
.AddCommand("Select-Object")
.AddParameter("First", 10)
.AddParameter("Property", new[] { "Name", "Id", "CPU", "WorkingSet" })
.Invoke();
foreach (PSObject row in rows)
{
string name = Convert.ToString(row.Properties["Name"]?.Value, CultureInfo.InvariantCulture) ?? "";
int id = Convert.ToInt32(row.Properties["Id"]?.Value, CultureInfo.InvariantCulture);
double? cpu = row.Properties["CPU"]?.Value is null
? null
: Convert.ToDouble(row.Properties["CPU"]!.Value, CultureInfo.InvariantCulture);
long workingSet = Convert.ToInt64(row.Properties["WorkingSet"]?.Value, CultureInfo.InvariantCulture);
Console.WriteLine($"{id}: {name}, CPU={cpu}, WorkingSet={workingSet:N0}");
}
這段程式碼相當於下面這樣的 PowerShell 管線:
Get-Process |
Sort-Object -Property CPU -Descending |
Select-Object -First 10 -Property Name, Id, CPU, WorkingSet
從 C# 的角度來看,透過連續呼叫 AddCommand,就組成了 PowerShell 的管線。
.AddCommand("Get-Process")
.AddCommand("Sort-Object")
.AddCommand("Select-Object")
這樣寫的話,前一個命令的輸出會傳給下一個命令。
用 Select-Object 篩選欄位之後,就可以像 row.Properties["Name"]?.Value 這樣依欄位名稱取值。
6. 轉換成 C# 的 record
如果讓 PSObject 原封不動地在整個應用程式中傳遞,後續的程式碼就會過度依賴 PowerShell。若要用於畫面顯示或業務處理,轉換成 C# 端的型別會更容易處理。
舉例來說,可以把處理程序資訊轉換成下面這個 record。
public sealed record ProcessSummary(
string Name,
int Id,
double? Cpu,
long WorkingSet);
把轉換處理拆開來寫,可讀性會更好。
using System.Globalization;
using System.Management.Automation;
static ProcessSummary ToProcessSummary(PSObject row)
{
string name = GetString(row, "Name");
int id = GetInt32(row, "Id");
double? cpu = GetNullableDouble(row, "CPU");
long workingSet = GetInt64(row, "WorkingSet");
return new ProcessSummary(name, id, cpu, workingSet);
}
static string GetString(PSObject row, string propertyName)
{
return Convert.ToString(row.Properties[propertyName]?.Value, CultureInfo.InvariantCulture) ?? "";
}
static int GetInt32(PSObject row, string propertyName)
{
return Convert.ToInt32(row.Properties[propertyName]?.Value, CultureInfo.InvariantCulture);
}
static long GetInt64(PSObject row, string propertyName)
{
return Convert.ToInt64(row.Properties[propertyName]?.Value, CultureInfo.InvariantCulture);
}
static double? GetNullableDouble(PSObject row, string propertyName)
{
object? value = row.Properties[propertyName]?.Value;
return value is null ? null : Convert.ToDouble(value, CultureInfo.InvariantCulture);
}
使用端則會是這樣:
List<ProcessSummary> processes = rows
.Select(ToProcessSummary)
.ToList();
foreach (ProcessSummary process in processes)
{
Console.WriteLine($"{process.Id}: {process.Name}");
}
PSObject 留在與 PowerShell 交界的地方處理,應用程式內部則轉換成像 ProcessSummary 這樣一般的 C# 型別。
做好這層分離後,即使之後修改 PowerShell 命令,影響範圍也能控制得更小。
7. 傳回 PSCustomObject 會讓 C# 端更容易處理
如果想在 PowerShell 端把多個值整合起來傳回,使用 [pscustomobject] 會很方便。
using System.Collections.ObjectModel;
using System.Management.Automation;
string script = @"
[pscustomobject]@{
MachineName = [System.Environment]::MachineName
PowerShellVersion = $PSVersionTable.PSVersion.ToString()
CurrentDirectory = (Get-Location).Path
}
";
using PowerShell ps = PowerShell.Create();
Collection<PSObject> rows = ps
.AddScript(script, useLocalScope: true)
.Invoke();
foreach (PSObject row in rows)
{
Console.WriteLine($"MachineName: {row.Properties["MachineName"]?.Value}");
Console.WriteLine($"PowerShell: {row.Properties["PowerShellVersion"]?.Value}");
Console.WriteLine($"Directory: {row.Properties["CurrentDirectory"]?.Value}");
}
只要在 PowerShell 腳本的最後傳回 [pscustomobject],C# 端就能從 Properties 依名稱取值。這比傳回複雜字串再由 C# 端拆分安全得多。
應該避免的是像下面這樣的輸出:
"$MachineName,$PowerShellVersion,$CurrentDirectory"
這種方式看起來簡單,但只要值裡面含有逗號或換行,就會出問題。
PowerShell 端傳回物件,C# 端則以屬性的方式讀取——維持這種形式,之後即使欄位增加也比較容易應付。
8. 不要把使用者輸入直接嵌入 AddScript
即使使用 PowerShell SDK,把腳本以字串方式組裝仍然很危險。舉例來說,下面這樣的程式碼應該避免:
// 應避免的寫法
string userInputPath = GetPathFromUser();
string script = $"Get-ChildItem -Path '{userInputPath}'";
using PowerShell ps = PowerShell.Create();
ps.AddScript(script).Invoke();
這種寫法會讓使用者輸入有可能被當作 PowerShell 程式碼解讀。要把值傳給 PowerShell 命令時,應盡量使用 AddCommand 與 AddParameter。
string userInputPath = GetPathFromUser();
using PowerShell ps = PowerShell.Create();
Collection<PSObject> files = ps
.AddCommand("Get-ChildItem")
.AddParameter("Path", userInputPath)
.AddParameter("File", true)
.Invoke();
用 AddParameter 傳遞的值,不會被當成 PowerShell 的程式碼字串串接,而是會被當作參數值處理。
在實務上,建議依下表區分使用方式:
| 寫法 | 適用情境 |
|---|---|
AddCommand / AddParameter |
想從 C# 端安全地組裝命令時 |
AddScript |
執行固定的短腳本,或載入既有腳本時 |
用字串串接組成的 AddScript |
原則上應避免。若要使用,務必謹慎驗證與轉義輸入值 |
把 PowerShell 整合進 C# 之後,應用程式就能具備強大的操作能力。方便之餘,唯一必須堅守的一條底線是:不要把使用者輸入直接變成腳本。
9. Format-Table 只用於最後的畫面顯示,傳給 C# 之前不要用
如果想在 C# 中以物件形式接收 PowerShell 的結果,基本上不會使用 Format-Table 或 Format-List。
舉例來說,下面這種 PowerShell 寫法對於讓人在畫面上檢視很方便:
Get-Service | Format-Table Name, Status
但是,如果在 C# 端接收之前使用了 Format-Table,結果就不再是服務物件,而會變成用於顯示的格式化資訊。如果要在 C# 中處理,應改用 Select-Object。
Get-Service | Select-Object Name, Status
從 C# 寫的話,會像下面這樣:
using PowerShell ps = PowerShell.Create();
Collection<PSObject> services = ps
.AddCommand("Get-Service")
.AddCommand("Select-Object")
.AddParameter("Property", new[] { "Name", "Status" })
.Invoke();
思路很簡單:
只是為了讓畫面好看 → Format-Table / Format-List
要在 C# 中做後續處理 → Select-Object / PSCustomObject
這個道理即使是單獨使用 PowerShell 時也適用,但在與 C# 整合時尤其重要。
10. 接收錯誤
在 PowerShell 中,輸出與錯誤是不同的資料流(stream)。如果只看 Invoke() 的回傳值,有可能會漏掉錯誤。基本寫法如下:
using System.Management.Automation;
using PowerShell ps = PowerShell.Create();
Collection<PSObject> output = ps
.AddCommand("Get-Item")
.AddParameter("Path", @"C:\no-such-file.txt")
.Invoke();
if (ps.HadErrors)
{
foreach (ErrorRecord error in ps.Streams.Error)
{
Console.WriteLine($"Error: {error.Exception.Message}");
Console.WriteLine($"Category: {error.CategoryInfo.Category}");
Console.WriteLine($"Target: {error.TargetObject}");
}
}
PowerShell 的 cmdlet 分為會終止處理的錯誤,以及可以繼續處理的錯誤。如果想在 C# 端當作例外(exception)處理,可以把 ErrorAction 指定為 Stop。
using System.Management.Automation;
try
{
using PowerShell ps = PowerShell.Create();
Collection<PSObject> output = ps
.AddCommand("Get-Item")
.AddParameter("Path", @"C:\no-such-file.txt")
.AddParameter("ErrorAction", "Stop")
.Invoke();
}
catch (RuntimeException ex)
{
Console.WriteLine($"PowerShell failed: {ex.Message}");
}
該用哪一種取決於應用程式的性質。在管理工具中,如果希望「即使部分失敗,也要輸出清單」,那麼回收錯誤資料流並顯示在畫面上會比較合適;如果希望「一旦失敗就中止整個處理」,用 ErrorAction Stop 當作例外處理會更清楚易懂。
11. 建立一個小型的執行包裝器
在需要多次呼叫 PowerShell 的應用程式中,如果每次都寫相同的錯誤處理,程式碼會變得雜亂。事先準備一個簡單的包裝器(wrapper)會很方便。
using System.Management.Automation;
public sealed record PowerShellRunResult(
IReadOnlyList<PSObject> Output,
IReadOnlyList<ErrorRecord> Errors);
public static class PowerShellRunner
{
public static PowerShellRunResult Run(Action<PowerShell> build)
{
using PowerShell ps = PowerShell.Create();
build(ps);
List<PSObject> output;
try
{
output = ps.Invoke().ToList();
}
catch (RuntimeException ex)
{
throw new InvalidOperationException($"PowerShell execution failed: {ex.Message}", ex);
}
return new PowerShellRunResult(
Output: output,
Errors: ps.Streams.Error.ToList());
}
}
使用端就能專注在組裝命令上。
PowerShellRunResult result = PowerShellRunner.Run(ps => ps
.AddCommand("Get-Service")
.AddCommand("Where-Object")
.AddParameter("Property", "Status")
.AddParameter("EQ", "Running")
.AddCommand("Select-Object")
.AddParameter("First", 10)
.AddParameter("Property", new[] { "Name", "DisplayName", "Status" }));
foreach (PSObject row in result.Output)
{
Console.WriteLine($"{row.Properties["Name"]?.Value}: {row.Properties["Status"]?.Value}");
}
foreach (ErrorRecord error in result.Errors)
{
Console.Error.WriteLine(error.Exception.Message);
}
不過,就像這個範例中的 Where-Object 一樣,把 PowerShell 特有的條件指定用 C# 組裝出來,有時會讓程式碼變得比較難讀。單純的命令與參數用 AddCommand / AddParameter 就足夠了,但複雜的篩選或彙總,有時準備成固定的 PowerShell 腳本會更容易閱讀。即便如此,「不要把外部輸入直接串接進腳本字串」這個原則仍然不變。
12. 複雜的處理交由 PowerShell 端整理成物件
結合 C# 與 PowerShell 時,先分清楚哪一邊負責什麼,設計上會更容易。
建議的分工如下:
| 負責 | 內容 |
|---|---|
| PowerShell | 貼近 Windows 或模組的操作、既有腳本、執行管理命令 |
| C# | UI、輸入驗證、型別轉換、業務邏輯、儲存、API 整合 |
在 PowerShell 端,把最終輸出整理成 [pscustomobject]。
Get-Service |
Where-Object Status -eq 'Running' |
Select-Object Name, DisplayName, Status
或者,明確地建立 [pscustomobject]。
$services = Get-Service | Where-Object Status -eq 'Running'
[pscustomobject]@{
Count = $services.Count
Names = $services.Name
}
在 C# 端,讀取傳回的 PSObject 屬性,轉換成自己應用程式的型別。
維持這種形式,就能避免把 PowerShell 的細節實作過度洩漏到 C# 端。
13. 實務上常見的注意事項
從 C# 執行 PowerShell 時,光是程式碼能動還不夠。在實務上,提早確認以下幾點會比較安全。
執行使用者的權限
PowerShell 會以執行 C# 應用程式的使用者權限運作。需要系統管理員權限的命令,即使用一般使用者身分執行也會失敗。服務操作、事件記錄、憑證、登錄檔、Hyper-V、Microsoft 365 管理相關模組等,都需要事先釐清權限。
32bit / 64bit 的差異
在 Windows 中,32bit 處理程序與 64bit 處理程序看到的登錄檔或模組有時會不同。如果要做成 Windows 管理用工具,基本上以 x64 為前提來執行,可以減少麻煩。
執行環境中是否有模組
即使在 C# 應用程式中加入 PowerShell SDK,也不代表所有 PowerShell 模組都會自動安裝進去。例如要使用特定產品的管理模組或內部模組時,需要確認執行環境中是否存在該模組,以及會從哪個路徑載入。
畫面應用程式中不要阻塞 UI 執行緒
從 WinForms 或 WPF 執行 PowerShell 時,如果直接在 UI 執行緒上執行繁重處理,畫面會凍住。這種情況下,應該設計成以背景處理方式執行,完成後再更新 UI。PowerShell SDK 也提供非同步執行的 API,但首先要守住「不在 UI 執行緒上執行長時間的 Invoke()」這條方針。
應用程式部署時的大小
Microsoft.PowerShell.SDK 雖然方便,但也會增加應用程式所包含的相依性。對於小型工具程式來說可能還能接受,但依部署形式或更新方式的不同,大小有時會成為問題。建議儘早以 ClickOnce、MSIX、單一 exe 部署、內部部署工具等實際的部署方式進行驗證,比較安心。
14. 以物件而非字串接收的優點
最後,來談談為什麼要如此堅持使用 PSObject。以外部程序方式執行 PowerShell、讀取標準輸出的方法確實簡單。
PowerShell 的輸出
↓
字串
↓
Split / 正規表達式 / Substring
↓
C# 的值
不過,這種方法會依賴顯示格式。欄寬、地區設定(locale)、換行、空白、錯誤訊息,以及值中包含的分隔字元,都容易讓它壞掉。
另一方面,使用 PowerShell SDK 則會是下面這個流程:
PowerShell 的輸出
↓
PSObject
↓
Properties / BaseObject
↓
C# 的型別
這種方式是根據資料結構而非顯示格式來取值。對業務應用程式或管理工具來說,後者更容易維護。
15. 總結
如果要從 C# 執行 PowerShell 並處理結果,除了單純啟動 powershell.exe 讀取標準輸出之外,值得考慮使用 PowerShell SDK 的做法。
基本流程如下:
加入 Microsoft.PowerShell.SDK
↓
用 PowerShell.Create() 建立執行物件
↓
用 AddCommand / AddParameter / AddScript 組裝處理
↓
用 Invoke() 執行
↓
以 Collection<PSObject> 形式接收
↓
從 BaseObject 或 Properties 取出值
↓
轉換成 C# 的 DTO / record / class
在實務上特別重要的是下面這 3 點:
- 若要在 C# 中做後續處理,應使用
Select-Object或[pscustomobject],而不是Format-Table - 不要把使用者輸入直接嵌入
AddScript的字串,盡量透過AddParameter傳遞 PSObject只在交界處處理,應用程式內部則轉換成 C# 的型別
PowerShell 擅長 Windows 管理與運用既有資產,C# 擅長應用程式化、畫面化,以及型別安全的業務處理。把兩者妥善連接起來,就能不捨棄既有的 PowerShell 腳本,逐步整理成 .NET 應用程式。
參考資料
- 本文的範例程式碼一整套(函式庫、demo、單元測試) https://github.com/gomurin0428/komurasoft-blog-samples/tree/main/csharp-run-powershell-receive-objects
- Microsoft Learn:Windows PowerShell 主機的快速入門
https://learn.microsoft.com/ja-jp/powershell/scripting/developer/hosting/windows-powershell-host-quickstart - Microsoft Learn:新增並呼叫命令
https://learn.microsoft.com/ja-jp/powershell/scripting/developer/hosting/adding-and-invoking-commands - Microsoft Learn:PowerShell Class
https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.powershell - Microsoft Learn:PSObject Class
https://learn.microsoft.com/ja-jp/dotnet/api/system.management.automation.psobject - NuGet Gallery:Microsoft.PowerShell.SDK
https://www.nuget.org/packages/Microsoft.PowerShell.SDK/ - NuGet Gallery:Microsoft.PowerShell.5.1.ReferenceAssemblies
https://www.nuget.org/packages/Microsoft.PowerShell.5.1.ReferenceAssemblies/
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
委外・委託開發 Windows 應用程式前該整理的事項
在委外・委託開發 Windows 應用程式之前,整理既有軟體改版、設備整合、COM/ActiveX、發布與更新、維護等應留意的重點。
用 Pester 整備 PowerShell 測試 ── 讓維運腳本不易損壞的實務做法
本文整理用 Pester v5 測試 PowerShell 腳本的實務步驟,涵蓋日期處理、檔案操作、刪除處理、Mock,一路到 CI 執行,協助安全地建立測試基礎。
PowerShell 實用指令集錦 ── 累積日常工作常用的小工具
本文整理 PowerShell 日常工作中常用的實用指令,說明 Measure-Object、Group-Object、Select-String、Compare-Object、Tee-Object、Start-Transcript 等指令的使用場景與時機。
PowerShell 腳本應用 ── 安全地自動化日誌調查、封存與報表化
本文整理用 PowerShell 腳本安全地推進日誌調查、CSV 報表、舊日誌封存、留存紀錄,一直到工作排程器定期執行的實務步驟。
PowerShell 指令基礎 ── 該先學會的操作與安全使用方式
為了讓 PowerShell 初學者在實務上不會迷路,本文整理了如何尋找 Cmdlet、管線、檔案操作、CSV 處理、執行原則,以及避免事故的確認步驟。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
既有資產活用 & 遷移支援
在持續活用 COM / ActiveX / OCX 資產、原生程式碼與 32 位元相依的同時,協助規劃階段性的遷移。
常見問題
整理諮詢這個主題時常見的問題。
- 從 C# 執行 PowerShell,該用哪種方法比較好?
- 大致可以分成兩種方式:用 ProcessStartInfo 將 powershell.exe / pwsh.exe 當作外部程序啟動,以及使用 System.Management.Automation.PowerShell(PowerShell SDK)。如果只是要把標準輸出當成字串讀取,前者也能動;但在 C# 端需要加工結果的管理工具或業務應用程式中,能以 PSObject 集合形式接收結果的後者更為合適。這樣就不需要解析字串,也能寫出不依賴顯示格式的安全處理邏輯。
- PSObject 的 BaseObject 與 Properties 該如何區分使用?
- 當你想直接使用 PowerShell 傳回的原始 .NET 物件時,就使用 BaseObject。例如直接執行 Get-Process 的結果,可以以 System.Diagnostics.Process 的形式取出。另一方面,經由 Select-Object 或 [pscustomobject] 整理過欄位的結果,通常會以 PowerShell 的自訂物件形式傳回,這種情況下用 Properties["欄位名稱"]?.Value 依欄位名稱取值會更自然。
- 從 C# 將使用者輸入傳給 PowerShell 時,該注意什麼?
- 應避免用字串串接的方式,把使用者輸入直接嵌入 AddScript 的腳本中,因為輸入內容有可能被當成 PowerShell 程式碼解讀,相當危險。若要傳遞值,使用 AddCommand 搭配 AddParameter,值就會被當作參數值處理,而不是程式碼字串。建議將 AddScript 限定用於固定的短腳本,或載入既有腳本這類情境。
- 在 C# 接收 PowerShell 結果時,為什麼不該使用 Format-Table?
- 因為經過 Format-Table 或 Format-List 之後,結果會變成用於顯示的格式化資訊,而不是原本的物件,導致 C# 端無法以屬性的方式取出值。若要在 C# 中做後續處理,應改用 Select-Object 篩選欄位,或在 PowerShell 端整理成傳回 [pscustomobject] 的形式。可以這樣區分:「只在畫面上檢視就用 Format 系列,要傳給 C# 就用 Select-Object」。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。