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

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

Поэтому я использовал:

  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 Programs = новый список ();
    string registry_key = @"ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ \Microsoft\Windows\CurrentVersion\Uninstall";
    использование (Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
    {
        foreach (строка subkey_name в key.GetSubKeyNames())
        {
            используя (RegistryKey subkey = key.OpenSubKey(subkey_name))
            {
                var name = (строка)subkey.GetValue("DisplayName");
                если (!string.IsNullOrEmpty(имя))
                {
                    programs.Add(имя);
                }
            }
        }
    }

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

3 ответа

Решение

Хорошо, Гюс, я написал класс, который может получить установленные программы из реестра без исправлений и обновлений. Это все еще не совсем как на панели управления, но почти. Я надеюсь, что это помогает кому-то еще.

открытый статический класс InstalledPrograms
{
    const string registry_key = @"ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ \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); } }

Ответ Мельникова достаточен для большинства целей - в моем списке было 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, определяемых как:

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

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

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

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

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

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

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

Другие вопросы по тегам