Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 5 Min.

MRU-Programme ermitteln

In der Windows-Registry gespeicherte Dokumente und Programme auslesen.
© dotnetpro
Wie man die in der Registry binär abgelegten Infos zu MRU-Dokumenten (Most Recently Used) ausliest, haben Sie in der vorangegangenen Folge dieser Serie [1] erfahren. Allerdings werden nicht alle Einträge, die sich im Registrierschlüssel befinden, auch in der MRUListEx-Zusammenstellung aufgeführt. Aus diesem Grund wird nun die Funk­tion GetNonMRUListExElements definiert, die auch die noch nicht berücksichtigten Einträge ermittelt. Auch diese Funk­tion liefert die Kennungen der Elemente über ein Ganzzahldatenfeld zurück. Entsprechend der Funktion GetMRUListEx werden die Elemente über Indexwerte zusammengestellt. Dafür werden in GetNonMRUListExElements die Elemente der MRUListEx-Zusammenstellung ausgewertet, um im Anschluss daran alle im Registrierschlüssel vorhandenen Schlüsselnamen (RecentDocs_UserKey.GetValueNames) über eine For-Each-Schleife zu durchlaufen. Ob ein Name ein Element der MRUListEx-Aufstellung ist, prüft die Funktion IsMRUListExElement. Nur wenn dies nicht der Fall ist, wird das Element dem Datenfeld Elements hinzugefügt. Das Ergebnis wird an das aufrufende Programm zurückgegeben, vergleiche Listing 1.
Listing 1: Weitere Einträge auslesen
Function GetNonMRUListExElements(
  ByVal CurrentUserSubKey As String) As Integer()
  Dim Counter As Integer = -1
  Dim Elements As Integer() = Nothing
  Dim RecentDocs_UserKey As RegistryKey = 
    CurrentUser.OpenSubKey(CurrentUserSubKey, True)
  Dim SortedElements As Integer() = 
    GetMRUListEx(CurrentUserSubKey)
  For Each ElementName As String _
    In RecentDocs_UserKey.GetValueNames
    If ElementName <> "MRUListEx" Then
      If IsMRUListExElement(SortedElements, 
        ElementName) = False Then
          Counter = Counter + 1
          ReDim Preserve Elements(Counter)
          Elements(Counter) = CInt(ElementName)
      End If
    End If
  Next
  Return Elements
End Function 
Mit der Funktion IsMRUListExElement prüfen Sie, ob ein numerisches MRUListEx-Element, das via IntegerKeynamed Bestandteil der über den Parameter MruElements übergebenen MRUListEx-Auflistung ist oder nicht. Dazu wird die gesamte Liste per For-Each-Schleife durchlaufen, mit dem Schlüsselwert verglichen und das Ergebnis als Wahrheitswert zurückgeliefert.

Function IsMRUListExElement(
  ByVal MruElements As Integer(), 
  ByVal IntegerKeyname As String) As Boolean
  Dim result As Boolean = False
  For Each Element As Integer In MruElements
    If CInt(IntegerKeyname) = Element Then
      result = True
      Exit For
    End If
  Next
  Return result
