最近負責的案子是要在 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/