【问题标题】:C#: How to get installing programs exactly like in control panel programs and features?C#:如何在控制面板程序和功能中安装程序?
【发布时间】:2021-07-09 02:55:04
【问题描述】:

我阅读了很多获取程序的信息。没有一种算法能做到我想要的。我需要像在控制面板中一样完全安装程序。

所以我用了:

  1. WMI Win32_Product 类。它仅显示 msi 安装的程序。
  2. 注册表项。 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall。同样,控制面板中不显示某些程序,控制面板中显示的某些程序不在此注册表节点中。

那么,这个世界上有没有人知道哪个算法使用控制面板来显示已安装的程序?

UPD1:是的,我使用 64 位,我知道还有另一个节点用于 64 位安装程序“HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall”,但以下代码枚举了两次 HKLM\SOFTWARE\Wow6432Node\Microsoft \Windows\CurrentVersion\Uninstall 部分,奇怪...

var 程序 = 新列表(); string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; 使用(Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key)) { foreach(key.GetSubKeyNames() 中的字符串 subkey_name) { 使用 (RegistryKey subkey = key.OpenSubKey(subkey_name)) { var name = (string)subkey.GetValue("DisplayName"); if(!string.IsNullOrEmpty(name)) { 程序。添加(名称); } } } } registry_key = @"软件\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"; 使用(Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key)) { foreach(key.GetSubKeyNames() 中的字符串 subkey_name) { 使用 (RegistryKey subkey = key.OpenSubKey(subkey_name)) { var name = (string)subkey.GetValue("DisplayName"); if (!string.IsNullOrEmpty(name)) { 程序。添加(名称); } } } } foreach(programs.OrderBy(x => x)中的var程序) { Console.WriteLine(程序); }

【问题讨论】:

  • HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall 应该这样做。您能否举一个出现在控制面板中但不在此 reg 键中的程序示例?
  • 您使用的是 64 位机器吗?
  • 我更新了主题,以下代码没有显示我在 SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall 节点中的 winrar 程序
  • 您运行的是 32 位进程吗?如果是这样,您需要使用 RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) 而不是 Registry.LocalMachine 来从 64 位密钥中获取条目。
  • @RichardDeeming,太棒了!但是 displayig windows 更新和修补程序仍然存在问题。我认为有一些关键定义该程序是更新公关修补程序。有什么想法吗?

标签: c# .net windows registry controlpanel


【解决方案1】:

Ok gyus,我编写的类可以从注册表中获取已安装的程序,而无需修补程序和更新。它仍然不完全像在控制面板中,但几乎。我希望这对其他人有帮助。