End Function 
Aufbauend auf die in der zurückliegenden Folge dieser Serie beschriebenen Funktionen können Sie sich nun der Eintragsanalyse zuwenden. Dazu definieren Sie die Funktion Get­RecentDocFolderInfos, um ein Datenfeld mit Datei-/Ordnerinformationen abzurufen. Sie übergeben die Schlüsselnamen als Ganzzahldatenfeld (EntryList) sowie den HKCU-Datenunterschlüssel (CurrentUserSubKey) als Ausgangsknoten für die Analyse. Für die Verwaltung der Informationen wird das Datenfeld RecentDocs (Typ RecentDocFolderInfos) eingerichtet. Dann wird die Eintragsliste EntryList in einer For-Each-Schleife durchlaufen.Das Informationsdatenfeld wird dabei schrittweise mithilfe der Zählvariablen Counter ohne Wertverlust redimensioniert. Danach werden die Basis-Infos (Index, IndexName) und der Zähler Counter gesichert, um anschließend die binären Eintragsdaten mit GetValue in die Variable EntryData einzulesen, in eine binäre Zeichenkette umzuwandeln und in die Variable Data sowie über das Element BinaryString in die Datenstruktur zu übernehmen. Über die binären Zeichen wird dann der Dateiname DocName abgespalten und darüber der Bezug zu einer Verknüpfungsdatei hergestellt. Diese wird mit GetLinkFileInfo ausgewertet, um die zugehörigen Informationen in das aktiv verarbeitete RecentDocs-Element aufzunehmen (Listing 2).
Listing 2: Infos zu MRU-Ordnern/Dateien ermitteln
Function GetRecentDocFolderInfos(
  ByVal EntryList As Integer(), 
  ByVal CurrentUserSubKey As String) _
  As RecentDocFolderInfos()
  If EntryList IsNot Nothing Then
    Dim Counter As Integer = -1
    Dim RecentDocs As RecentDocFolderInfos() = Nothing
    Dim RecentDocs_UserKey As RegistryKey = 
      CurrentUser.OpenSubKey(
      CurrentUserSubKey, True)
    If RecentDocs_UserKey IsNot Nothing Then
      For Each Element As Integer In EntryList
        Counter = Counter + 1
        ReDim Preserve RecentDocs(Counter)
        With RecentDocs(Counter)
          .Counter = Counter
          .IndexValue = Element
          .IndexName = .IndexValue.ToString
          Dim EntryData As Byte() = 
            RecentDocs_UserKey.GetValue(
           .IndexName, Nothing)
          Dim Data As String = 
            GetStringFromBinary(EntryData)
          .BinaryString = Data
          Dim dLen As Integer = Data.Length
          .DocName = Data.Substring(
            0, Data.IndexOf(vbNullChar))
          Dim DataStart As Integer = 
            .DocName.Length
          Dim LinkFileStart As Integer = 
            Data.IndexOf(.DocName, DataStart)
          If LinkFileStart = -1 And 
            InStr(.DocName, ".") > 0 Then
            Dim DotPosition As Integer = 
              .DocName.IndexOf(".")
            Dim MainFileName As String = 
              .DocName.Substring(0, DotPosition)
            LinkFileStart = Data.IndexOf(
              MainFileName, DataStart)
          ElseIf LinkFileStart = -1 And
            InStr(.DocName, " ") > 0 Then
            Dim DotPosition As Integer = 
              .DocName.IndexOf(" ")
            Dim MainFileName As String = 
              .DocName.Substring(0, DotPosition)
            LinkFileStart = Data.IndexOf(
              MainFileName, DataStart)
          End If
          Try
            Dim LinkFileEnd As Integer = 
              Data.IndexOf(vbNullChar, LinkFileStart)
            Dim LinkFile As String = 
              "C:\Users\" &Environment.UserName &
              "\Recent\" & Data.Substring(
              LinkFileStart, LinkFileEnd - 
              LinkFileStart)
            If IO.File.Exists(LinkFile) Then
              Dim fi As New FileInfo(LinkFile)
              .LinkFileName = LinkFile
              .LinkFileInfo = GetLinkFileInfo(
              .LinkFileName)
              .LastAccessTime = fi.LastAccessTime
            Else
              .LinkFileName = LinkFile
            End If
          Catch ex As Exception
            ReDim Preserve RecentDocs(Counter -1)
          End Try
        End With
      Next
    End If
    Return RecentDocs
  Else
    Return Nothing
  End If
End Function 
Die Hilfsfunktion GetStringFromBinary übernimmt ein Byte-Datenfeld und konvertiert dieses mit Encoding.Unicode.GetString in eine binäre Zeichenkette und gibt das Ergebnis zurück.

Function GetStringFromBinary(
  ByVal EntryData As Byte()) As String
  Dim DocName As String = Nothing
  If EntryData IsNot Nothing Then
    DocName = Encoding.Unicode.GetString(
    EntryData, 0, EntryData.Length)
  End If
  Return DocName
End Function 

Registry-Verlaufsdaten ausgeben

