Pobierz zainstalowane aplikacje w systemie

83

Jak zainstalować aplikacje w systemie za pomocą kodu C #?

Sauron
źródło

Odpowiedzi:

116

Wydaje się, że iteracja klucza rejestru „SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall” daje pełną listę zainstalowanych aplikacji.

Oprócz poniższego przykładu możesz znaleźć wersję podobną do tego, co zrobiłem tutaj .

To jest przybliżony przykład, prawdopodobnie będziesz chciał zrobić coś, aby usunąć puste wiersze, jak w drugim podanym łączu.

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))
        {
            Console.WriteLine(subkey.GetValue("DisplayName"));
        }
    }
}

Alternatywnie możesz użyć WMI, jak wspomniano:

ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach(ManagementObject mo in mos.Get())
{
    Console.WriteLine(mo["Name"]);
}

Jest to jednak raczej wolniejsze do wykonania i słyszałem, że może wyświetlać tylko programy zainstalowane w „ALLUSERS”, chociaż może to być niepoprawne. Ignoruje również składniki i aktualizacje systemu Windows, które mogą być przydatne.

Xiaofu
źródło
27
Warto zauważyć, że użycie klasy WMI Win32_Product jest złym pomysłem, jeśli planujesz powtarzać to zapytanie. Zobacz ten artykuł bazy wiedzy Microsoft: support.microsoft.com/kb/974524/EN-US Podstawowym problemem jest to, że (a) Win32_Product działa bardzo wolno i (b) generuje komunikat „Instalator Windows ponownie skonfigurował produkt”. komunikat dziennika zdarzeń dla każdego produktu zainstalowanego w systemie ... za każdym razem, gdy uruchamiasz zapytanie. No! W tym artykule zaleca się użycie klasy Win32reg_AddRemovePrograms ... która nie jest obecna, chyba że zainstalowano program SMS. No! Więc prawdopodobnie lepiej trzymać się zapytania rejestru.
Simon Gillbee,
Komentarz Simona Gillbee powinien być zaakceptowaną odpowiedzią lub Kirtanami! WMI WIN32_Product nie jest właściwą drogą, zaufaj mi!
bdd
13
Właśnie dlatego przykład rejestru jest pierwszy w mojej odpowiedzi. WMI został przedstawiony po prostu jako rozwiązanie alternatywne i nawet tam stwierdzam, że „to jest raczej wolniejsze do wykonania” i inne wady. Przeczytaj odpowiedź od początku. ;)
Xiaofu
1
Trochę dziwne, ale jeśli odinstalujesz program i zainstalujesz go z powrotem, spróbuj go znaleźć za pomocą kluczy rejestru, nie możesz tego zrobić, chyba że ponownie uruchomisz komputer
Yar,
3
Aby odpowiedzieć na moje własne pytanie: stackoverflow.com/questions/27838798/… Chociaż denerwujące może być zapytanie zarówno 64-bitowe, jak i 32-bitowe.
Robert Koernke
9

Możesz rzucić okiem na ten artykuł . Korzysta z rejestru, aby odczytać listę zainstalowanych aplikacji.

public void GetInstalledApps()
{
    string uninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(uninstallKey))
    {
        foreach (string skName in rk.GetSubKeyNames())
        {
            using (RegistryKey sk = rk.OpenSubKey(skName))
            {
                try
                {
                    lstInstalled.Items.Add(sk.GetValue("DisplayName"));
                }
                catch (Exception ex)
                { }
            }
        }
    }
}
Kirtan
źródło
Nie chcę całej listy, potrzebuję tylko wybranych programów instalacyjnych, więc co mogę z tym zrobić. Dziękuję
Dhru 'soni
9

Zgadzam się, że wyliczanie za pomocą klucza rejestru jest najlepszym sposobem.

Należy jednak pamiętać , że podany klucz @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"wyświetli listę wszystkich aplikacji w 32-bitowej instalacji systemu Windows i 64-bitowych w 64-bitowej instalacji systemu Windows.

Aby zobaczyć również aplikacje 32-bitowe zainstalowane w 64-bitowej instalacji systemu Windows, należałoby również wyliczyć klucz @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall".

Stephen Walter
źródło
Jesteś tego pewien? Na moim Windows 10 Enterprise 64bit obie listy wyglądają podobnie, a aplikacje x86 pojawiają się na obu.
Florian Straub
Dziękuję, to działa dla mnie, znalazłem program, którego szukałem.
Xtian11
Na regeditto wygląda. Jednak w programie 32-bitowym (w 64-bitowym systemie Windows) obie listy są identyczne z listami WOW6432Nodez regedit.
Meow Cat 2012,
6

