如何使用 C# 代码获取系统中安装的应用程序?
遍历注册表键“SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall”似乎可以提供已安装应用程序的全面列表。
除了下面的示例,您还可以找到类似于我所做的此处的版本。
这只是一个简单的示例,您可能需要像第二个链接中提供的那样去掉空行。
string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using(Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
{
foreach(string subkey_name in key.GetSubKeyNames())
{
using(RegistryKey subkey = key.OpenSubKey(subkey_name))
{
Console.WriteLine(subkey.GetValue("DisplayName"));
}
}
}
或者,您可以使用已经提到的WMI:
ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach(ManagementObject mo in mos.Get())
{
Console.WriteLine(mo["Name"]);
}
但是这种方法执行起来相对较慢,我听说它可能只列出在“ALLUSERS”下安装的程序,但这可能是不正确的。此外,它也会忽略Windows组件和更新,而这可能对你有用。
我想要提取一个应用程序列表,就像它们在开始菜单中显示的那样。使用注册表,我得到的条目并没有出现在开始菜单中。
我还想找到exe路径,并提取图标以最终制作一个漂亮的启动器。不幸的是,使用注册表方法有点靠运气,因为我的观察结果是这些信息不是可靠地可用的。
我的替代方法基于shell:AppsFolder,您可以通过运行explorer.exe shell:appsFolder
来访问它,它列出了所有应用程序,包括当前安装并可通过开始菜单访问的商店应用程序。问题是这是一个虚拟文件夹,无法使用System.IO.Directory
访问。相反,您需要使用本机shell32命令。幸运的是,Microsoft在Nuget上发布了Microsoft.WindowsAPICodePack-Shell,它是前面命令的一个包装器。话说到此为止,这里是代码:
// GUID taken from https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
var FOLDERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FOLDERID_AppsFolder);
foreach (var app in (IKnownFolder)appsFolder)
{
// The friendly app name
string name = app.Name;
// The ParsingName property is the AppUserModelID
string appUserModelID = app.ParsingName; // or app.Properties.System.AppUserModel.ID
// You can even get the Jumbo icon in one shot
ImageSource icon = app.Thumbnail.ExtraLargeBitmapSource;
}
就是这样了。您还可以使用以下方式启动应用程序
System.Diagnostics.Process.Start("explorer.exe", @" shell:appsFolder\" + appModelUserID);
这适用于常规的Win32应用程序和UWP商店应用程序。这些苹果怎么样。
既然您有兴趣列出所有已安装的应用程序,那么您可能还希望监视新应用程序或已卸载的应用程序,您可以使用ShellObjectWatcher
实现:
ShellObjectWatcher sow = new ShellObjectWatcher(appsFolder, false);
sow.AllEvents += (s, e) => DoWhatever();
sow.Start();
编辑:有人可能会对知道上面提到的AppUserModelID感兴趣,它是 Windows用来分组任务栏窗口的唯一标识符.
2022: 在Windows 11中测试,仍然运行良好。 Windows 11似乎也缓存了并非直接安装的应用程序,例如不需要安装的便携式应用程序。 它们会出现在开始菜单的搜索结果中,还可以从shell:appsFolder
检索。
我认为枚举注册表键是最好的方法。
注意,提供的键 @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
将列出32位Windows安装中的所有应用程序以及64位Windows安装中的64位应用程序。
为了查看在Windows 64位安装上安装的32位应用程序,您还需要枚举键@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
。
regedit
中看起来是这样的。然而,在一个32位程序(在64位Windows上)中,两个列表与 regedit
中的 WOW6432Node
列表相同。 - Meow Cat 2012public void GetInstalledApps()
{
string uninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(uninstallKey))
{
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
lstInstalled.Items.Add(sk.GetValue("DisplayName"));
}
catch (Exception ex)
{ }
}
}
}
}
虽然已经有接受的解决方案可用,但它并不完整。远远不够。
如果您想获取所有密钥,则需要考虑两个因素:
x86和x64应用程序无法访问相同的注册表。 基本上,x86无法正常访问x64注册表。而某些 应用程序仅向x64注册表注册。
和
某些应用程序实际上会安装到CurrentUser注册表中,而不是LocalMachine
考虑到这一点,我使用以下代码成功获取了没有使用WMI的所有已安装应用程序
以下是代码:
List<string> installs = new List<string>();
List<string> keys = new List<string>() {
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
};
// The RegistryView.Registry64 forces the application to open the registry as x64 even if the application is compiled as x86
FindInstalls(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64), keys, installs);
FindInstalls(RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64), keys, installs);
installs = installs.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList();
installs.Sort(); // The list of ALL installed applications
private void FindInstalls(RegistryKey regKey, List<string> keys, List<string> installed)
{
foreach (string key in keys)
{
using (RegistryKey rk = regKey.OpenSubKey(key))
{
if (rk == null)
{
continue;
}
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
installed.Add(Convert.ToString(sk.GetValue("DisplayName")));
}
catch (Exception ex)
{ }
}
}
}
}
}
string[] registryKeys = new string[] {
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" };
public class InstalledApp
{
public string DisplayName { get; set; }
public string DisplayIcon { get; set; }
public string Version { get; set; }
public string InstallLocation { get; set; }
}
private void AddInstalledAppToResultView(RegistryHive hive, RegistryView view, string registryKey,Dictionary<string,InstalledApp> resultView)
{
using (var key = RegistryKey.OpenBaseKey(hive, view).OpenSubKey(registryKey))
{
foreach (string subKeyName in key.GetSubKeyNames())
{
using (RegistryKey subkey = key.OpenSubKey(subKeyName))
{
var displayName = subkey.GetValue("DisplayName");
var displayIcon = subkey.GetValue("DisplayIcon");
if (displayName == null || displayIcon == null)
continue;
var app = new InstalledApp
{
DisplayName = (string)displayName,
DisplayIcon = (string)displayIcon,
InstallLocation = (string)subkey.GetValue("InstallLocation"),
Version = (string)subkey.GetValue("DisplayVersion")
};
if(!resultView.ContainsKey(app.DisplayName))
{
resultView.Add(app.DisplayName,app);
}
}
}
}
}
void Main()
{
var result = new Dictionary<string,InstalledApp>();
var view = Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32;
AddInstalledAppToResultView(RegistryHive.LocalMachine, view, registryKeys[0],result);
AddInstalledAppToResultView(RegistryHive.CurrentUser, view, registryKeys[0],result);
AddInstalledAppToResultView(RegistryHive.LocalMachine, RegistryView.Registry64, registryKeys[1],result);
Console.WriteLine("==============" + result.Count + "=================");
result.Values.ToList().ForEach(item => Console.WriteLine(item));
}
使用Windows Installer API!
它允许可靠地枚举所有程序。注册表不可靠,但WMI太重量级。
遍历"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"键并检查它们的"DisplayName"值。
正如其他人指出的那样,被接受的答案并没有返回x86和x64的安装。下面是我的解决方案。它创建了一个StringBuilder
,将注册表值附加到其中(带有格式),并将其输出写入文本文件:
const string FORMAT = "{0,-100} {1,-20} {2,-30} {3,-8}\n";
private void LogInstalledSoftware()
{
var line = string.Format(FORMAT, "DisplayName", "Version", "Publisher", "InstallDate");
line += string.Format(FORMAT, "-----------", "-------", "---------", "-----------");
var sb = new StringBuilder(line, 100000);
ReadRegistryUninstall(ref sb, RegistryView.Registry32);
sb.Append($"\n[64 bit section]\n\n{line}");
ReadRegistryUninstall(ref sb, RegistryView.Registry64);
File.WriteAllText(@"c:\temp\log.txt", sb.ToString());
}
private static void ReadRegistryUninstall(ref StringBuilder sb, RegistryView view)
{
const string REGISTRY_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view);
using var subKey = baseKey.OpenSubKey(REGISTRY_KEY);
foreach (string subkey_name in subKey.GetSubKeyNames())
{
using RegistryKey key = subKey.OpenSubKey(subkey_name);
if (!string.IsNullOrEmpty(key.GetValue("DisplayName") as string))
{
var line = string.Format(FORMAT,
key.GetValue("DisplayName"),
key.GetValue("DisplayVersion"),
key.GetValue("Publisher"),
key.GetValue("InstallDate"));
sb.Append(line);
}
key.Close();
}
subKey.Close();
baseKey.Close();
}