C#: Как получить установку программ, точно так же, как в программах и функциях панели управления?
Я прочитал много информации о получении программ. Ни один из алгоритмов не сделал то, что я хочу. Мне нужно получить установленные программы точно так же, как в панели управления.
Поэтому я использовал:
- WMI
Win32_Product
учебный класс. Он показывает только MSI установленных программ. - Ключи реестра.
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, может вызвать исключение в этих ситуациях.