如何用C#获取Windows系统已安装软件及对应许可证信息
解决方案:多源数据整合 + 针对性密钥提取
没有单一方案能覆盖所有场景,建议结合注册表、WMI、系统API及特定软件的密钥解析逻辑,互补短板。以下是具体实现思路和代码:
一、获取已安装软件列表:注册表为主,WMI补全
1. 注册表读取(快速覆盖多数软件)
Windows多数软件会在注册表的Uninstall项注册信息,需同时处理64位和32位路径:
using Microsoft.Win32; using System.Collections.Generic; public List<SoftwareInfo> GetSoftwareFromRegistry() { var softwareList = new List<SoftwareInfo>(); // 覆盖64位系统下的32/64位软件路径 var paths = new[] { @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" }; foreach (var path in paths) { using (var key = Registry.LocalMachine.OpenSubKey(path)) { if (key == null) continue; foreach (var subKeyName in key.GetSubKeyNames()) { using (var subKey = key.OpenSubKey(subKeyName)) { if (subKey == null) continue; var name = subKey.GetValue("DisplayName")?.ToString(); var version = subKey.GetValue("DisplayVersion")?.ToString(); var publisher = subKey.GetValue("Publisher")?.ToString(); if (!string.IsNullOrEmpty(name)) { softwareList.Add(new SoftwareInfo { Name = name, Version = version, Publisher = publisher, Source = "Registry" }); } } } } } return softwareList; } public class SoftwareInfo { public string Name { get; set; } public string Version { get; set; } public string Publisher { get; set; } public string LicenseKey { get; set; } public string Source { get; set; } }
2. WMI补全(针对MSI安装的软件)
Win32_Product确实慢,但可以通过只查询必要字段优化速度,避免全量属性查询:
using System.Management; public List<SoftwareInfo> GetSoftwareFromWmi() { var softwareList = new List<SoftwareInfo>(); // 仅查询核心字段,减少数据传输 var query = new SelectQuery("SELECT Name, Version, Vendor, IdentifyingNumber FROM Win32_Product"); using (var searcher = new ManagementObjectSearcher(query)) { foreach (var obj in searcher.Get()) { softwareList.Add(new SoftwareInfo { Name = obj["Name"]?.ToString(), Version = obj["Version"]?.ToString(), Publisher = obj["Vendor"]?.ToString(), Source = "WMI" }); } } return softwareList; }
二、提取许可证密钥:分场景处理
1. Windows系统产品密钥
读取注册表的DigitalProductId并解密,无需依赖第三方工具:
using System; using System.Text; public string GetWindowsProductKey() { using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")) { if (key == null) return null; var digitalProductId = key.GetValue("DigitalProductId") as byte[]; if (digitalProductId == null) return null; // Windows通用密钥解密算法 const string chars = "BCDFGHJKMPQRTVWXY2346789"; var keyParts = new int[15]; Array.Copy(digitalProductId, 52, keyParts, 0, 15); var productKey = new StringBuilder(); for (int i = 24; i >= 0; i--) { int remainder = 0; for (int j = 14; j >= 0; j--) { int temp = remainder * 256 + keyParts[j]; keyParts[j] = temp / 24; remainder = temp % 24; } productKey.Insert(0, chars[remainder]); if ((i % 5 == 0) && (i != 0)) productKey.Insert(0, "-"); } return productKey.ToString(); } }
2. Office套件密钥
类似Windows密钥,读取对应版本的注册表项解密:
public string GetOfficeProductKey(string officeVersion = "16.0") // 16.0=Office 2019/365, 15.0=2013 { var path = $@"SOFTWARE\Microsoft\Office\{officeVersion}\Registration"; using (var key = Registry.LocalMachine.OpenSubKey(path)) { if (key == null) return null; foreach (var subKeyName in key.GetSubKeyNames()) { using (var subKey = key.OpenSubKey(subKeyName)) { if (subKey == null) continue; var digitalProductId = subKey.GetValue("DigitalProductID") as byte[]; if (digitalProductId == null) continue; // Office密钥解密逻辑(偏移量与Windows不同) const string chars = "BCDFGHJKMPQRTVWXY2346789"; var keyParts = new int[15]; Array.Copy(digitalProductId, 56, keyParts, 0, 15); var productKey = new StringBuilder(); for (int i = 24; i >= 0; i--) { int remainder = 0; for (int j = 14; j >= 0; j--) { int temp = remainder * 256 + keyParts[j]; keyParts[j] = temp / 24; remainder = temp % 24; } productKey.Insert(0, chars[remainder]); if ((i % 5 == 0) && (i != 0)) productKey.Insert(0, "-"); } return productKey.ToString(); } } } return null; }
3. 第三方软件密钥
多数第三方软件会将密钥存在特定注册表路径(如HKLM\Software\[厂商]\[软件名]下的SerialNumber或LicenseKey字段),可针对性读取:
public string GetThirdPartyLicenseKey(string vendorPath, string keyName) { using (var key = Registry.LocalMachine.OpenSubKey(vendorPath)) { return key?.GetValue(keyName)?.ToString(); } } // 示例:获取Adobe Photoshop密钥 // var photoshopKey = GetThirdPartyLicenseKey(@"SOFTWARE\Adobe\Adobe Photoshop\2024", "SerialNumber");
4. 系统许可证状态(替代Slmgr)
直接调用系统自带的slmgr.vbs解析输出,无需额外依赖:
using System.Diagnostics; public string GetWindowsLicenseStatus() { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "cscript.exe", Arguments = @"//nologo %windir%\system32\slmgr.vbs /dli", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); var output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); return output; }
三、优化与注意事项
- 权限:必须以管理员身份运行程序,否则无法读取部分注册表路径和WMI信息。
- 去重:整合注册表和WMI的软件列表时,需按名称/IdentifyingNumber去重。
- 异步并行:将注册表读取、WMI查询、密钥提取放在异步任务中并行执行,减少总耗时。
- 局限性:部分软件的许可证密钥会被加密存储或不公开,无法通过通用方法提取,只能覆盖主流软件。
内容的提问来源于stack exchange,提问作者Majid Ebadiparsa
相关产品推荐
相关产品推荐

