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