最近負責的案子是要在 Windows 的設備中定時回傳資料到伺服器,包含一些對 Windows 的操作,第一次製作類似這樣的程式,寫了很多沒有寫過的東西。

記憶體、處理器、硬碟…等等基本的資訊可以透過 WMI 物件取得 Windows Management Instrumentation (WMI)

Windows Management Instrumentation (WMI) 是 Microsoft 在 Web 架構企業管理 (WBEM) 方面的實作,這是一種開發標準技術的業界措施,用於存取企業環境中的管理資訊。 WMI 使用通用訊息模型 (CIM) 業界標準來代表系統、應用程式、網路、裝置和其他受管理元件。 CIM 是由分散式管理工作組 (DMTF) 所開發和維護。

– Microsoft docs

簡單說就是 Windows 內建的一個東西,可以呼叫他做很多事情就對了!
那可以透過 System.Management 去呼叫,但是在 .Net 6 的環境下需要另外安裝此套件(舊的 .Net 好像有內建,不確定,網路上寫的)

可以透過 Nuget 安裝,確認有安裝後實作方式為以下

using System.Management;

ManagementClass mangnmt = new ManagementClass("Win32_PhysicalMemory");
ManagementObjectCollection managementObjectCollection = mangnmt.GetInstances();
foreach (ManagementObject managementObject in managementObjectCollection)
{
    var data = managementObject.GetPropertyValue("Name");
    Console.WriteLine(data);
}

遇到的問題是,在取得安裝的軟體列表的時候可以用 Win32_Product 來取得

但是透過 Win32_Product 取得的時候,速度非常慢,在舊電腦 Windows 10(四代 i7)上跑要將近一分鐘,新安裝的 Windows 11(十二代 i5)也要將近十秒的時間
所以這邊就想說有沒有其他方式可以取得安裝列表

另一方式是從註冊表,註冊表會記錄每一個軟體解除安裝的路徑,對應到的是控制台的應用程式,包含版本號以及解除安裝的執行檔案位置都是記錄在註冊表

這邊用 RegistryKey 取得,程式如下

string uninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey rk64 = Registry.LocalMachine.OpenSubKey(uninstallKey);
foreach (string skName in rk64.GetSubKeyNames())
{
    RegistryKey sk = rk64.OpenSubKey(skName);
    var name = sk.GetValue("DisplayName")?.ToString();
    if (!string.IsNullOrEmpty(name)) Console.WriteLine(name);
}

這邊要注意的是 x64 跟 x86 的軟體會在註冊表不同的位置
如果是在 x64 的 Windows 環境下

x64 軟體註冊表位置:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
x86 軟體註冊表位置:SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

但是如果在 x86 的 Windows 環境下
x86 軟體註冊表位置:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

這個是比較需要注意的地方,不過 .Net 6 非常方便可以用 Environment.Is64BitOperatingSystem 來判斷是否是 x64 的環境(True/False)

System.Environment.Is64BitOperatingSystem

完整代碼如下:

var result = new List<SoftwareView>();

if (Environment.Is64BitOperatingSystem)
{
    // x64 Registry
    string uninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    RegistryKey rk64 = Registry.LocalMachine.OpenSubKey(uninstallKey);
    foreach (string skName in rk64.GetSubKeyNames())
    {
        RegistryKey sk = rk64.OpenSubKey(skName);
        var name = sk.GetValue("DisplayName")?.ToString();
        if (!string.IsNullOrEmpty(name)) result.Add(SoftwareMap(name, sk, "x64"));
    }

    // x86 Registry
    uninstallKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
    RegistryKey rk32 = Registry.LocalMachine.OpenSubKey(uninstallKey);
    foreach (string skName in rk32.GetSubKeyNames())
    {
        RegistryKey sk = rk32.OpenSubKey(skName);
        var name = sk.GetValue("DisplayName")?.ToString();
        if (!string.IsNullOrEmpty(name)) result.Add(SoftwareMap(name, sk, "x86"));
    }
}
else
{
    // x86 Registry
    string uninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    RegistryKey rk64 = Registry.LocalMachine.OpenSubKey(uninstallKey);
    foreach (string skName in rk64.GetSubKeyNames())
    {
        RegistryKey sk = rk64.OpenSubKey(skName);
        var name = sk.GetValue("DisplayName")?.ToString();
        if (!string.IsNullOrEmpty(name)) result.Add(SoftwareMap(name, sk, "x86"));
    }
}

return result;

SoftwareView SoftwareMap(string name, RegistryKey sk, string platform)
{
    return new SoftwareView
    {
        DisplayName = name,
        DisplayVersion = sk.GetValue("DisplayVersion")?.ToString(),
        InstallDate = sk.GetValue("InstallDate")?.ToString(),
        InstallSource = sk.GetValue("InstallSource")?.ToString(),
        InstallLocation = sk.GetValue("InstallLocation")?.ToString(),
        InstallDir = sk.GetValue("InstallDir")?.ToString(),
        PSChildName = sk.GetValue("PSChildName")?.ToString(),
        Publisher = sk.GetValue("Publisher")?.ToString(),
        QuietUninstallString = sk.GetValue("QuietUninstallString")?.ToString(),
        UninstallString = sk.GetValue("UninstallString")?.ToString(),
        Platform = platform
    };
}
public class SoftwareView
{
    public string DisplayName { get; set; }

    public string DisplayVersion { get; set; }

    public string InstallSource { get; set; }

    public string InstallLocation { get; set; }

    public string InstallDir { get; set; }

    public string Publisher { get; set; }

    public string InstallDate { get; set; }

    public string PSChildName { get; set; }

    public string Platform { get; set; }

    public string QuietUninstallString { get; set; }

    public string UninstallString { get; set; }

}

不曉得還有沒有更好的取得方式,這邊透過註冊表取得的列表和 Win32_Product 取得的資料是有不同的,但是目前沒有確認差異在哪裡。

相關資源 :

System.Management: https://www.nuget.org/packages/System.Management/

分類於:

標籤:

, ,