首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# WMI 網路程式設計完整指南:System.Management 核心類別深度解析

     編輯:關於C#

前言

Windows Management Instrumentation (WMI) 是 Windows 內建的管理基礎設施,提供統一介面查詢/操作系統、硬體、網路、服務、進程等資源。.NET 透過 `System.Management` 命名空間完整支援 WMI,讓 C# 開發者能輕鬆建立**無代理程式**的遠端管理工具。本文深度解析六大核心類別、連線認證、WQL 查詢、遠端操作與實戰模式。

WMI 服務基礎知識

  • 服務名稱:`Winmgmt` (Windows Management Instrumentation)
  • 執行檔:`%SystemRoot%\System32\wbem\WinMgmt.exe`
  • 命名空間:`root\cimv2` (核心類別)、`root\default` (系統預設)、`root\directory` (LDAP) 等
  • 權限模型:本機 Administrators 群組預設擁有完整權限;可透過「電腦管理 → 服務與應用程式 → WMI 控制 → 安全性」為特定使用者/群組授予「啟用帳號」、「遠端啟用」、「執行方法」、「寫入提供者」等權限
  • 防火牆:遠端 WMI 需開放 TCP 135 (RPC 端點對應器) + 動態連接埠範圍 (通常 49152-65535),或使用 `winrm quickconfig` 設定 WinRM (HTTP/HTTPS 5985/5986) 作為替代傳輸

六大核心類別完整解析

1. ConnectionOptions - 連線認證選項


using System.Management;

var options = new ConnectionOptions
{
    Username = @"DOMAIN\Administrator", // 或 ".\Administrator" (本機)
    Password = "P@ssw0rd",
    Authority = "ntlmdomain:DOMAIN",    // 指定驗證授權單位 (Kerberos/NTLM)
    Locale = "en-US",                   // 地區設定 (影響日期/數字格式)
    EnablePrivileges = true,            // 啟用特殊權限 (如 SeShutdownPrivilege)
    Impersonation = ImpersonationLevel.Impersonate, // 模擬等級
    Authentication = AuthenticationLevel.PacketPrivacy, // 加密封包
    Timeout = TimeSpan.FromMinutes(2)   // 連線逾時
};

2. ManagementScope - 管理範圍 (連線上下文)


// 方式一:建構子直接連線 (可能拋出例外)
var scope = new ManagementScope(@"\\Server01\root\cimv2", options);

// 方式二:分步連線 (可捕獲連線錯誤)
var scope = new ManagementScope(@"\\Server01\root\cimv2");
scope.Options = options;
try
{
    scope.Connect(); // 實際建立連線
    Console.WriteLine($"已連線至 {scope.Path.Server}");
}
catch (ManagementException ex)
{
    Console.WriteLine($"WMI 錯誤: {ex.ErrorCode} - {ex.Message}");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("存取被拒絕:檢查帳號密碼、WMI 權限、防火牆");
}
catch (System.Runtime.InteropServices.COMException ex)
{
    Console.WriteLine($"RPC 錯誤 (通常是防火牆/服務未啟動): 0x{ex.ErrorCode:X}");
}

3. ObjectQuery / SelectQuery - WQL 查詢物件


// 簡單查詢
var query = new ObjectQuery("SELECT * FROM Win32_OperatingSystem");

// 參數化查詢 (SelectQuery 繼承 ObjectQuery)
var query = new SelectQuery("Win32_Service", "State = 'Running' AND StartMode = 'Auto'");
// 等同: SELECT * FROM Win32_Service WHERE State = 'Running' AND StartMode = 'Auto'

// 投影查詢 (只取需要欄位,效能較好)
var query = new SelectQuery("SELECT Name, ProcessId, ExecutablePath FROM Win32_Process WHERE Name LIKE 'chrome%'");

4. ManagementObjectSearcher - 查詢執行器


var searcher = new ManagementObjectSearcher(scope, query);

// 同步執行 (阻塞直到完成)
ManagementObjectCollection results = searcher.Get();