Chciałem mieć możliwość wyodrębnienia listy aplikacji tak, jak pojawiają się w menu Start. Korzystając z rejestru, otrzymywałem wpisy, które nie pojawiają się w menu Start.

Chciałem też znaleźć ścieżkę exe i wyodrębnić ikonę, aby ostatecznie stworzyć ładnie wyglądający program uruchamiający. Niestety, w przypadku metody rejestru jest to trochę chybione, ponieważ z moich obserwacji wynika, że ​​informacje te nie są rzetelnie dostępne.

Moja alternatywa opiera się na powłoce: AppsFolder, do którego można uzyskać dostęp, uruchamiając explorer.exe shell:appsFolder i który zawiera listę wszystkich aplikacji, w tym aplikacji sklepu, aktualnie zainstalowanych i dostępnych za pośrednictwem menu Start. Problem polega na tym, że jest to folder wirtualny, do którego nie można uzyskać dostępu System.IO.Directory. Zamiast tego musiałbyś użyć natywnych poleceń shell32. Na szczęście Microsoft opublikował Microsoft.WindowsAPICodePack-Shell na Nuget, który jest opakowaniem dla wyżej wymienionych poleceń. Wystarczy powiedzieć, oto kod:

// GUID taken from https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid
var FOLDERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FOLDERID_AppsFolder);

foreach (var app in (IKnownFolder)appsFolder)
{
    // The friendly app name
    string name = app.Name;
    // The ParsingName property is the AppUserModelID
    string appUserModelID = app.ParsingName; // or app.Properties.System.AppUserModel.ID
    // You can even get the Jumbo icon in one shot
    ImageSource icon =  app.Thumbnail.ExtraLargeBitmapSource;
}

I to wszystko. Możesz także uruchomić aplikacje za pomocą

System.Diagnostics.Process.Start("explorer.exe", @" shell:appsFolder\" + appModelUserID);

Działa to w przypadku zwykłych aplikacji Win32 i aplikacji ze sklepu UWP. A co z tymi jabłkami?

Ponieważ jesteś zainteresowany wyświetleniem wszystkich zainstalowanych aplikacji, rozsądnie jest oczekiwać, że możesz chcieć monitorować nowe aplikacje lub odinstalowane aplikacje, co możesz zrobić za pomocą ShellObjectWatcher:

ShellObjectWatcher sow = new ShellObjectWatcher(appsFolder, false);
sow.AllEvents += (s, e) => DoWhatever();
sow.Start();

Edycja: Można również chcieć wiedzieć, że wspomniany powyżej AppUserMoedlID jest unikalnym identyfikatorem używanym przez system Windows do grupowania okien na pasku zadań .

user1969903
źródło
Dziękuję bardzo, naprawdę dobry sposób na osiągnięcie tego. Czy wiesz, czy istnieje sposób, aby uzyskać nazwę, przeanalizować nazwę lub coś podobnego bezpośrednio z ShellObjectWatcher?
forlayo
Istnieją inne rodzaje zdarzeń oprócz AllEventstakich jak ItemCreatedlub ItemRenamedktóre Próbowałem za pomocą śledzić aplikacje jak zostały one zainstalowane lub usunięte. Argumenty zdarzeń tych zdarzeń zawierają Pathwłaściwość, ale ta właściwość jest zawsze zerowa, przynajmniej w moich testach. Nie próbowałem dowiedzieć się, jak uzyskać z niej nazwę analizy, ponieważ jest ona zawsze pusta. Zamiast tego po prostu przechowuję listę aplikacji, które synchronizuję za każdym razem, gdy element zostanie podniesiony, iterując po aplikacjach w folderze. Nie jest idealny, ale wykonuje swoją pracę.
user1969903
1
Dzięki! Faktycznie robię to samo; pomogło mi to również w innych pytaniach dotyczących „jak odkryć główny plik wykonywalny aplikacji, która właśnie została zainstalowana” -> stackoverflow.com/questions/60440044/… W takim razie dzięki za to! :)
forlayo
4

Warto zauważyć, że klasa WMI Win32_Product reprezentuje produkty, które są instalowane przez Instalatora Windows . nie każda aplikacja korzysta z instalatora Windows

jednakże „SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall” oznacza aplikacje dla wersji 32-bitowej. W przypadku wersji 64-bitowej należy również przejść przez „HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall”, a ponieważ nie każde oprogramowanie ma wersję 64-bitową, wszystkie zainstalowane aplikacje to suma kluczy w obu lokalizacjach z opcją „UninstallString” Wartość z nimi.