Nachdem die Auswertungsfunktionen für die Recent-Einträge der Registry ausgelesen wurden, können Sie sich um die Datenausgabe kümmern (Bild 1). Für die vollständige Datenausgabe zeichnet die Methode GetAndShowRecentDocs verantwortlich. Der Methode wird der Ausgangsknoten für die Verlaufsdaten über den Parameter DateTimeNode übergeben (Listing 3). In der Routine wird der Ausgangsknoten der Registry für die Datenermittlung festgelegt, um dann mit GetMRUList­Ex die Schlüsselnamen der zuletzt verwendeten Dokumente/Ordner zu ermitteln und im Anschluss daran die zugehörigen
Informationen mit GetRecentDocFolderInfos auszulesen. Diese Infos werden dann mit ShowRecentDocs in der Strukturansicht angezeigt.
Zuletzt geöffnete Dokumente und Ordner der Registry anzeigen lassen (Bild 1) © Autor
Listing 3: Registry-Verlaufsdaten anzeigen
Sub GetAndShowRecentDocs(
  ByVal DateTimeNode As HistoryTreeNode)
  Dim SubKey As String = "Software\Microsoft\Windows\
    CurrentVersion\Explorer\RecentDocs"
  Dim SortedElements As Integer() = 
    GetMRUListEx(SubKey)
  Dim rdi As RecentDocFolderInfos() = 
    GetRecentDocFolderInfos(SortedElements, SubKey)
  ShowRecentDocs(DateTimeNode, rdi, "MRUListEx", True)
  Dim UnSortedElements As Integer() = 
    GetNonMRUListExElements(SubKey)
  Dim rdi2 As RecentDocFolderInfos() = 
    GetRecentDocFolderInfos(UnSortedElements, SubKey)
  ShowRecentDocs(DateTimeNode, rdi2, 
    "kein MRUListEx", False)
  Dim RecentDocs_UserKey As RegistryKey = 
    CurrentUser.OpenSubKey(SubKey, True)
  For Each ValueName As String In 
    RecentDocs_UserKey.GetSubKeyNames
    SortedElements = GetMRUListEx(
      SubKey & "\" & ValueName)
    rdi = GetRecentDocFolderInfos(
      SortedElements, SubKey & "\" & ValueName)
    ShowRecentDocs(DateTimeNode, rdi, 
      "MRUListEx [" & ValueName & "]", True)
    UnSortedElements = GetNonMRUListExElements(
      SubKey & "\" & ValueName)
    rdi2 = GetRecentDocFolderInfos(
      UnSortedElements, SubKey & "\" & ValueName)
    ShowRecentDocs(DateTimeNode, rdi2, 
      "kein MRUListEx [" & ValueName & "]", False)
  Next
  SubKey = "Software\Microsoft\Windows\CurrentVersion\
    Explorer\RecentDocs\Folder"
  SortedElements = GetMRUListEx(SubKey)
  rdi = GetRecentDocFolderInfos(
    SortedElements, SubKey)
  ShowRecentDocs(DateTimeNode, rdi, "MRUListEx", True)
  UnSortedElements = GetNonMRUListExElements(SubKey)
  rdi2 = GetRecentDocFolderInfos(
    UnSortedElements, SubKey)
  ShowRecentDocs(DateTimeNode, rdi2, 
    "kein MRUListEx", False)
End Sub 
Der nächste Schritt kümmert sich um die Elemente, die nicht Bestandteil der Liste MRUListEx sind. Hier kommen die Funktionen GetNonMRUListExElements, GetRecentDocFolderInfos und ShowRecentDocs zum Einsatz. In einem gesonderten Arbeitsschritt wird der Unterschlüssel Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs\Folder verarbeitet, in dem die Ordner-Infos abgelegt sind.Die Ausgabe übernimmt die Methode ShowRecentDocs, der Sie den Ausgangsknoten für die Verlaufsdaten (DateTimeNode) sowie das Info-Datenfeld (Typ RecentDocFolderInfos) mit den zuletzt geöffneten Ordnern und/oder Dokumenten (rdi) übergeben. Zusatzinformationen können dabei über den String-Parameter Info übergeben werden.Ob eine Eintragsliste Bestandteil der MRUListEx-Zusammenstellung ist oder nicht, geben Sie über den Parameter MruListEntries an (True, False). Alle per For-Each-Schleife ausgewerteten Elemente werden mit den vorhandenen Informationen zur Anlage neuer Strukturknoten (Typ HistoryTreeNode) genutzt und datumsbezogen einsortiert. Gibt es einen Bezug zu einer Verknüpfungsdatei, wird daraus der Zeitstempel extrahiert. Der Zielknoten DestinationDateNode wird bei Vorhandensein direkt genutzt oder nach Bedarf neu angelegt. Das Bildsymbol mit dem Index 12 kennzeichnet einen Windows-Registry-Verlaufseintrag (Listing 4).
Listing 4: Registry-Infos ausgeben
Sub ShowRecentDocs(ByVal DateTimeNode As TreeNode, 
  ByVal rdi As RecentDocFolderInfos(), 
  ByVal Info As String,
  Optional MruListEntries As Boolean = True)
 
  If rdi IsNot Nothing Then
    Dim NodeSymbol As Integer
    If MruListEntries = True Then
      NodeSymbol = 12
    Else
      NodeSymbol = 13
    End If
    For Each Element As RecentDocFolderInfos In rdi
      If Element.LinkFileInfo.LinkFileName _
        IsNot Nothing Then
        With Element
          Dim EntryDate As String = 
            .LastAccessTime.ToShortDateString
          Dim DestinationDateNode As _
            HistoryTreeNode = Nothing
          If DateNodeExist(DateTimeNode, 
            EntryDate) = False Then
            DestinationDateNode = CreateNewDateNode(
              DateTimeNode, EntryDate)
          Else
            DestinationDateNode = GetDateNode(
              DateTimeNode, EntryDate)
          End If
          Dim NewEntryNode As New HistoryTreeNode
          With NewEntryNode
            .Text = Element.DocName
            .ImageIndex = 12
            .SelectedImageIndex = 12
            .Description = "Verlaufseintrag 
              [Registry - " & Info & "]"
            .NodeDate = Element.LastAccessTime
              .ToShortDateString
            .NodeTime = Element.LastAccessTime
              .ToShortTimeString
            If Element.LinkFileInfo.Type = 
              eLinkType.File Then
                .NodeType = "Datei"
            ElseIf Element.LinkFileInfo.Type = 
              eLinkType.Folder Then
                .NodeType = "Verzeichnis"
            Else
              .NodeType = "URL"
            End If
            .UrlOrFileOrFolder = Element.LinkFileName
          End With
          If NewHistoryNodeAlreadyExists(
            DestinationDateNode,
            NewEntryNode) = False Then
              DestinationDateNode.Nodes.Add(
                NewEntryNode)
          End If
        End With
      End If
    Next
  End If
End Sub 

Ausgeführte Programme ermitteln

Über die Registry können Sie auch die zuletzt im Ausführen-Dialog eingegebenen Befehle abrufen. Eine zeitliche Einordnung der Befehle ist allerdings nicht möglich. Die Informa­tionen werden daher im Hierarchiezweig Verlaufsdaten\Programmaufrufe ohne Datumszuweisung ausgegeben.Um die Informationen auszulesen, wird die Datenstruktur RunCommandInfos definiert. Die Struktur fasst den Registry-Namen RegistryName mit dem Aufrufbefehl Command zusammen.

Structure RunCommandInfos
  Dim Command As String
  Dim RegistryName As String
End Structure 
Die Funktion GetRunMRU ermittelt die letzten Ausführungsbefehle und gibt diese als Zeichenkettenfeld zurück. Funk­tionsintern werden alle Befehle dem Datenfeld RunMRUs (Typ RunCommandInfos) zugeordnet. Die Informationen selbst sind im Registrierzweig HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU zu finden. Nachdem der Knoten mit OpenSubKey geöffnet wurde, kann die MRU-Liste über den Schlüsselnamen MRUList ermittelt und der Zeichenkette EntryList zugeordnet werden. Anders als bei den zuvor genutzten Listen ist die Liste MRUList nicht verschlüsselt. Sie enthält lediglich Buchstaben, die den Schlüsselnamen der einzelnen Befehle entsprechen. Dementsprechend kann diese Zeichenkette per For-Schleife verarbeitet werden. Dazu wird nacheinander jeder einzelne Buchstabe extrahiert, um dann den zugehörigen Wert im gleichnamigen Schlüsselnamen auszulesen. Der zugehörige Aufrufbefehl wird ebenfalls unverschlüsselt verwaltet. In den Zeichenketten enthaltene Abschlusszeichen („\1“) werden entfernt und die Ergebnisliste RunMRUs an das aufrufende Programm zurückgegeben, siehe Listing 5.
Listing 5: Zuletzt ausgeführte Programme auslesen
Function GetRunMRUs() As RunCommandInfos()
  Dim Counter As Integer = -1
  Dim RunMRUs As RunCommandInfos() = Nothing
  Dim RunMRU_UserKey As RegistryKey =   
    CurrentUser.OpenSubKey("Software\Microsoft\
    Windows\CurrentVersion\Explorer\RunMRU", True)
  If RunMRU_UserKey IsNot Nothing Then
    With RunMRU_UserKey
      Dim EntryList As String = .GetValue("MRUList", "")
      If EntryList.Trim <> "" Then
        For x As Integer = 1 To Len(EntryList)
          Dim Letter As String = Mid(EntryList, x, 1)
          Counter = Counter + 1
          ReDim Preserve RunMRUs(Counter)
          RunMRUs(Counter).Command = 
            .GetValue(Letter).Replace("\1", "")
          RunMRUs(Counter).RegistryName = Letter
        Next
      End If
    End With
  End If
  Return RunMRUs
End Function 

Ausgeführte Programme ausgeben

Die Methode ShowExecutedPrograms(Listing 6) übernimmt die Ausgabe der Ausführungsbefehle für Programme. Über den Parameter rNode wird ihr der Wurzelknoten mit dem Text Verlaufsdaten übergeben. Mit GetRunMRUs ermittelt die Methode die Befehle, legt einen neuen Unterknoten mit dem Titel Programmaufrufe an und platziert darunter alle Aufrufbefehle (rci) per For-Each-Schleife in der Strukturansicht für Verlaufsdaten.
Listing 6: Zuletzt ausgeführte Programme ausgeben
Sub ShowExecutedPrograms(ByVal rNode As TreeNode)
  If IsInDesignMode() = False Then
    Dim rci As RunCommandInfos() = GetRunMRUs()
    Dim AppSubNode As New HistoryTreeNode
    With AppSubNode
      .Text = "Programmaufrufe"
      .ImageIndex = 6
      .SelectedImageIndex = 6
      .Description = "In der Systemregistrierung 
        gesicherte Ausführungsbefehle"
      .NodeDate = DateTime.Now.ToShortDateString
      .NodeTime = DateTime.Now.ToShortTimeString
      .NodeType = "Wurzelelement für Ausführungsbefehle"
      .UrlOrFileOrFolder = "HKCU\Software\Microsoft\
      Windows\CurrentVersion\Explorer\RunMRU"
      rNode.Nodes.Add(AppSubNode)
    End With
     For Each entry As RunCommandInfos In rci
       Dim ExecuteNode As New HistoryTreeNode
       With ExecuteNode
         .Text = entry.Command
         .ImageIndex = 7
         .SelectedImageIndex = 7
         .Description = "In der Systemregistrierung 
           gesicherte Ausführungsbefehle"
         .NodeDate = DateTime.Now.ToShortDateString
         .NodeTime = DateTime.Now.ToShortTimeString
         .NodeType = "Startbefehl"
         .UrlOrFileOrFolder = entry.Command
        AppSubNode.Nodes.Add(ExecuteNode)
      End With
    Next
  End If
End Sub  
Damit sind alle Informationen zu zuletzt geöffneten Ordnern, Dateien und ausgeführten Programmen über das Dateisystem und auch die Systemregistrierung ermittelt worden. In der kommenden Folge dieser Serie wird es darum gehen, weitere Verlaufsdaten auszulesen, die der Windows Explorer aufzeichnet.
Anzeige der zuletzt gestarteten Programme (Bild 2) © Autor
Ist auch das geschafft, fehlt nur noch ein wenig Feinschliff: Das Verlaufsdatensteuerelement wird abschließend mit Kontextmenüs und variablen Anzeigemodi optimiert.
Projektdateien herunterladen

Fussnoten

  1. Andreas Maslo, Windows Document History, dotnetpro 11/2023, Seite 132 ff.,

Neueste Beiträge

Arbeiten mit Tabellen und KI in Dataverse
Microsoft unterstützt die zentrale Datenmanagement-Lösung Dataverse in Power Apps mit KI-Features.
7 Minuten
6. Aug 2025
Managed DevOps Pools - Azure DevOps Pipelines Security
Agent Pools als Managed Service mit einfacher Integration in private Netzwerke und Authentisierung mittels Managed Identity tragen deutlich zur Sicherheit der Agent-Infrastruktur bei.
7 Minuten
7. Aug 2025
Müssen Ziele SMART sein?
Wenn es um Ziele im Projektmanagement oder in der Führung einer Organisation geht, stoßen wir schnell und fast ausnahmslos auf das Akronym SMART. Was steckt dahinter, und kann es nicht auch sinnvolle Ziele geben, die nicht SMART sind?
8 Minuten
Miscellaneous

Das könnte Dich auch interessieren

Sicher ist sicher - Azure DevOps Pipelines Security
Als integraler Bestandteil der Entwicklungsumgebung ist Azure DevOps Pipelines oft Ziel von Angriffen. Da ist es gut zu wissen, wo die Schwachstellen des Systems liegen.
14 Minuten
16. Jun 2025
CodeProject.AI Server in neuer Version - Lokaler AI-Server
CodeProject.AI Server (jetzt in Version 2.1.10) ist ein lokal installierter, selbstgehosteter, schneller, kostenloser und Open Source Artificial Intelligence Server für jede Plattform und jede Sprache.
2 Minuten
Für Einsteiger: Backend-Webentwicklung mit .NET - Microsoft
Auf YouTube bietet Microsoft eine Videoserie für Einsteiger in die Backend-Webentwicklung mit .NET.
2 Minuten
13. Feb 2024
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige