С#: Как получить установку программ точно так же, как в программах и функциях панели управления?

Я прочитал много информации о получении программ. Ни один из алгоритмов не сделал то, что я хочу. Мне нужно установить установленные программы точно так же, как на панели управления.

Итак, я использовал:

  • Класс WMI Win32_Product. Он показывает только установленные программы msi.
  • Ключи реестра. HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall. Опять же, некоторые программы не отображаются на панели управления, некоторые программы отображаются на панели управления не в этом реестре node.

Итак, есть ли кто-нибудь в этом мире, кто знал, какой алгоритм использует панель управления для отображения установленных программ?

UPD1: да, я использую 64-битный, я знаю, что есть еще один node для 64-битных установленных программ "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall", но следующий код перечисляет твиз HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall, странно...

var programs = new List();
    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))
            {
                var name = (string)subkey.GetValue("DisplayName");
                if(!string.IsNullOrEmpty(name))
                {
                    programs.Add(name);
                }
            }
        }
    }

registry_key = @"SOFTWARE\Wow6432Node\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))
        {
            var name = (string)subkey.GetValue("DisplayName");
            if (!string.IsNullOrEmpty(name))
            {
                programs.Add(name);
            }
        }
    }
}

foreach (var program in programs.OrderBy(x => x))
{
    Console.WriteLine(program);
}

Ответы

Ответ 1

Ok gyus, я написал класс, который может устанавливать установленные программы из реестра без исправлений и обновлений. Он по-прежнему не точно, как на панели управления, но почти. Надеюсь, это поможет кому угодно.

public static class InstalledPrograms
{
    const string registry_key = @"SOFTWARE\Microsoft\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);
    }
}

Ответ 2

Я взял код, который написал Мельников (что было очень полезно) и добавил пару вещей. Во-первых, он ищет четыре места в реестре:

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.() + -].

Я только начинаю с С#, поэтому я не знал, как пройти через все четыре регистры, поэтому у меня есть два цикла (один для 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
}

Ответ 3

Ответ MelnikovI достаточен для большинства целей - у меня было 144 элемента в моем списке по сравнению с 143 в программах и функциях. Для обзора его решение заключается в удалении по этим разделам реестра:

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

Чтобы получить квалификацию, каждый подраздел должен иметь:

  • Значение Display_NAME REG_SZ

И НЕ ДОЛЖЕН иметь:

  • SystemComponent REG_DWORD (отличное от нуля)
  • Значения ParentKeyName или ParentDisplayName REG_SZ
  • Значение Release_Type REG_SZ

одно дополнительное усовершенствование, которое я нашел для записей установщика Windows, определяемых как:

  • Имя ключа - это стандартная строка GUID
  • WindowsInstaller REG_DWORD присутствует (и не равен нулю)

Для таких записей вы можете воспользоваться дополнительным шагом использования функции Win32 MsiGetProductInfoW из msi.dll и запросить свойство "VersionString" для представленного GUID по ключу.

Если эта функция возвращает 1605: ERROR_UNKNOWN_PRODUCT, это означает, что запись не установлена ​​в соответствии с установщиком Windows и должна быть исключена из отображения.

После выполнения этой незначительной настройки, мой список теперь идентичен программам и функциям.

Ответ 4

Раздел реестра SystemComponent, обсуждаемый здесь в нескольких других ответах, обычно представляет собой REG_DWORD с возможными значениями 0 или 1. Однако я видел несколько примеров (например, Microsoft Visio 2010 и Microsoft Project 2010), где SystemComponent представляет собой REG_SZ без данных, Поэтому любое решение, которое приводит SystemComponent к int, может вызвать исключение в этих ситуациях.