ale najlepsze opcje pozostają takie same. Traverse klucze rejestru są lepszym podejściem, ponieważ każda aplikacja ma wpis w rejestrze [w tym te w Instalatorze Windows]. jednak metoda rejestru jest niebezpieczna, ponieważ ktoś usunie odpowiedni klucz, nie będziesz wiedział Wręcz przeciwnie, zmiana klucza HKEY_Classes_ROOT \ Installers jest trudniejsza, ponieważ wiąże się z kwestiami licencyjnymi, takimi jak pakiet Microsoft Office lub inne produkty. aby uzyskać bardziej niezawodne rozwiązanie, zawsze możesz połączyć alternatywę rejestru z WMI.

Akshita
źródło
3

Chociaż przyjęte rozwiązanie działa, nie jest kompletne. O wiele.

Jeśli chcesz zdobyć wszystkie klucze, musisz wziąć pod uwagę jeszcze 2 rzeczy:

Aplikacje x86 i x64 nie mają dostępu do tego samego rejestru. Zasadniczo x86 nie może normalnie uzyskać dostępu do rejestru x64. Niektóre aplikacje rejestrują się tylko w rejestrze x64.

i

niektóre aplikacje faktycznie instalują się w rejestrze CurrentUser zamiast w LocalMachine

Mając to na uwadze, udało mi się pobrać WSZYSTKIE zainstalowane aplikacje przy użyciu poniższego kodu, BEZ korzystania z WMI

Oto kod:

List<string> installs = new List<string>();
List<string> keys = new List<string>() {
  @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
  @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
};

// The RegistryView.Registry64 forces the application to open the registry as x64 even if the application is compiled as x86 
FindInstalls(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64), keys, installs);
FindInstalls(RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64), keys, installs);

installs = installs.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList();
installs.Sort(); // The list of ALL installed applications



private void FindInstalls(RegistryKey regKey, List<string> keys, List<string> installed)
{
  foreach (string key in keys)
  {
    using (RegistryKey rk = regKey.OpenSubKey(key))
    {
      if (rk == null)
      {
        continue;
      }
      foreach (string skName in rk.GetSubKeyNames())
      {
        using (RegistryKey sk = rk.OpenSubKey(skName))
        {
          try
          {
            installed.Add(Convert.ToString(sk.GetValue("DisplayName")));
          }
          catch (Exception ex)
          { }
        }
      }
    }
  }
}
Pic Mickael
źródło
1

Przejdź przez klucze „HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall” i sprawdź ich wartości „DisplayName”.

Moayad Mardini
źródło
1

Użyj interfejsu API Instalatora Windows!

Pozwala na rzetelne wyliczenie wszystkich programów. Rejestr nie jest wiarygodny, ale WMI jest ciężki.

Brian Cannard
źródło
z pewnością jest ciężki - jeśli biegasz wielokrotnie, zobaczysz spadki wydajności, jak duży ciężar. jeśli funkcja mojej aplikacji zależy od innej aplikacji i wiem, czy została poprawnie zainstalowana, potrzebuję klucza rejestru dezinstalacji dla wersji 32 lub 64 tylko wtedy, gdy aplikacja jest dostępna również w wersji 64-bitowej) z drugiej strony, jeśli muszę używać wmi, ja ograniczy się do użycia tylko raz podczas aplikacji za pomocą sztuczki inteligentnej właściwości.
gg89
1

Obiekt do listy:

public class InstalledProgram
{
    public string DisplayName { get; set; }
    public string Version { get; set; }
    public string InstalledDate { get; set; }
    public string Publisher { get; set; }
    public string UnninstallCommand { get; set; }
    public string ModifyPath { get; set; }
}

Wezwanie do stworzenia listy:

    List<InstalledProgram> installedprograms = new List<InstalledProgram>();
    string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
    {
        foreach (string subkey_name in key.GetSubKeyNames())
        {
            using (RegistryKey subkey = key.OpenSubKey(subkey_name))
            {
                if (subkey.GetValue("DisplayName") != null)
                {
                    installedprograms.Add(new InstalledProgram
                    {
                        DisplayName = (string)subkey.GetValue("DisplayName"),
                        Version = (string)subkey.GetValue("DisplayVersion"),
                        InstalledDate = (string)subkey.GetValue("InstallDate"),
                        Publisher = (string)subkey.GetValue("Publisher"),
                        UnninstallCommand = (string)subkey.GetValue("UninstallString"),
                        ModifyPath = (string)subkey.GetValue("ModifyPath")
                    });
                }
            }
        }
    }
Alexandru-Codrin Panaite
źródło
1

Jak zauważyli inni, zaakceptowana odpowiedź nie zwraca zarówno instalacji x86, jak i x64. Poniżej znajduje się moje rozwiązanie. Tworzy StringBuilder, dołącza do niego wartości rejestru (z formatowaniem) i zapisuje dane wyjściowe do pliku tekstowego:

const string FORMAT = "{0,-100} {1,-20} {2,-30} {3,-8}\n";

