获取系统中已安装的应用程序


Answers:


115

遍历注册表项“ 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组件和更新,这可能对您很方便。


27
值得注意的是,如果计划重复运行此查询,则使用WMI Win32_Product类是一个坏主意。请参阅此Microsoft KB文章:support.microsoft.com/kb/974524/EN-US 核心问题是(a)Win32_Product确实很慢,并且(b)生成“ Windows Installer重新配置了产品”。每次运行查询时,系统上每个已安装产品的事件日志消息。h!本文建议使用Win32reg_AddRemovePrograms类...,除非您已安装SMS,否则该类不存在。h!因此,最好坚持使用注册表查询。
Simon Gillbee 2010年

Simon Gillbee的评论应该是被接受的答案,或者是Kirtans!WMI WIN32_Product不在这里,相信我!
2010年

13
嗯,这就是为什么注册表示例是我的答案中第一个的原因。WMI只是作为替代解决方案而提出的,即使在那儿,我也指出“执行起来相当慢”和其他缺点。从头开始阅读答案。;)
小夫

1
有点奇怪,但是如果您卸载程序并重新安装它,然后尝试使用注册表项找到它,除非您重新启动计算机,否则您将无法找到它
2015年

3
要回答我自己的问题:stackoverflow.com/questions/27838798/… 尽管很烦人,您可能必须同时查询64位和32位。
罗伯特·科恩克

9

您可以看一下这篇文章。它利用注册表来读取已安装应用程序的列表。

public 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)
                { }
            }
        }
    }
}

我不需要整个列表,我只需要一些选定的安装程序,那么我该怎么做。谢谢
Dhru'soni 2015年

8

我同意通过注册表项枚举是最好的方法。

但是请注意,给定的密钥@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"将列出32位Windows安装中的所有应用程序,以及Windows 64位安装中的64位应用程序。

为了还可以在Windows 64位安装上查看安装的32位应用程序,还需要枚举key @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"


你确定吗?在我的Windows 10 Enterprise 64位上,两个列表看起来很相似,并且x86应用程序都显示在两个列表中。
Florian Straub

谢谢,这对我有用,我找到了正在搜索的程序。
Xtian11

regedit这看来。在32位的程序(在64位Windows),然而,这两个列表是相同的 WOW6432Node从一个regedit
喵喵猫2012年

5

我希望能够像在开始菜单中一样提取应用程序列表。使用注册表,我得到的条目没有显示在开始菜单中。

我还想找到exe路径并提取一个图标,最终使它看起来很不错。不幸的是,对于注册表方法,这是一种偶然,因为我的观察是无法可靠地获得此信息。

我的替代方案基于shell:AppsFolder,您可以通过运行explorer.exe shell:appsFolder该程序进行访问,并列出当前安装的所有应用程序,包括商店应用程序,这些应用程序可通过开始菜单使用。问题在于,这是一个虚拟文件夹,无法使用进行访问System.IO.Directory。相反,您将必须使用本机shell32命令。幸运的是,Microsoft在Nuget上发布了Microsoft.WindowsAPICodePack-Shell,它是上述命令的包装。够了,这里是代码:

// GUID taken from https://docs.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();

编辑:您可能还想知道上面提到的AppUserMoedlID是Windows用于在任务栏中将窗口分组唯一ID


非常感谢,这是实现此目标的一种非常好的方法。您是否知道是否可以直接从ShellObjectWatcher获取名称,解析名称?
forlayo

除了其他事件类型,AllEvents例如ItemCreatedItemRenamed我尝试使用它们来跟踪应用程序的安装或删除情况。这些事件的事件args包含一个Path属性,但至少在我的测试中,此属性始终为null。我一直在试图弄清楚如何从中获取解析名称,因为它始终为null。取而代之的是,我只是保留了一个应用程序列表,只要在项目出现时通过迭代文件夹中的应用程序就可以同步这些应用程序。不理想,但可以完成工作。
user1969903

1
谢谢!我实际上也在做;这也帮助过我关于“如何发现已刚刚安装的应用程序的主执行程序”等quesion - > stackoverflow.com/questions/60440044/... 然后感谢您的!:)
forlayo

4

值得注意的是Win32_Product WMI类代表Windows Installer安装的产品。并非每个应用程序都使用Windows Installer

但是,“ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall”表示32位的应用程序。对于64位,您还需要遍历“ HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall”,并且由于并非每个软件都具有64位版本,因此安装的总应用程序是两个位置都具有“ UninstallString”的键的并集重视他们。

但是最好的选择保持不变。遍历注册表项是一种更好的方法,因为每个应用程序都在注册表中有一个条目(包括Windows Installer中的条目)。但是,注册表方法是不安全的,好像有人删除了相应的项,那么您将不知道相反,更改HKEY_Classes_ROOT \ Installers则比较棘手,因为它与许可问题(例如Microsoft Office或其他产品)相关联。要获得更强大的解决方案,您始终可以将注册表替代方案与WMI结合使用。