// 非同步執行 (事件模式)
searcher.GetCompleted += (sender, e) => 
{
    if (e.Error != null) Console.WriteLine($"查詢失敗: {e.Error}");
    else foreach (ManagementObject obj in e.Result) ProcessObject(obj);
};
searcher.GetAsync();

// 取消查詢
searcher.CancelAsync();

5. ManagementObjectCollection - 結果集合


// 實作 IEnumerable,支援 foreach
foreach (ManagementObject obj in results)
{
    Console.WriteLine($"物件路徑: {obj.Path}");
    foreach (PropertyData prop in obj.Properties)
    {
        Console.WriteLine($"  {prop.Name} = {prop.Value} (型別: {prop.Type})");
    }
}

// 取得計數 (需完整枚舉)
int count = results.Count; // 注意:這會觸發完整枚舉

// 複製到陣列
ManagementObject[] array = new ManagementObject[results.Count];
results.CopyTo(array, 0);

6. ManagementObject - 單一管理物件


// 核心屬性
obj.Path;           // 物件路徑 (可用於後續直接取得)
obj.ClassPath;      // 類別路徑
obj.Properties;     // 所有屬性集合
obj.SystemProperties; // 系統屬性 (__SERVER, __NAMESPACE, __CLASS, __RELPATH 等)

// 核心方法
obj.Get();                    // 重新整理資料 (同步)
obj.GetAsync();               // 非同步重新整理
obj.InvokeMethod("MethodName", args); // 呼叫方法 (如 Reboot, Shutdown, Create, Delete)
obj.Put();                    // 提交屬性變更
obj.Delete();                 // 刪除物件實例
obj.GetRelated();             // 取得關聯物件 (關聯類別)
obj.GetRelationships();       // 取得關聯關係物件

實戰範例 1:遠端電腦硬體資產掃描


public class AssetScanner
{
    private readonly ConnectionOptions _options;

    public AssetScanner(string username, string password, string domain = null)
    {
        _options = new ConnectionOptions
        {
            Username = string.IsNullOrEmpty(domain) ? username : $@"{domain}\{username}",
            Password = password,
            Impersonation = ImpersonationLevel.Impersonate,
            Authentication = AuthenticationLevel.PacketPrivacy
        };
    }

    public ComputerAsset Scan(string computerName)
    {
        var scope = new ManagementScope($@"\\{computerName}\root\cimv2", _options);
        scope.Connect();

        var asset = new ComputerAsset { ComputerName = computerName };

        // 作業系統
        QuerySingle(scope, "SELECT Caption, Version, BuildNumber, OSArchitecture FROM Win32_OperatingSystem",
            obj => { asset.OS = $"{obj["Caption"]} {obj["Version"]} ({obj["OSArchitecture"]})"; });

        // CPU
        QuerySingle(scope, "SELECT Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed FROM Win32_Processor",
            obj => { asset.CPU = $"{obj["Name"]} ({obj["NumberOfCores"]}C/{obj["NumberOfLogicalProcessors"]}T) @{obj["MaxClockSpeed"]}MHz"; });

        // 記憶體
        QuerySingle(scope, "SELECT TotalVisibleMemorySize FROM Win32_OperatingSystem",
            obj => { asset.TotalMemoryGB = Math.Round(Convert.ToDouble(obj["TotalVisibleMemorySize"]) / 1024 / 1024, 1); });

        // 磁碟
        foreach (var obj in Query(scope, "SELECT DeviceID, Size, FreeSpace, FileSystem FROM Win32_LogicalDisk WHERE DriveType = 3"))
        {
            asset.Disks.Add(new DiskInfo
            {
                Drive = obj["DeviceID"].ToString(),
                SizeGB = Math.Round(Convert.ToDouble(obj["Size"]) / 1024 / 1024 / 1024, 1),
                FreeGB = Math.Round(Convert.ToDouble(obj["FreeSpace"]) / 1024 / 1024 / 1024, 1),
                FileSystem = obj["FileSystem"]?.ToString()
            });
        }

        // 網卡
        foreach (var obj in Query(scope, "SELECT Description, MACAddress, IPEnabled, IPAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True"))
        {
            asset.NetworkAdapters.Add(new NetworkInfo
            {
                Description = obj["Description"]?.ToString(),
                MAC = obj["MACAddress"]?.ToString(),
                IPs = (string[])obj["IPAddress"]
            });
        }

        return asset;
    }