private void LogInstalledSoftware()
{
    var line = string.Format(FORMAT, "DisplayName", "Version", "Publisher", "InstallDate");
    line += string.Format(FORMAT, "-----------", "-------", "---------", "-----------");
    var sb = new StringBuilder(line, 100000);
    ReadRegistryUninstall(ref sb, RegistryView.Registry32);
    sb.Append($"\n[64 bit section]\n\n{line}");
    ReadRegistryUninstall(ref sb, RegistryView.Registry64);
    File.WriteAllText(@"c:\temp\log.txt", sb.ToString());
}

   private static void ReadRegistryUninstall(ref StringBuilder sb, RegistryView view)
    {
        const string REGISTRY_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
        using var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view);
        using var subKey = baseKey.OpenSubKey(REGISTRY_KEY);
        foreach (string subkey_name in subKey.GetSubKeyNames())
        {
            using RegistryKey key = subKey.OpenSubKey(subkey_name);
            if (!string.IsNullOrEmpty(key.GetValue("DisplayName") as string))
            {
                var line = string.Format(FORMAT,
                    key.GetValue("DisplayName"),
                    key.GetValue("DisplayVersion"),
                    key.GetValue("Publisher"),
                    key.GetValue("InstallDate"));
                sb.Append(line);
            }
            key.Close();
        }
        subKey.Close();
        baseKey.Close();
    }
Mike Lowery
źródło
0

Mogę zasugerować przyjrzenie się WMI ( Instrumentacja zarządzania Windows ). Jeśli dodasz odwołanie System.Management do projektu C #, uzyskasz dostęp do klasy `ManagementObjectSearcher ', która prawdopodobnie okaże się przydatna.

Istnieją różne klasy WMI dla zainstalowanych aplikacji , ale jeśli została zainstalowana za pomocą Instalatora Windows, to klasa Win32_Product prawdopodobnie najlepiej Ci odpowiada.

ManagementObjectSearcher s = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
Nacięcie
źródło
0

Użyłem podejścia Nicka - musiałem sprawdzić, czy zdalne narzędzia dla Visual Studio są zainstalowane, czy nie, wydaje się to trochę powolne, ale w osobnym wątku jest to dla mnie w porządku. - tutaj mój rozszerzony kod:

    private bool isRdInstalled() {
        ManagementObjectSearcher p = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
        foreach (ManagementObject program in p.Get()) {
            if (program != null && program.GetPropertyValue("Name") != null && program.GetPropertyValue("Name").ToString().Contains("Microsoft Visual Studio 2012 Remote Debugger")) {
                return true;
            }
            if (program != null && program.GetPropertyValue("Name") != null) {
                Trace.WriteLine(program.GetPropertyValue("Name"));
            }
        }
        return false;
    }
Marc Loeb
źródło
0

Moim wymaganiem jest sprawdzenie, czy w moim systemie jest zainstalowane określone oprogramowanie. To rozwiązanie działa zgodnie z oczekiwaniami. To może ci pomóc. Użyłem aplikacji Windows w języku C # z Visual Studio 2015.

 private void Form1_Load(object sender, EventArgs e)
        {

            object line;
            string softwareinstallpath = string.Empty;
            string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
            using (var baseKey = Microsoft.Win32.RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
            {
                using (var key = baseKey.OpenSubKey(registry_key))
                {
                    foreach (string subkey_name in key.GetSubKeyNames())
                    {
                        using (var subKey = key.OpenSubKey(subkey_name))
                        {
                            line = subKey.GetValue("DisplayName");
                            if (line != null && (line.ToString().ToUpper().Contains("SPARK")))
                            {

                                softwareinstallpath = subKey.GetValue("InstallLocation").ToString();
                                listBox1.Items.Add(subKey.GetValue("InstallLocation"));
                                break;
                            }
                        }
                    }
                }
            }

            if(softwareinstallpath.Equals(string.Empty))
            {
                MessageBox.Show("The Mirth connect software not installed in this system.")
            }



            string targetPath = softwareinstallpath + @"\custom-lib\";
            string[] files = System.IO.Directory.GetFiles(@"D:\BaseFiles");

            // Copy the files and overwrite destination files if they already exist. 
            foreach (var item in files)
            {
                string srcfilepath = item;
                string fileName = System.IO.Path.GetFileName(item);
                System.IO.File.Copy(srcfilepath, targetPath + fileName, true);
            }
            return;

        }
Pardha Saradhi Vanjarpau
źródło
foreach (string nazwa_podklucza w key.GetSubKeyNames ()) <- Nie sprawdzaj tutaj, jeśli null.
Burgo855
Z TEGO powodu nie zasługuje na głosowanie negatywne. Chociaż jest zduplikowany.
Meow Cat 2012,