3

虽然公认的解决方案有效,但是它并不完整。到目前为止。

如果要获取所有密钥,则需要再考虑两件事:

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)
          { }
        }
      }
    }
  }
}

这比其他解决方案更准确。
Vilas Joshi

1

遍历“ HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall”键,然后检查其“ DisplayName”值。


1

使用Windows Installer API!

它允许对所有程序进行可靠的枚举。注册表不可靠,但是WMI很重要。


当然是沉重的-如果反复运行,将会发现性能像沉重的一样下降。如果我的应用程序功能依赖于另一个应用程序,并且知道是否正确安装,则仅当该应用程序也以64位可用时,我才需要32或64的卸载注册表项),另一方面,如果我必须使用wmi,我通过智能属性技巧,在应用程序中将只能使用一次。
gg89 2015年


1

列表的对象:

public class InstalledProgram
{
    public string DisplayName { get; set; }
    public string Version { get; set; }
    public string InstalledDate { get; set; }
    public string Publisher { get; set; }
    public string UnninstallCommand { get; set; }
    public string ModifyPath { get; set; }
}

创建列表的调用:

    List<InstalledProgram> installedprograms = new List<InstalledProgram>();
    string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
    {
        foreach (string subkey_name in key.GetSubKeyNames())
        {
            using (RegistryKey subkey = key.OpenSubKey(subkey_name))
            {
                if (subkey.GetValue("DisplayName") != null)
                {
                    installedprograms.Add(new InstalledProgram
                    {
                        DisplayName = (string)subkey.GetValue("DisplayName"),
                        Version = (string)subkey.GetValue("DisplayVersion"),
                        InstalledDate = (string)subkey.GetValue("InstallDate"),
                        Publisher = (string)subkey.GetValue("Publisher"),
                        UnninstallCommand = (string)subkey.GetValue("UninstallString"),
                        ModifyPath = (string)subkey.GetValue("ModifyPath")
                    });
                }
            }
        }
    }

1

正如其他人指出的那样,可接受的答案不会同时返回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();
    }


0

可能我建议您看看WMI(Windows Management Instrumentation)。如果将System.Management引用添加到C#项目,则可以访问类“ ManagementObjectSearcher”,您可能会发现它很有用。

对于已安装的应用程序,有各种WMI类,但是如果它是与Windows Installer一起安装的,则Win32_Product类可能最适合您。

ManagementObjectSearcher s = new ManagementObjectSearcher("SELECT * FROM Win32_Product");

0

我使用了Nicks方法-我需要检查是否安装了Visual Studio的远程工具,这似乎有点慢,但是在单独的线程中,这对我来说很好。-这是我的扩展代码:

    private bool isRdInstalled() {
        ManagementObjectSearcher p = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
        foreach (ManagementObject program in p.Get()) {
            if (program != null && program.GetPropertyValue("Name") != null && program.GetPropertyValue("Name").ToString().Contains("Microsoft Visual Studio 2012 Remote Debugger")) {
                return true;
            }
            if (program != null && program.GetPropertyValue("Name") != null) {
                Trace.WriteLine(program.GetPropertyValue("Name"));
            }
        }
        return false;
    }

0

我的要求是检查系统中是否安装了特定软件。此解决方案按预期方式工作。它可能会帮助您。我在Visual Studio 2015的c#中使用了Windows应用程序。

 private void Form1_Load(object sender, EventArgs e)
        {

            object line;
            string softwareinstallpath = string.Empty;
            string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
            using (var baseKey = Microsoft.Win32.RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
            {
                using (var key = baseKey.OpenSubKey(registry_key))
                {
                    foreach (string subkey_name in key.GetSubKeyNames())
                    {
                        using (var subKey = key.OpenSubKey(subkey_name))
                        {
                            line = subKey.GetValue("DisplayName");
                            if (line != null && (line.ToString().ToUpper().Contains("SPARK")))
                            {

                                softwareinstallpath = subKey.GetValue("InstallLocation").ToString();
                                listBox1.Items.Add(subKey.GetValue("InstallLocation"));
                                break;
                            }
                        }
                    }
                }
            }

            if(softwareinstallpath.Equals(string.Empty))
            {
                MessageBox.Show("The Mirth connect software not installed in this system.")
            }



            string targetPath = softwareinstallpath + @"\custom-lib\";
            string[] files = System.IO.Directory.GetFiles(@"D:\BaseFiles");

            // Copy the files and overwrite destination files if they already exist. 
            foreach (var item in files)
            {
                string srcfilepath = item;
                string fileName = System.IO.Path.GetFileName(item);
                System.IO.File.Copy(srcfilepath, targetPath + fileName, true);
            }
            return;

        }

foreach(key.GetSubKeyNames()中的字符串subkey_name)<-不检查此处是否为null。
Burgo855 '18

由于这个原因,它不应该被否决。虽然是重复的。
猫猫2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.