    private ManagementObjectCollection Query(ManagementScope scope, string wql)
    {
        return new ManagementObjectSearcher(scope, new ObjectQuery(wql)).Get();
    }

    private void QuerySingle(ManagementScope scope, string wql, Action action)
    {
        foreach (ManagementObject obj in Query(scope, wql))
        {
            action(obj);
            break; // 只取第一筆
        }
    }
}

// 資料模型
public class ComputerAsset
{
    public string ComputerName { get; set; }
    public string OS { get; set; }
    public string CPU { get; set; }
    public double TotalMemoryGB { get; set; }
    public List Disks { get; } = new();
    public List NetworkAdapters { get; } = new();
}

public class DiskInfo { public string Drive { get; set; } public double SizeGB { get; set; } public double FreeGB { get; set; } public string FileSystem { get; set; } }
public class NetworkInfo { public string Description { get; set; } public string MAC { get; set; } public string[] IPs { get; set; } }

實戰範例 2:遠端服務管理 (啟動/停止/重啟)


public class ServiceController
{
    private readonly ManagementScope _scope;

    public ServiceController(string computer, ConnectionOptions options)
    {
        _scope = new ManagementScope($@"\\{computer}\root\cimv2", options);
        _scope.Connect();
    }

    public ServiceStatus GetStatus(string serviceName)
    {
        var query = new SelectQuery("Win32_Service", $"Name = '{serviceName}'");
        using var searcher = new ManagementObjectSearcher(_scope, query);
        foreach (ManagementObject svc in searcher.Get())
        {
            return new ServiceStatus
            {
                Name = svc["Name"].ToString(),
                DisplayName = svc["DisplayName"].ToString(),
                State = svc["State"].ToString(), // Running, Stopped, Paused
                StartMode = svc["StartMode"].ToString(), // Auto, Manual, Disabled
                ProcessId = Convert.ToInt32(svc["ProcessId"])
            };
        }
        return null;
    }

    public bool StartService(string serviceName)
    {
        return InvokeServiceMethod(serviceName, "StartService");
    }

    public bool StopService(string serviceName)
    {
        return InvokeServiceMethod(serviceName, "StopService");
    }

    private bool InvokeServiceMethod(string serviceName, string methodName)
    {
        var query = new SelectQuery("Win32_Service", $"Name = '{serviceName}'");
        using var searcher = new ManagementObjectSearcher(_scope, query);
        foreach (ManagementObject svc in searcher.Get())
        {
            var result = svc.InvokeMethod(methodName, null);
            return Convert.ToUInt32(result) == 0; // 0 = 成功
        }
        return false;
    }
}

public class ServiceStatus
{
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public string State { get; set; }
    public string StartMode { get; set; }
    public int ProcessId { get; set; }
}

實戰範例 3:遠端程序管理與效能監控


public class ProcessManager
{
    private readonly ManagementScope _scope;

    public ProcessManager(string computer, ConnectionOptions options)
    {
        _scope = new ManagementScope($@"\\{computer}\root\cimv2", options);
        _scope.Connect();
    }