公共静态类 InstalledPrograms { 常量字符串registry_key = @"软件\微软\Windows\CurrentVersion\Uninstall"; public static List<string> GetInstalledPrograms() { var result = new List<string>(); result.AddRange(GetInstalledProgramsFromRegistry(RegistryView.Registry32)); result.AddRange(GetInstalledProgramsFromRegistry(RegistryView.Registry64)); return result; } private static IEnumerable<string> GetInstalledProgramsFromRegistry(RegistryView registryView) { var result = new List<string>(); using (RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView).OpenSubKey(registry_key)) { foreach (string subkey_name in key.GetSubKeyNames()) { using (RegistryKey subkey = key.OpenSubKey(subkey_name)) { if(IsProgramVisible(subkey)) { result.Add((string)subkey.GetValue("DisplayName")); } } } } return result; } private static bool IsProgramVisible(RegistryKey subkey) { var name = (string)subkey.GetValue("DisplayName"); var releaseType = (string)subkey.GetValue("ReleaseType"); //var unistallString = (string)subkey.GetValue("UninstallString"); var systemComponent = subkey.GetValue("SystemComponent"); var parentName = (string)subkey.GetValue("ParentDisplayName"); return !string.IsNullOrEmpty(name) && string.IsNullOrEmpty(releaseType) && string.IsNullOrEmpty(parentName) && (systemComponent == null); } }

【讨论】:

  • 非常优雅的课程。对我来说,它提供了一个额外的 Autodesk 条目,并错过了程序和功能中显示的 8 个 nvidia 条目中的 6 个。如果我知道为什么我会在这里发帖。
  • 我发现如果我将方法 IsProgramVisible(RegistryKey subkey) 的最后一行从: && (systemComponent == null) 更改为: && (systemComponent == null || (int)systemComponent == 0 );那么它和我在功能中看到的程序完美匹配,也就是说,我现在可以看到 NVIDIA 驱动程序和功能条目。
  • 感谢您发布有用的代码。确定已安装程序列表后,注册表项与发现已安装 exe 文件的确切名称之间的程序链接是什么?我浏览了在几种情况下看到的所有字段,但只能找到安装目录和卸载 exe 文件的名称(有时)。我知道我可以在应用程序目录中查找 exe 文件,但这与将官方信息跟踪到启动的 exe 不同。谢谢
【解决方案2】:

MelnikovI 的答案对于大多数用途来说已经足够了——我的列表中有 144 项,而程序和功能中有 143 项。 供审查,他的解决方案是点击以下注册表位置:

  • HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall
  • HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall
  • HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

要获得资格,每个子项必须具有:

  • DisplayName REG_SZ 值

并且不得有:

  • SystemComponent REG_DWORD(非零)
  • ParentKeyName 或 ParentDisplayName REG_SZ 值
  • ReleaseType REG_SZ 值

我发现的一个附加增强是针对 Windows Installer 条目的,定义为:

  • 键名是标准的 GUID 字符串
  • WindowsInstaller REG_DWORD 存在(且非零)

对于此类条目,您可以采取额外的步骤,使用 msi.dll 中的 Win32 函数 MsiGetProductInfoW,并为表示的 GUID 请求“VersionString”属性关键。

如果此函数返回1605: ERROR_UNKNOWN_PRODUCT,则表示该条目没有按照Windows Installer安装,应该从显示中排除。

在实施这个小调整后,我的列表现在与程序和功能相同。

【讨论】:

    【解决方案3】:

    我采用了 MelnikovI 编写的代码(非常有用)并添加了一些东西。首先,它在注册表中搜索四个地方:

    HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

    HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

    HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

    HKCU\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

    它还会检查是否有任何子键 - 如果没有,它会跳过那个。

    最后,它做了一个正则表达式,只允许一组特定的字符 [^a-zA-Z0-9 .()+-]。

    我只是从 C# 开始,所以我不知道循环遍历所有四个 reg 位置的方法,所以我有两个循环(一个用于 HKLM,一个用于 HKCU)。

    public static class InstalledPrograms
        {
          public static List<string> GetInstalledPrograms()
            {
                var result = new List<string>();
                result.AddRange(GetInstalledProgramsFromRegistry(RegistryView.Registry32));
                result.AddRange(GetInstalledProgramsFromRegistry(RegistryView.Registry64));
                result.Sort();
                return result;
            }
            private static string cleanText(string dirtyText)
            {
                Regex rgx = new Regex("[^a-zA-Z0-9 .()+-]");
                string result = rgx.Replace(dirtyText, "");
                return result;
            }
            private static IEnumerable<string> GetInstalledProgramsFromRegistry(RegistryView registryView)
            {
                var result = new List<string>();
                List<string> uninstall = new List<string>();
                uninstall.Add(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
                uninstall.Add(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");
                foreach (string registry_key in uninstall)
                {
                   using (RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView).OpenSubKey(registry_key))
                   {
                        foreach (string subkey_name in key.GetSubKeyNames())
                        {
                            using (RegistryKey subkey = key.OpenSubKey(subkey_name))
                            {
                                if (IsProgramVisible(subkey))
                                {
                                    result.Add(cleanText(subkey.GetValue("DisplayName").ToString()).ToString());
                                }
                            }
                        }
                    }
                    using (RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, registryView).OpenSubKey(registry_key))
                    {
                        if (key != null)
                        {
                            foreach (string subkey_name in key.GetSubKeyNames())
                            {
                                using (RegistryKey subkey = key.OpenSubKey(subkey_name))
                                {
                                    if (IsProgramVisible(subkey))
                                    {
                                        result.Add(cleanText(subkey.GetValue("DisplayName").ToString()).ToString());
                                    }
                                }
                            }
                        }
                    }
                }
    
                return result;
            }
    

    如果有人感兴趣,我将结果与我一直使用的 PowerShell 进行了比较,它们是相同的。

    ##Get list of Add/Remove programs
    if (!([Diagnostics.Process]::GetCurrentProcess().Path -match '\\syswow64\\'))
    {
    $uninstallPath = "\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
    $uninstallWow6432Path = "\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
    @(
    if (Test-Path "HKLM:$uninstallWow6432Path" ) { Get-ChildItem "HKLM:$uninstallWow6432Path"}
    if (Test-Path "HKLM:$uninstallPath" ) { Get-ChildItem "HKLM:$uninstallPath" }
    if (Test-Path "HKCU:$uninstallWow6432Path") { Get-ChildItem "HKCU:$uninstallWow6432Path"}
    if (Test-Path "HKCU:$uninstallPath" ) { Get-ChildItem "HKCU:$uninstallPath" }
    ) |
    ForEach-Object { Get-ItemProperty $_.PSPath } |
    Where-Object {
    $_.DisplayName -and !$_.SystemComponent -and !$_.ReleaseType -and !$_.ParentKeyName -and ($_.UninstallString -or $_.NoRemove)
    } |
    Sort-Object DisplayName |
    Select-Object DisplayName
    }
    else
    {
    "You are running 32-bit Powershell on 64-bit system. Please run 64-bit Powershell instead." | Write-Host -ForegroundColor Red
    }
    

    【讨论】:

    • 您在 rigt 轨道上,但您应该查看 RegistryView,因为在 64 位系统上使用 wow6432Node 路径不会返回您认为的结果。我发现,如果您将 SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall 之类的路径与 Registry32 一起使用,它将返回 SOFTWARE\Wowo6432Node\Microsoft\Windows\CurrentVersion\Uninstall 并且当您使用与 Registry64 完全相同的路径时,它将返回 SOFTWARE\Microsoft\Windows \CurrentVersion\卸载
    【解决方案4】:

    此处在其他几个答案中讨论的 SystemComponent 注册表项通常是一个可能值为 0 或 1 的 REG_DWORD。但是,我已经看到了几个实例(例如 Microsoft Visio 2010 和 Microsoft Project 2010),其中 SystemComponent 是没有数据的 REG_SZ。因此,任何将 SystemComponent 转换为 int 的解决方案都可能在这些情况下引发异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-08
      • 1970-01-01
      • 2012-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      • 1970-01-01
      相关资源
      最近更新 更多