Benutzer mit den meisten Antworten
Programm nach installierten Programmen suchen lassen

Frage
-
Hallöle!
Ich hätte auch mal wieder eine Frage. Ich möchte ein Programm erstellen, dass die installierten Programme ausliest und dass man diese über eine weitere Form über Buttons starten kann.
Ich habe ja bereits ein Programm erstellt das dass System darauf prüft, welche der vorgegebenen Programme in "\Program Files" existieren... Nur ist mir das zu wenig, denn ich möchte ja nicht nur - sagen wir mal 50 Programme die ich aus dem Stehgreif kenne - überprüfen lassen. Vor allem da nicht jeder diese auf seinem Rechner hat. Also möchste ich die Anwendung etwas Benutzerfreundlicher machen, sodass der Benutzer seine zur Zeit installierten Programme angezeigt bekommt und diese bequem öffnen kann. Natürlich sollte auch eine ProgressBar den Fortschritt anzeigen, aber dass dürfte ich noch mit ach und krach hinbekommen ;)
Hat jemand eine Idee (und Code) wie so etwas funktioniert? Habe jetzt auch schon gesucht, aber ich finde ums verrecken nichts. Vielleicht finde ich hier eine Antwort ;)
Danke im Vorraus!
LG
Dominik
Antworten
-
Hallo Dominik,
was Du Dir da ausgesucht hast, ist alles andere als trivial. Was betrachtet man als installiert?
Windows bietet mehrere Pfade innerhalb der Systemregistrierung an, über die eine Anwendung ihre Präsenz anzeigen kann. Der bekannteste ist sicher die Liste installierter Software unter Systemsteuerung => Software (XP) bzw. Systemsteuerung => Programme & Funktionen (Vista/Windows 7). Der Pfad in der Systemregistrierung "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall" kann mit der Registry Klasse des Framework ausgelesen werden.
Damit bekommt man allerdings im Idealfall wenigstens das Installationsverzeichnis heraus. Dieses wird in dem Wert „InstallLocation“ abgelegt. Aber dieser Wert muss nicht gefüllt sein, noch muss er überhaupt vorhanden sein, selbst wenn die Installation über den Windows Installer (MSI) erfolgt, mit dem dieser Wert eingeführt wurde. Auch das Installationsverzeichnis selber gibt eigentlich nicht genügend Auskunft darüber, welche ausführbare Anwendung sich dahinter verbirgt. Zum Beispiel steht für Office nur ein einziger Eintrag in der Softwareverwaltung, dahinter können sich aber mehrere Anwendungen, wie Word, Excel, PowerPoint und andere verbergen.
In der Annahme, dass in diesem Installationsverzeichnis nur die produktiven Anwendungen zu finden sind, kann man sich alle Dateien auflisten lassen, die auf ".exe" enden. Dabei bekommt man aber oft auch Anwendungsdateien, die für den produktiven Einsatz kaum sinnvoll sind.
Mehr Informationen kann man dagegen aus dem Registrierungsschlüssel "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths" beziehen. Darunter sind bekannte Anwendungen mit ihren Anwendungspfaden aufgelistet.
Seit Windows XP (oder Windows 2000?) gibt es einen erweiterten „Öffnen mit“ Dialog, der ebenfalls eine Auswahl bekannter Anwendungen auflistet. Diese werden entweder ebenfalls von der Anwendung gesetzt oder vom Explorer ergänzt, wenn der Benutzer eine neue Dateizuordnung festlegt. Diese Information ist im Schlüssel "HKEY_CLASSES_ROOT\Applications" hinterlegt. Hier findet man meist eine ähnliche Struktur, wie bei Dateierweiterungen \appname.exe \Shell\verb \Command hinter dem sich die Kommandozeile verbirgt, mit der Dateien an die Anwendung übergeben werden können. Mit der API Funktion AssocQueryString kann man aus dem gefundenen Namen des Executable, den vollständigen Pfad der Anwendung auslesen, so dass man sich mit der variablen Struktur der Shell Dateiregistrierung nicht weiter auseinandersetzen muss.
Application Registration
http://msdn.microsoft.com/en-us/library/ee872121.aspx
Die Klartextbeschreibung für die gefundenen Anwendungsdateien kann über die Versionsinformation ausgelesen werden.
' ApplicationSearcher.vb Imports System.Runtime.InteropServices Imports System.Diagnostics Imports Microsoft.Win32 Public Class ApplicationSearcher Public Function Find() As Dictionary(Of String, ApplicationInfo) Dim lApps As New Dictionary(Of String, ApplicationInfo) FindAppPath(Registry.LocalMachine, lApps) FindAppPath(Registry.CurrentUser, lApps) FindApplications(lApps) FindUninstall(lApps) Return lApps End Function Private Function FindApplications( _ ByVal apps As Dictionary(Of String, ApplicationInfo) _ ) As Integer Dim lAppPaths As RegistryKey Dim lSubKeys() As String Dim lPath As String Dim lCount As Integer lAppPaths = Registry.ClassesRoot.OpenSubKey("Applications", False) lSubKeys = lAppPaths.GetSubKeyNames() For i As Integer = 0 To lSubKeys.Length - 1 If lSubKeys(i).ToLower.EndsWith(".exe") Then lPath = GetPathFromExeName(lSubKeys(i)) If lPath <> "" AndAlso IO.File.Exists(lPath) Then Dim lAppInfo As New ApplicationInfo(lPath) If Not apps.ContainsKey(lAppInfo.ExeName) Then lCount += 1 apps.Add(lAppInfo.ExeName, lAppInfo) End If End If End If Next lAppPaths.Close() Return lCount End Function Private Function GetPathFromExeName(ByVal exeName As String) As String Dim lSb As New System.Text.StringBuilder(260) Dim lResult As UInteger lResult = NativeMethods.AssocQueryString(NativeMethods.AssocF.Open_ByExeName, _ NativeMethods.AssocStr.Executable, _ exeName, _ Nothing, _ lSb, _ lSb.Capacity) If lResult = 0 Then Return lSb.ToString() End If End Function Private Function FindAppPath(ByVal hive As RegistryKey, _ ByVal apps As Dictionary(Of String, ApplicationInfo) _ ) As Integer Dim lAppPaths As RegistryKey Dim lKey As RegistryKey Dim lSubKeys() As String Dim lPath As String Dim lCount As Integer Const AppPathsKey As String = "Software\Microsoft\Windows\CurrentVersion\App Paths" lAppPaths = hive.OpenSubKey(AppPathsKey, False) lSubKeys = lAppPaths.GetSubKeyNames() For i As Integer = 0 To lSubKeys.Length - 1 lKey = lAppPaths.OpenSubKey(lSubKeys(i), False) lPath = lKey.GetValue(Nothing, "").ToString lKey.Close() If lPath <> "" AndAlso IO.File.Exists(lPath) Then Dim lAppInfo As New ApplicationInfo(lPath) If Not apps.ContainsKey(lAppInfo.ExeName) Then lCount += 1 apps.Add(lAppInfo.ExeName, lAppInfo) End If End If Next lAppPaths.Close() Return lCount End Function Private Function FindUninstall(ByVal apps As Dictionary(Of String, ApplicationInfo) _ ) As Integer Dim lAppPaths As RegistryKey Dim lKey As RegistryKey Dim lSubKeys() As String Dim lExeNames() As String Dim lInstallLocation As String Dim lCount As Integer Const UninstallKey As String = "Software\Microsoft\Windows\CurrentVersion\Uninstall" lAppPaths = Registry.LocalMachine.OpenSubKey(UninstallKey, False) lSubKeys = lAppPaths.GetSubKeyNames() For i As Integer = 0 To lSubKeys.Length - 1 lKey = lAppPaths.OpenSubKey(lSubKeys(i), False) lInstallLocation = lKey.GetValue("InstallLocation", "").ToString() If lInstallLocation <> "" AndAlso IO.Directory.Exists(lInstallLocation) Then lExeNames = IO.Directory.GetFiles(lInstallLocation, "*.exe") For n As Integer = 0 To lExeNames.Length - 1 Dim lAppInfo As New ApplicationInfo(lExeNames(n)) If Not apps.ContainsKey(lAppInfo.ExeName) Then lCount += 1 apps.Add(lAppInfo.ExeName, lAppInfo) End If Next End If lKey.Close() Next lAppPaths.Close() Return lCount End Function End Class Public Class ApplicationInfo Private m_Path As String Private m_ExeName As String Private m_Description As String Private m_FileVersion As Version Private m_ProductName As String Private m_Comments As String Private m_VersionInfoExtracted As Boolean Public Sub New(ByVal path As String) m_Path = IO.Path.GetFullPath(path) m_ExeName = IO.Path.GetFileName(m_Path).ToLower() End Sub Public ReadOnly Property Comments() As String Get If Not m_VersionInfoExtracted Then InitializeInfo() End If Return m_Comments End Get End Property Public ReadOnly Property Description() As String Get If Not m_VersionInfoExtracted Then InitializeInfo() End If Return m_Description End Get End Property Public ReadOnly Property ExeName() As String Get Return m_ExeName End Get End Property Public ReadOnly Property FileVersion() As Version Get If Not m_VersionInfoExtracted Then InitializeInfo() End If Return m_FileVersion End Get End Property Public ReadOnly Property Path() As String Get Return m_Path End Get End Property Public ReadOnly Property ProductName() As String Get If Not m_VersionInfoExtracted Then InitializeInfo() End If Return m_ProductName End Get End Property Private Sub InitializeInfo() Dim lFileVersion As FileVersionInfo lFileVersion = FileVersionInfo.GetVersionInfo(m_Path) m_Description = lFileVersion.FileDescription m_FileVersion = New Version(lFileVersion.FileMajorPart, _ lFileVersion.FileMinorPart, _ lFileVersion.FileBuildPart, _ lFileVersion.FilePrivatePart) m_Comments = lFileVersion.Comments m_ProductName = lFileVersion.ProductName If String.IsNullOrEmpty(m_Description) OrElse m_Description.Trim() = "" Then m_Description = IO.Path.GetFileNameWithoutExtension(m_Path) End If m_VersionInfoExtracted = True End Sub End Class Public Class NativeMethods <Flags()> _ Public Enum AssocF As UInteger Init_NoRemapCLSID = &H1 Init_ByExeName = &H2 Open_ByExeName = &H2 Init_DefaultToStar = &H4 Init_DefaultToFolder = &H8 NoUserSettings = &H10 NoTruncate = &H20 Verify = &H40 RemapRunDll = &H80 NoFixUps = &H100 IgnoreBaseClass = &H200 End Enum Public Enum AssocStr As UInteger Command = 1 Executable FriendlyDocName FriendlyAppName NoOpen ShellNewValue DDECommand DDEIfExec DDEApplication DDETopic End Enum <DllImport("shlwapi.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ Public Shared Function AssocQueryString(ByVal flags As AssocF, _ ByVal str As AssocStr, _ ByVal pszAssoc As String, _ ByVal pszExtra As String, _ ByVal pszOut As System.Text.StringBuilder, _ ByRef pcchOut As Integer _ ) As UInteger End Function End Class ' Form1.vb Public Class Form1 Private Sub ListBox1_DoubleClick(ByVal sender As Object, _ ByVal e As System.EventArgs _ ) Handles ListBox1.DoubleClick If ListBox1.SelectedItem IsNot Nothing Then Process.Start(DirectCast(ListBox1.SelectedItem, _ ApplicationInfo).Path) End If End Sub Private Sub Form2_Load(ByVal sender As Object, _ ByVal e As System.EventArgs _ ) Handles Me.Load Dim lApps As Dictionary(Of String, ApplicationInfo) lApps = (New ApplicationSearcher).Find() ListBox1.DisplayMember = "Description" ListBox1.DataSource = lApps.Values.ToList() End Sub End Class
Bedenke, dass dies nur ein Ansatz, sicher aber keine vollständige Lösung ist. Wie bereits erwähnt, werden damit unter anderem Anwendungen gefunden, die lediglich Helper Apps darstellen. Auch werden sicher nicht alle installierten Anwendungen gefunden, da wie gesagt, die Information über den Installationsort nicht immer ermittelbar ist. Da der Name des Executable als eindeutiges Merkmal hergenommen wird, um mehrfache Treffer der 4 durchsuchten Bereiche zu vermeiden, fallen auch verschiedene Versionen einer Anwendung unter den Tisch. Ggf. wäre hier der Pfad geeigneter.
Thorsten Dörfler
Microsoft MVP Visual Basic- Als Antwort vorgeschlagen Elmar Boye Sonntag, 2. Mai 2010 18:14
- Als Antwort markiert Robert Breitenhofer Freitag, 7. Mai 2010 14:47
Alle Antworten
-
Hallo Dominik,
was Du Dir da ausgesucht hast, ist alles andere als trivial. Was betrachtet man als installiert?
Windows bietet mehrere Pfade innerhalb der Systemregistrierung an, über die eine Anwendung ihre Präsenz anzeigen kann. Der bekannteste ist sicher die Liste installierter Software unter Systemsteuerung => Software (XP) bzw. Systemsteuerung => Programme & Funktionen (Vista/Windows 7). Der Pfad in der Systemregistrierung "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall" kann mit der Registry Klasse des Framework ausgelesen werden.
Damit bekommt man allerdings im Idealfall wenigstens das Installationsverzeichnis heraus. Dieses wird in dem Wert „InstallLocation“ abgelegt. Aber dieser Wert muss nicht gefüllt sein, noch muss er überhaupt vorhanden sein, selbst wenn die Installation über den Windows Installer (MSI) erfolgt, mit dem dieser Wert eingeführt wurde. Auch das Installationsverzeichnis selber gibt eigentlich nicht genügend Auskunft darüber, welche ausführbare Anwendung sich dahinter verbirgt. Zum Beispiel steht für Office nur ein einziger Eintrag in der Softwareverwaltung, dahinter können sich aber mehrere Anwendungen, wie Word, Excel, PowerPoint und andere verbergen.
In der Annahme, dass in diesem Installationsverzeichnis nur die produktiven Anwendungen zu finden sind, kann man sich alle Dateien auflisten lassen, die auf ".exe" enden. Dabei bekommt man aber oft auch Anwendungsdateien, die für den produktiven Einsatz kaum sinnvoll sind.
Mehr Informationen kann man dagegen aus dem Registrierungsschlüssel "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths" beziehen. Darunter sind bekannte Anwendungen mit ihren Anwendungspfaden aufgelistet.
Seit Windows XP (oder Windows 2000?) gibt es einen erweiterten „Öffnen mit“ Dialog, der ebenfalls eine Auswahl bekannter Anwendungen auflistet. Diese werden entweder ebenfalls von der Anwendung gesetzt oder vom Explorer ergänzt, wenn der Benutzer eine neue Dateizuordnung festlegt. Diese Information ist im Schlüssel "HKEY_CLASSES_ROOT\Applications" hinterlegt. Hier findet man meist eine ähnliche Struktur, wie bei Dateierweiterungen \appname.exe \Shell\verb \Command hinter dem sich die Kommandozeile verbirgt, mit der Dateien an die Anwendung übergeben werden können. Mit der API Funktion AssocQueryString kann man aus dem gefundenen Namen des Executable, den vollständigen Pfad der Anwendung auslesen, so dass man sich mit der variablen Struktur der Shell Dateiregistrierung nicht weiter auseinandersetzen muss.
Application Registration
http://msdn.microsoft.com/en-us/library/ee872121.aspx
Die Klartextbeschreibung für die gefundenen Anwendungsdateien kann über die Versionsinformation ausgelesen werden.
' ApplicationSearcher.vb Imports System.Runtime.InteropServices Imports System.Diagnostics Imports Microsoft.Win32 Public Class ApplicationSearcher Public Function Find() As Dictionary(Of String, ApplicationInfo) Dim lApps As New Dictionary(Of String, ApplicationInfo) FindAppPath(Registry.LocalMachine, lApps) FindAppPath(Registry.CurrentUser, lApps) FindApplications(lApps) FindUninstall(lApps) Return lApps End Function Private Function FindApplications( _ ByVal apps As Dictionary(Of String, ApplicationInfo) _ ) As Integer Dim lAppPaths As RegistryKey Dim lSubKeys() As String Dim lPath As String Dim lCount As Integer lAppPaths = Registry.ClassesRoot.OpenSubKey("Applications", False) lSubKeys = lAppPaths.GetSubKeyNames() For i As Integer = 0 To lSubKeys.Length - 1 If lSubKeys(i).ToLower.EndsWith(".exe") Then lPath = GetPathFromExeName(lSubKeys(i)) If lPath <> "" AndAlso IO.File.Exists(lPath) Then Dim lAppInfo As New ApplicationInfo(lPath) If Not apps.ContainsKey(lAppInfo.ExeName) Then lCount += 1 apps.Add(lAppInfo.ExeName, lAppInfo) End If End If End If Next lAppPaths.Close() Return lCount End Function Private Function GetPathFromExeName(ByVal exeName As String) As String Dim lSb As New System.Text.StringBuilder(260) Dim lResult As UInteger lResult = NativeMethods.AssocQueryString(NativeMethods.AssocF.Open_ByExeName, _ NativeMethods.AssocStr.Executable, _ exeName, _ Nothing, _ lSb, _ lSb.Capacity) If lResult = 0 Then Return lSb.ToString() End If End Function Private Function FindAppPath(ByVal hive As RegistryKey, _ ByVal apps As Dictionary(Of String, ApplicationInfo) _ ) As Integer Dim lAppPaths As RegistryKey Dim lKey As RegistryKey Dim lSubKeys() As String Dim lPath As String Dim lCount As Integer Const AppPathsKey As String = "Software\Microsoft\Windows\CurrentVersion\App Paths" lAppPaths = hive.OpenSubKey(AppPathsKey, False) lSubKeys = lAppPaths.GetSubKeyNames() For i As Integer = 0 To lSubKeys.Length - 1 lKey = lAppPaths.OpenSubKey(lSubKeys(i), False) lPath = lKey.GetValue(Nothing, "").ToString lKey.Close() If lPath <> "" AndAlso IO.File.Exists(lPath) Then Dim lAppInfo As New ApplicationInfo(lPath) If Not apps.ContainsKey(lAppInfo.ExeName) Then lCount += 1 apps.Add(lAppInfo.ExeName, lAppInfo) End If End If Next lAppPaths.Close() Return lCount End Function Private Function FindUninstall(ByVal apps As Dictionary(Of String, ApplicationInfo) _ ) As Integer Dim lAppPaths As RegistryKey Dim lKey As RegistryKey Dim lSubKeys() As String Dim lExeNames() As String Dim lInstallLocation As String Dim lCount As Integer Const UninstallKey As String = "Software\Microsoft\Windows\CurrentVersion\Uninstall" lAppPaths = Registry.LocalMachine.OpenSubKey(UninstallKey, False) lSubKeys = lAppPaths.GetSubKeyNames() For i As Integer = 0 To lSubKeys.Length - 1 lKey = lAppPaths.OpenSubKey(lSubKeys(i), False) lInstallLocation = lKey.GetValue("InstallLocation", "").ToString() If lInstallLocation <> "" AndAlso IO.Directory.Exists(lInstallLocation) Then lExeNames = IO.Directory.GetFiles(lInstallLocation, "*.exe") For n As Integer = 0 To lExeNames.Length - 1 Dim lAppInfo As New ApplicationInfo(lExeNames(n)) If Not apps.ContainsKey(lAppInfo.ExeName) Then lCount += 1 apps.Add(lAppInfo.ExeName, lAppInfo) End If Next End If lKey.Close() Next lAppPaths.Close() Return lCount End Function End Class Public Class ApplicationInfo Private m_Path As String Private m_ExeName As String Private m_Description As String Private m_FileVersion As Version Private m_ProductName As String Private m_Comments As String Private m_VersionInfoExtracted As Boolean Public Sub New(ByVal path As String) m_Path = IO.Path.GetFullPath(path) m_ExeName = IO.Path.GetFileName(m_Path).ToLower() End Sub Public ReadOnly Property Comments() As String Get If Not m_VersionInfoExtracted Then InitializeInfo() End If Return m_Comments End Get End Property Public ReadOnly Property Description() As String Get If Not m_VersionInfoExtracted Then InitializeInfo() End If Return m_Description End Get End Property Public ReadOnly Property ExeName() As String Get Return m_ExeName End Get End Property Public ReadOnly Property FileVersion() As Version Get If Not m_VersionInfoExtracted Then InitializeInfo() End If Return m_FileVersion End Get End Property Public ReadOnly Property Path() As String Get Return m_Path End Get End Property Public ReadOnly Property ProductName() As String Get If Not m_VersionInfoExtracted Then InitializeInfo() End If Return m_ProductName End Get End Property Private Sub InitializeInfo() Dim lFileVersion As FileVersionInfo lFileVersion = FileVersionInfo.GetVersionInfo(m_Path) m_Description = lFileVersion.FileDescription m_FileVersion = New Version(lFileVersion.FileMajorPart, _ lFileVersion.FileMinorPart, _ lFileVersion.FileBuildPart, _ lFileVersion.FilePrivatePart) m_Comments = lFileVersion.Comments m_ProductName = lFileVersion.ProductName If String.IsNullOrEmpty(m_Description) OrElse m_Description.Trim() = "" Then m_Description = IO.Path.GetFileNameWithoutExtension(m_Path) End If m_VersionInfoExtracted = True End Sub End Class Public Class NativeMethods <Flags()> _ Public Enum AssocF As UInteger Init_NoRemapCLSID = &H1 Init_ByExeName = &H2 Open_ByExeName = &H2 Init_DefaultToStar = &H4 Init_DefaultToFolder = &H8 NoUserSettings = &H10 NoTruncate = &H20 Verify = &H40 RemapRunDll = &H80 NoFixUps = &H100 IgnoreBaseClass = &H200 End Enum Public Enum AssocStr As UInteger Command = 1 Executable FriendlyDocName FriendlyAppName NoOpen ShellNewValue DDECommand DDEIfExec DDEApplication DDETopic End Enum <DllImport("shlwapi.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ Public Shared Function AssocQueryString(ByVal flags As AssocF, _ ByVal str As AssocStr, _ ByVal pszAssoc As String, _ ByVal pszExtra As String, _ ByVal pszOut As System.Text.StringBuilder, _ ByRef pcchOut As Integer _ ) As UInteger End Function End Class ' Form1.vb Public Class Form1 Private Sub ListBox1_DoubleClick(ByVal sender As Object, _ ByVal e As System.EventArgs _ ) Handles ListBox1.DoubleClick If ListBox1.SelectedItem IsNot Nothing Then Process.Start(DirectCast(ListBox1.SelectedItem, _ ApplicationInfo).Path) End If End Sub Private Sub Form2_Load(ByVal sender As Object, _ ByVal e As System.EventArgs _ ) Handles Me.Load Dim lApps As Dictionary(Of String, ApplicationInfo) lApps = (New ApplicationSearcher).Find() ListBox1.DisplayMember = "Description" ListBox1.DataSource = lApps.Values.ToList() End Sub End Class
Bedenke, dass dies nur ein Ansatz, sicher aber keine vollständige Lösung ist. Wie bereits erwähnt, werden damit unter anderem Anwendungen gefunden, die lediglich Helper Apps darstellen. Auch werden sicher nicht alle installierten Anwendungen gefunden, da wie gesagt, die Information über den Installationsort nicht immer ermittelbar ist. Da der Name des Executable als eindeutiges Merkmal hergenommen wird, um mehrfache Treffer der 4 durchsuchten Bereiche zu vermeiden, fallen auch verschiedene Versionen einer Anwendung unter den Tisch. Ggf. wäre hier der Pfad geeigneter.
Thorsten Dörfler
Microsoft MVP Visual Basic- Als Antwort vorgeschlagen Elmar Boye Sonntag, 2. Mai 2010 18:14
- Als Antwort markiert Robert Breitenhofer Freitag, 7. Mai 2010 14:47
-
Hallo Thorsten.
Ich liebe ja Herausforderungen, aber so wie es aussieht wird dass für meine derzeitigen Kenntnisse etwas zu groß. Da bleibe ich lieber derzeit noch bei den direkten Pfad angaben!
Also, werde mal schauen, welche Programme am häufigsten vertreten sind auf Windows Systemen und diese dann mittels Pfad suchen lassen.
Trotzdem Danke!
Dominik