    public List GetTopProcessesByCpu(int top = 10)
    {
        var query = new SelectQuery(@"
            SELECT Name, ProcessId, PercentProcessorTime, WorkingSetSize, PageFileUsage 
            FROM Win32_PerfFormattedData_PerfProc_Process 
            WHERE Name NOT IN ('_Total', 'Idle', 'System')
            ORDER BY PercentProcessorTime DESC");
        
        var results = new List();
        using var searcher = new ManagementObjectSearcher(_scope, query);
        int count = 0;
        foreach (ManagementObject proc in searcher.Get())
        {
            if (count++ >= top) break;
            results.Add(new ProcessInfo
            {
                Name = proc["Name"].ToString(),
                ProcessId = Convert.ToInt32(proc["ProcessId"]),
                CpuPercent = Convert.ToDouble(proc["PercentProcessorTime"]),
                WorkingSetMB = Math.Round(Convert.ToDouble(proc["WorkingSetSize"]) / 1024 / 1024, 1),
                PageFileMB = Math.Round(Convert.ToDouble(proc["PageFileUsage"]) / 1024 / 1024, 1)
            });
        }
        return results;
    }

    public bool KillProcess(int processId)
    {
        var query = new SelectQuery("Win32_Process", $"ProcessId = {processId}");
        using var searcher = new ManagementObjectSearcher(_scope, query);
        foreach (ManagementObject proc in searcher.Get())
        {
            var result = proc.InvokeMethod("Terminate", new object[] { 0 }); // exit code 0
            return Convert.ToUInt32(result) == 0;
        }
        return false;
    }
}

public class ProcessInfo
{
    public string Name { get; set; }
    public int ProcessId { get; set; }
    public double CpuPercent { get; set; }
    public double WorkingSetMB { get; set; }
    public double PageFileMB { get; set; }
}

WQL 查詢語言速查

語法範例說明
SELECTSELECT Name, State FROM Win32_Service投影欄位,* 為全部
WHEREWHERE State = 'Running'字串用單引號,數值直接寫
LIKEWHERE Name LIKE 'SQL%'% 多字元,_ 單字元
IS NULLWHERE ParentProcessId IS NULL檢查空值
ISAWHERE __CLASS ISA 'Win32_BaseService'繼承關係查詢
ASSOCIATORS OFASSOCIATORS OF {Win32_Service.Name='wuauserv'} WHERE ResultClass=Win32_DependentService關聯查詢 (相依服務)
REFERENCES OFREFERENCES OF {Win32_Service.Name='wuauserv'}反向關聯查詢

常見錯誤代碼與排查

錯誤代碼HRESULT原因解決
Access Denied0x80070005權限不足檢查 WMI 安全性設定、帳號群組、UAC 遠端限制
RPC Server Unavailable0x800706BARPC 服務停用/防火牆啟動 RPC 服務、開放 TCP 135/動態埠、檢查 `winrm quickconfig`
Invalid Namespace0x8004100E命名空間錯誤確認 `root\cimv2` 拼寫、目標 OS 版本支援
Provider Load Failure0x80041013WMI 提供者損壞重建 WMI 儲存庫: `winmgmt /salvagerepository` 或 `/resetrepository`
Quota Violation0x8004106C查詢傳回資料過大加上 WHERE 條件、分頁查詢、投影必要欄位

效能優化建議

  • ✅ **投影必要欄位**:`SELECT Name, State` 而非 `SELECT *`
  • ✅ **加上 WHERE 條件**:減少傳輸資料量
  • ✅ **重用 ManagementScope**:同一台電腦多次查詢共用連線
  • ✅ **非同步查詢**:UI 應用使用 `GetAsync()` 避免阻塞
  • ✅ **批次處理**:多台電腦平行掃描 (Parallel.ForEach + 限制併發度)
  • ✅ **快取結果**:靜態資產資訊快取 1-24 小時

安全性最佳實務

  1. 建立專用服務帳號 (非 Domain Admin),僅授予必要 WMI 權限
  2. 使用 `AuthenticationLevel.PacketPrivacy` 強制加密傳輸
  3. 避免在程式碼硬編碼密碼,使用 Windows Credential Manager / Azure Key Vault / 環境變數
  4. 啟用 WMI 事件記錄稽核 (事件 ID 5600-5605)
  5. 定期輪換服務帳號密碼

總結

`System.Management` 提供了完整、強型別的 WMI 存取能力。掌握 **ConnectionOptions → ManagementScope → ObjectQuery → ManagementObjectSearcher → ManagementObjectCollection → ManagementObject** 這條核心鏈路,配合 WQL 查詢語言,即可建立企業級的無代理遠端管理系統。關鍵成功因素:**正確的權限設定、防火牆規則、錯誤處理、效能優化、安全性實務**。現代替代方案包括 PowerShell Remoting (WinRM)、CIM Cmdlets (`Get-CimInstance`)、Windows Admin Center、Azure Arc 等,但 WMI 仍是 Windows 管理的基石,深度理解極具價值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved