Zur Hauptseite ... Zum Onlinearchiv ... Zum Abonnement ... Zum Newsletter ... Zu den Tools ... Zum Impressum ... Zum Login ...

Gedrucktes Heft

Diesen Beitrag finden Sie in Ausgabe 3/2016.

Unser Angebot für Sie!

Lesen Sie diesen Beitrag und 500 andere sofort im Onlinearchiv, und erhalten Sie alle zwei Monate brandheißes Access-Know-how auf 72 gedruckten Seiten! Plus attraktive Präsente, zum Beispiel das bald erscheinende Buch 'Access 2010 - Das Grundlagenbuch für Entwickler'!

Diesen Beitrag twittern

API-Praxis: Zugriff auf die Registry

Windows stellt einige API-Prozeduren zum Zugriff auf die Registry zur Verfügung. Die Registry verwaltet viele der für das System wichtigen Informationen. Hierzu gehören Informationen über das System, über die verschiedenen Anwendungen und Dateitypen und wie sie verknüpft werden und vieles mehr. Dieser Beitrag zeigt, wie Sie per VBA über die Funktionen der Windows-API auf die Registry zugreifen.

Neben den Informationen über das System finden Sie auch Informationen zu den Anwendungen selbst in der Registry. Wenn Sie zum Beispiel in den Optionen von Access den Standarddatenbankpfad ändern, wird der neue Pfad in die Registry eingetragen.

Auch wenn Sie der Datenbank einen neuen Assistenten oder ein Add-In hinzufügen, schreibt der Add-In-Manager die benötigten Informationen in die Registry. Für den Entwickler wird die Registry interessant, wenn er bestimmte Daten speichern will, die nicht in einer Tabelle untergebracht werden können oder sollen.

Die Registry ist im Prinzip wie das Dateisystem von Windows aufgebaut. Die Verzeichnisstruktur ist nahezu identisch. Statt der Dateien enthält die Registry Variablen. Wie die Dateien haben auch die Variablen einen bestimmten Inhalt.

Die Windows-API stellt einige Befehle zur Bearbeitung der Registry zur Verfügung. Im Folgenden lernen Sie Befehle, die Sie zum Hinzufügen, Bearbeiten und Entfernen von Schlüsseln benötigen, anhand eines Beispiels kennen.

Aufbau der Registry-Einträge

Die Einträge in die Registry, die Sie über den Befehl RegEdit öffnen (beispielsweise über das Suchen-Feld von Windows 10), sind nach einem bestimmten Schema aufgebaut. Auf der obersten Ebene befinden sich die Hauptpfade. Es gibt insgesamt fünf Hauptpfade, die Sie auch in Bild 1 sehen können:

Die Registry mit den fünf Hauptzweigen

Bild 1: Die Registry mit den fünf Hauptzweigen

  • HKEY_CLASSES_ROOT: Speichert Informationen über die unterschiedlichen Dateitypen und die dazugehörenden Anwendungen.
  • HKEY_CURRENT_USER: Speichert die Einstellungen des aktuellen Benutzers.
  • HKEY_LOCAL_MACHINE: Speichert Informationen über die Hard- und Software des Systems.
  • HKEY_USERS: Speichert die voreingestellten Einstellungen bezüglich Hard- und Software sowie benutzerdefinierte Änderungen.
  • HKEY_CURRENT_CONFIG: Speichert allgemeine Informationen über die Systemkonfiguration.

Jeder dieser Hauptpfade enthält weitere, aus unterschiedlich vielen Ebenen bestehende Unterpfade. Der unterste Pfad jeder der Ebenen enthält schließlich die eigentlichen Schlüssel. Die Schlüssel bestehen aus einem Namen und einem Wert. Mit dem Registrierungseditor, mit dem Sie die Einträge der Registry bequem einsehen, editieren und löschen sowie neue Einträge hinzufügen können, greifen Sie über die Benutzeroberfläche auf diese Schlüssel zu.

Der in Bild 2 markierte Pfad lautet beispielsweise HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Access\Menu Add-Ins\RibbonAdmin2016.

Die Access-Add-Ins und ihre Registrierung in der Windows-Registry

Bild 2: Die Access-Add-Ins und ihre Registrierung in der Windows-Registry

In dem Pfad finden Sie vier Schlüssel, die einige Informationen zu einem Access-Add-In beinhalten – unter anderem die Funktion, die beim Aufruf des Add-Ins gestartet werden soll, sowie der Ort der Bibliothek, in der die benötigte Funktion enthalten ist.

Praxisbeispiel: Shareware mit zeitlicher Begrenzung

Wenn Sie eine Datenbankanwendung als Shareware vertreiben, möchten Sie die Benutzung der Anwendung möglicherweise auf eine Testphase von beispielsweise 30 Tagen beschränken. Die Anwendung muss also bei jedem Start zunächst überprüfen, ob der angegebene Zeitraum schon beendet ist oder nicht. Dazu müssen Sie zunächst eine Information über das Installationsdatum oder das Datum der ersten Anwendung speichern.

Das Speichern der Daten innerhalb der Datenbank ist deshalb nicht zu empfehlen, weil der Anwender die Datenbank nach Ablauf der 30 Tage einfach neu installieren könnte. Eine weitere Möglichkeit ist das Speichern des Datums in der Registry. Wenn der Benutzer die Anwendung startet, vergleicht die Anwendung das in die Registry eingetragene Datum mit dem aktuellen Datum und soll den Benutzer gegebenenfalls auf das Ablaufen der Testphase hinweisen. Wenn der Benutzer dann die Datenbank neu installiert, findet die Datenbank den noch vorhandenen Registry-Eintrag und erkennt, dass die Testphase bereits einmal abgelaufen ist.

Zunächst einmal überlegt man sich, wo in der Registry ein solcher Wert am besten abgelegt wird. Da die Anwendung vermutlich unabhängig vom Benutzer auf dem Rechner installiert, wählt man den Hauptpfad HKEY_CURRENT_USER aus. Der Name des Pfades soll \AMVShop\Beispiele\Registry lauten. Den Schlüssel nennen Sie Installationsdatum und der Wert soll das aktuelle Datum sein.

Selbstverständlich sollten Sie die Informationen etwas besser verstecken und gegebenenfalls auch verschlüsseln, wenn Sie eine Anwendung wirksam schützen wollen. Da der vorliegende Anwendungsfall jedoch nur als Beispiel dienen soll, wird hier mit offenen Karten gespielt.

Konstanten

Im Rahmen der nachfolgend beschriebenen API-Funktionen definieren Sie einige Konstanten. Die Konstanten benötigen Sie, um innerhalb des VBA-Code statt nichtssagender Werte für den Entwickler verständliche Begriffe verwenden zu können. So definiert man beispielsweise die folgende Konstante HKEY_CLASSES_ROOT, um sie bei der Angabe eines Registryschlüssels statt des Ausdrucks &H80000003 zu verwenden:

Public Const HKEY_CLASSES_ROOT As Long = &H80000000

Damit Sie nicht zwischendurch immer wieder neue Kon­stanten definieren müssen, geben Sie direkt alle in Zusammenhang mit den Registryzugriffen benötigten Konstanten ein:

Public Const HKEY_CURRENT_USER   As Long = &H80000001
Public Const HKEY_LOCAL_MACHINE  As Long = &H80000002
Public Const HKEY_USERS          As Long = &H80000003
Public Const HKEY_CURRENT_CONFIG As Long = &H80000005
Public Const REG_STR             As Long = 1
Public Const REG_OPTION_NON_VOLATILE  As Long = 0
Public Const REG_SZ              As Long = 1
Public Const KEY_ALL_ACCESS      As Long = &H3F
Public Const StringlängeMax      As Long = 200
Public Const ERROR_SUCCESS       As Long = 0&
Public Const ERROR_NO_MORE_ITEMS As Long = 259&

Noch praktischer ist es, wenn Sie für die fünf Konstanten für die Hauptpfade der Registry gleich eine Enumeration anlegen. Dann können Sie später, wenn Sie in Funktionen eine dieser Konstanten als Parameter übergeben wollen, ganz einfach per Intellisense auf diese Konstanten zugreifen. Dies sieht dann so aus:

Public Enum eRegistryPfade
     HKEY_CLASSES_ROOT = &H80000000
     HKEY_CURRENT_USER = &H80000001
     HKEY_LOCAL_MACHINE = &H80000002
     HKEY_USERS = &H80000003
     HKEY_CURRENT_CONFIG = &H80000005
End Enum

Sie müssen die entsprechenden Konstanten dann allerdings löschen oder umbenennen, da sonst mehrere Elemente gleichen Namens im Code vorkommen, was zu Kompilierfehlern führt.

Achtung: Sie werden im Folgenden neben den Konstanten auch noch API-Funktionen deklarieren sowie VBA-Funktionen und -Prozeduren eingeben. Am besten legen Sie für die Konstanten und die API-Funktionen ein eigenes Modul an (zum Beispiel mdlAPIRegistry). Die VBA-Funktionen und -Prozeduren zum Thema Registry sollten Sie ebenso in einem eigenen Modul zusammenfassen – beispielsweise mdlRegistry.

Anlegen eines Schlüssels

Die API-Funktion RegCreateKeyEx dient zum Anlegen eines Registrierungsschlüssels. Falls der anzulegende Schlüssel bereits vorhanden ist, öffnet die Funktion den Schlüssel. Die Deklaration der Funktion lautet:

Declare Function RegCreateKeyEx _
     Lib "advapi32.dll" _
     Alias "RegCreateKeyExA" (_
     ByVal hKey As Long, _
     ByVal lpSubKey As String, _
     ByVal Reserved As Long, _
     ByVal lpClass As String, _
     ByVal dwOptions As Long, _
     ByVal samDesired As Long, _
     lpSecurityAttributes As SECURITY_ATTRIBUTES, _
     phkResult As Long, _
     lpdwDisposition As Long
) As Long

Interessant sind vor allem die Parameter hKey und lpSubKey. Für den Parameter hKey geben Sie entweder das Handle an, das auf den entsprechenden Eintrag in der Registry zeigt, oder Sie geben einen der Hauptpfade aus der obigen Liste ein. Für den Parameter lpSubKey geben Sie den Pfad des Registry-Wertes an, den Sie öffnen möchten.

Für den Parameter lpSecurityAttributes müssen Sie noch einen Type definieren, und zwar im Modul mdlAPIRegistry:

Private Type SECURITY_ATTRIBUTES
     nLength As Long
     lpSecurityDescriptor As Long
     bInheritHandle As Boolean
End Type

Um den Aufruf der API-Funktion ein wenig komfortabler zu gestalten, erstellen Sie eine Funktion, an die Sie einfach nur den Hauptpfad und den Pfad des Registry-Eintrages übergeben müssen:

Public Function SchluesselErstellen(lngHauptpfad As Long, _
         Pfad As String) As Long
...

Als Hauptpfad würden Sie nun eine der Konstanten wie etwa HKEY_LOCAL_MACHINE eingeben. Da wir dafür aber eine Enumeration angelegt haben, können Sie diese auch als Datentyp für lngHauptpfad angeben, sodass die Funktion nun wie in Listing 1 aussieht.

Public Function SchluesselErstellen(strHauptpfad As eRegistryPfade, strPfad As String) As Long
     Dim lngHandle As Long
     Dim secAttrib As SECURITY_ATTRIBUTES
     RegCreateKeyEx strHauptpfad, strPfad, 0&, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, secAttrib, lngHandle, 0&
     SchluesselErstellen = lngHandle
End Function

Listing 1: Hilfsfunktion zum Erstellen eines Schlüssels in der Registry

Sie können die Funktion zum Beispiel über das Testfenster aufrufen.

Geben Sie dort den folgenden Befehl ein, erhalten Sie Unterstützung bei der Auswahl des Hauptpfades (s. Bild 3):

Aufruf der Funktion SchluesselErstellen mit IntelliSense-Unterstützung

Bild 3: Aufruf der Funktion SchluesselErstellen mit IntelliSense-Unterstützung

MsgBox SchlüsselErstellen(HKEY_CURRENT_USER, "SOFTWARE\Haufe\Beispiele\Test")

Das Meldungsfenster zeigt daraufhin die Nummer des Handles an, mit dem Sie auf den Eintrag in die Registry zugreifen können, ohne den kompletten Pfad eingeben zu müssen – zum Beispiel 4576.

Außerdem erstellt die Funktion den angegebenen Pfad in der Registry, wenn er nicht bereits vorhanden ist. Wenn der Pfad bereits erstellt wurde, bereitet die Funktion den Pfad auf den Zugriff vor. Mit dem Registrierungseditor können Sie schnell überprüfen, ob die Funktion den Registrierungsschlüssel wie gewünscht angelegt hat. Öffnen Sie den Editor und navigieren Sie zu dem entsprechenden Pfad (s. Bild 4).

Der neue Eintrag in der Windows-Registry

Bild 4: Der neue Eintrag in der Windows-Registry

Öffnen eines Schlüssels

Wenn Sie nur auf einen Schlüssel zugreifen möchten, verwenden Sie den API-Befehl RegOpenKey. Die Funktion hat die folgende Syntax:

Declare Function RegOpenKeyEx _
     Lib "advapi32.dll" _
     Alias "RegOpenKeyExA" ( _
     ByVal hKey As Long, _
     ByVal lpSubKey As String, _
     ByVal ulOptions As Long, _
     ByVal samDesired As Long, _
     phkResult As Long) _
As Long

Auch den Aufruf dieser API-Funktion können Sie ein wenig komfortabler gestalten, indem Sie eine Funktion erstellen, die nur die wichtigsten Werte als Parameter erfordert:

Die Funktion SchluesselOeffnen aus Listing 2 hat nur einen Unterschied im Vergleich zu der vorherigen Funktion: Sie erstellt keinen neuen Schlüssel, falls der angegebene Schlüssel nicht vorhanden ist. Beachten Sie, dass die API-Funktion keinen Fehler auslöst, wenn der zu öffnende Schlüssel nicht vorhanden ist. Sie gibt lediglich für das Handle den Wert 0 zurück.

Public Function SchluesselOeffnen(lngHauptpfad As eRegistryPfade, strPfad As String) As Long
     Dim lngHandle As Long
     RegOpenKeyEx lngHauptpfad, strPfad, 0, KEY_ALL_ACCESS, lngHandle
     SchluesselOeffnen = lngHandle
End Function

Listing 2: Hilfsfunktion zum Ermitteln des Handles eines Registry-Eintrags

Ein Beispielaufruf sieht wie folgt aus:

  SchluesselOeffnen(HKEY_CURRENT_USER, "SOFTWARE\AMVShop\Beispiele\Test")
  3816 

Entfernen eines Schlüssels

Bevor Sie beginnen, den neuen Schlüssel mit Werten zu füllen, erfahren Sie noch, wie Sie einen Schlüssel per API-Aufruf wieder entfernen können. Auch das ist wichtig, denn wenn Sie mit der Installation der Software in die Registry eingreifen, sollten Sie die Änderung bei einer Deinstallation auch wieder rückgängig machen. Zum Löschen eines Pfades in der Registry verwendet man den API-Befehl RegDeleteKey, den Sie wie folgt deklarieren:

Declare Function RegDeleteKey _
     Lib "advapi32.dll" _
     Alias "RegDeleteKeyA" ( _
     ByVal hKey As Long, _
     ByVal lpSubKey As String _
) As Long

Die Funktion erwartet nur den Hauptpfad und den Pfad des zu löschenden Schlüssels. Die Funktion gibt den Wert 0 zurück – es sei denn, die Operation schlägt fehl, zum Beispiel, weil der angegebene Pfad nicht vorhanden ist. Wichtig ist, dass Sie den kompletten zu entfernenden Pfad bis in die unterste Ebene angeben.

Auch für diese API-Funktion haben wir wieder eine Wrapper-Funktion programmiert, die Sie in Listing 3 finden.

Public Function SchluesselLoeschen(lngHauptpfad As eRegistryPfade, strPfad As String) As Long
     Dim lngHandle As Long
     lngHandle = RegDeleteKey(lngHauptpfad, strPfad)
     SchluesselLoeschen = lngHandle
End Function

Listing 3: Hilfsfunktion zum Löschen eines Eintrags

Wenn Sie nun den gleichen Pfad wieder löschen wollen, den Sie soeben angelegt haben, gelingt dies nicht mit dem folgenden Aufruf der Funktion:

  SchluesselLoeschen(HKEY_CURRENT_USER, "SOFTWARE\AMVShop\Beispiele\Test")
0

Dies liefert mit dem Wert 0 zwar eine Erfolgsmeldung, allerdings wird hier logischerweise nur genau das angegebene Element gelöscht, nämlich das Verzeichnis Test.

Wenn Sie alle Verzeichnisse löschen wollen, die Sie zuvor angelegt haben, sind noch zwei weitere Aufrufe nötig – Sie müssen nämlich jedes Verzeichnis vom hintersten an einzeln löschen:

  SchluesselLoeschen(HKEY_CURRENT_USER, "SOFTWARE\AMVShop\Beispiele")
0
? SchluesselLoeschen(HKEY_CURRENT_USER, "SOFTWARE\AMVShop")
0

Wenn Sie direkt versuchen, den Pfad SOFTWARE\AMVShop zu löschen, lösen Sie einen Fehler aus beziehungsweise erhalten einen anderen Wert als 0 zurück, in diesem Fall den Wert 5. Wenn Sie einen Pfad löschen wollen, der nicht mehr vorhanden ist, liefert die Funktion den Wert 2.

Schließen eines Schlüssels

Mit der API-Anweisung RegCloseKey schließen Sie einen geöffneten Schlüssel wieder und geben die dabei gebundenen Ressourcen wieder frei.

Der einzige Parameter der Funktion ist das Handle des zu schließenden Schlüssels, das Sie beim Öffnen mit Reg­OpenKey ermittelt haben. Die Deklaration lautet folgendermaßen:

Declare Function RegCloseKey _
     Lib "advapi32.dll" ( _
     ByVal hKey As Long _
) As Long

Wenn auch eigentlich nicht nötig, da ohnehin nur ein Parameter übergeben wird, haben wir dennoch auch hier eine Wrapper-Funktion erstellt. Sie liefert den Wert 0 zurück, wenn das Schließen gelungen ist, sonst den Wert 6:

Public Function SchluesselSchliessen(lngHandle As Long) As Long
     SchluesselSchliessen = RegCloseKey(lngHandle)
End Function

Setzen eines Schlüsselwertes

Nachdem Sie nun wissen, wie Sie Registrypfade anlegen, öffnen, schließen und wieder entfernen, lernen Sie jetzt, wie Sie mit der Funktion RegSetValueEx einen Schlüssel hinzufügen und einen Wert dafür angeben. Deklarieren Sie die Funktion RegSetValueEx folgendermaßen:

Declare Function RegSetValueEx _
     Lib "advapi32.dll" _
     Alias "RegSetValueExA" ( _
     ByVal hKey As Long, _
     ByVal lpValueName As String, _
     ByVal Reserved As Long, _
     ByVal dwType As Long, _
     lpData As Any, _
     ByVal cbData As Long _
) As Long

Wenn Sie die Deklaration aufmerksam betrachten, dass der Parameter lpData als einziger nicht ByVal, sondern ByRef übergeben wird und dass als Datentyp Any angegeben ist. Dahinter verbergen sich einige Besonderheiten.

Wie bereits weiter oben erwähnt, gibt es in C im Gegensatz zu Visual Basic Parameter, die unterschiedlichen Datentyps sein können. Hier tritt dieser Fall zu Tage: Auch die Schlüsselwerte in der Registry können unterschiedlichen Datentyps sein, zum Beispiel String oder Long. Unmittelbar vor dem Parameter lpData finden Sie aber den Parameter dwType. Mit ihm können Sie der API-Funktion mitteilen, welchen Datentyps der Parameter ist. Im Falle der Registry können Sie beispielsweise Zeichenketten (String) oder Variablen des Typs Long verwenden.

Falls Sie eine Zeichenkette verwenden, heißt die entsprechende Konstante REG_SZ. Für Variablen des Datentyps Long verwenden Sie die Konstante REG_DWORD. Die Konstanten dienen der besseren Handhabung für den menschlichen Benutzer, der mit ihnen sicher mehr anfangen kann als mit den Zahlenwerten, die sich hinter den Konstanten verbergen. Natürlich müssen Sie diese und andere Konstanten deklarieren:

Public Const REG_OPTION_NON_VOLATILE As Long = 0
Public Const REG_SZ As Long = 1
Public Const
Public Const KEY_ALL_ACCESS As Long = &H3F
Public Const StringlaengeMax As Long = 200
Public Const ERROR_SUCCESS As Long = 0&
Public Const ERROR_NO_MORE_ITEMS As Long = 259&

Die API-Funktion verwendet einige Parameter, die von vornherein festgelegt beziehungsweise bei der Ausführung ermittelt werden. Die Funktion aus Listing 4 vereinfacht den Aufruf der Funktion RegSetValueEx.

Public Function WertSetzenString(lngHauptpfad As eRegistryPfade, strPfad As String, strSchluesselname As String, _
         strSchluesselwert As String) As Long
     Dim lngHandle As Long
     Dim lngLaenge As Long
     lngLaenge = Len(strSchluesselwert)
     strSchluesselwert = strSchluesselwert & Chr$(0)
     lngHandle = SchluesselOeffnen(lngHauptpfad, strPfad)
     WertSetzenString = RegSetValueEx(lngHandle, strSchluesselname, 0&, REG_SZ, strSchluesselwert, lngLaenge)
End Function

Listing 4: Hilfsfunktion zum Eintragen eines Wertes in die Registry

Leider gelingt das Eintragen des Wertes noch nicht wie gewünscht, denn dieser erscheint in einem falschen Zeichensatz (s. Bild 5). Dies korrigieren wir, indem wir die speziell für Zeichenketten geeignete API-Funktion RegSetValueEx_Str verwenden:

Neuer Wert mit falscher Codierung

Bild 5: Neuer Wert mit falscher Codierung

Declare Function RegSetValueEx_Str _    Lib "advapi32.dll" _    Alias "RegSetValueExA" ( _    ByVal hKey As Long, _
     ByVal lpValueName As String, _
     ByVal Reserved As Long, _
     ByVal dwType As Long, _
     ByVal lpData As String, _
     ByVal cbData As Long _
) As Long

Den Aufruf aus der Funktion WertSetzenString passen Sie entsprechend an:

WertSetzenString = RegSetValueEx_Str(lngHandle,  strSchluesselname, 0&, REG_SZ, strSchluesselwert,  lngLaenge)

Die Funktion erwartet als Eingabeparameter den Hauptpfad, den Pfad, den Namen und den Wert des Schlüssels. Die Funktion hängt zunächst den Wert Chr$(0) an den Schlüsselwert an. Das ist erforderlich, damit die API-Funktion das Ende der Zeichenkette erkennen kann.

Der anschließende Aufruf der API-Funktion verwendet die Funktion SchluesselOeffnen, um aus dem Hauptpfad und dem Pfad das Handle auf den gewünschten Schlüssel zu ermitteln.

Nach dieser Änderung trägt die Funktion den Wert im richtigen Zeichensatz in die Registry ein. Ein Beispielaufruf für das Direktfenster sieht wie folgt aus:

  WertSetzenString(HKEY_CURRENT_USER, "SOFTWARE\AMVShop\ Beispiele", "Beispieltext", "Dies ist ein Beispieltext.")
  0 

Die Registrierungsdatentypen

In der Regel kommen Sie mit den folgenden verschiedenen Datentypen aus:

  • REG_BINARY: binäre Daten
  • REG_DWORD: eine 32-bit-Zahl
  • REG_EXPAND_SZ: eine null-terminierte Zeichenkette, die nicht aufgelöste Verweise wie %PATH% enthalten kann
  • REG_MULTI_SZ: eine Folge null-terminierter Zeichenketten, die durch den Ausdruck \n voneinander getrennt sind
  • REG_SZ: eine null-terminierte Zeichenkette

Nachfolgend finden Sie eine Liste der Deklarationen der entsprechenden Konstanten für die Datentypen inklusive Zahlenwerte:

Public Const REG_SZ As Long = 1
Public Const REG_EXPAND_SZ As Long = 2
Public Const REG_BINARY As Long = 3
Public Const REG_DWORD As Long = 4
Public Const REG_MULTI_SZ As Long = 5

Datumsangaben eintragen

Wie Sie weiter oben gesehen haben, gibt es für das Speichern einer Zahl eine etwas andere Konstante als für das Speichern einer Zeichenkette. Also bauen wir uns eine weitere Wrapper-Funktion, mit der wir zuverlässig Zahlenwerte in der Registry speichern können. Diese heißt WertSetzenLong und sieht wie in Listing 5 aus.

Public Function WertSetzenLong(lngHauptpfad As eRegistryPfade, strPfad As String, strSchluesselname As String, _
         lngSchluesselwert As Long) As Long
     Dim lngHandle As Long
     Dim lngLaenge As Long
     lngLaenge = Len(lngSchluesselwert)
     lngHandle = SchluesselOeffnen(lngHauptpfad, strPfad)
     WertSetzenLong = RegSetValueExA(lngHandle, strSchluesselname, 0&, REG_DWORD, lngSchluesselwert, lngLaenge)
End Function

Listing 5: Hilfsfunktion zum Eintragen eines Long-Wertes in die Registry

Wenn Sie so einen Datumswert speichern wollen, müssen Sie nur dafür Sorge tragen, dass dieser Datumswert im Format des Datentyps Long übergeben wird. Beim aktuellen Datum, das Sie mit der Funktion Date() ermitteln, ist dies kein Problem:

WertSetzenLong HKEY_CURRENT_USER,  "SOFTWARE\AMVShop\Beispiele", "Datum", Date()

Date() liefert ohnehin einen Long-Wert, der nur für Ausgabezwecke in das Datumsformat umgewandelt wird. Dies belegt auch ein Blick auf den neu angelegten Wert (s. Bild 6). Hier wird statt des Datums 8.5.2016 der Zahlenwert 42498 eingetragen (Anzahl der Tage seit dem 30.12.1899).

Ein weiterer neuer Eintrag in der Windows-Registry

Bild 6: Ein weiterer neuer Eintrag in der Windows-Registry

Die Funktion legt den Schlüssel allerdings nur an, wenn der gewünschte Pfad bereits besteht. Wenn Sie einen kompletten neuen Pfad mit Schlüsseln erstellen möchten, müssen Sie eine kleine Änderung in der Funktion WertSetzenLong vornehmen. Ersetzen Sie hier einfach SchluesselOeffnen durch SchluesselErstellen (dies gilt auch für WertSetzenString). So wird nicht nur das Handle auf den Pfad ermittelt, sondern der gewünschte Pfad direkt erstellt – soweit er nicht bereits vorhanden ist.

Die Funktionen erzeugen zwar sonst keinen Fehler, aber geben den Wert 6 zurück, was bedeutet, dass der Wert nicht angelegt werden konnte.

Abfragen eines Schlüsselwertes

Sie haben nun alle Informationen, um ein bestimmtes Datum in der Registry zu speichern. Wenn Sie, wie anfangs beschrieben, eine Shareware-Anwendung mit einer zeitlichen Begrenzung versehen möchten, fehlt Ihnen nun nur noch die Abfrage des Installationsdatums. Sie können dann leicht das Installationsdatum mit dem aktuellen Datum vergleichen und die Dauer der bisherigen Nutzung ermitteln.

Um einen Schlüsselwert aus der Registry zu ermitteln, verwenden Sie den API-Befehl RegQueryValueEx. Deklarieren Sie den Befehl wie folgt:

Declare Function RegQueryValueEx _
     Lib "advapi32.dll" _
     Alias "RegQueryValueExA" ( _
     ByVal hKey As Long, _
     ByVal lpValueName As String, _
     ByVal lpReserved As Long, _
     lpType As Long, _
     ByVal lpData As Any, _
     lpcbData As Long _
) As Long

Die Funktion WertLesenString aus Listing 6 vereinfacht den Aufruf der API-Funktion RegQueryValueEx. Sie erwartet als Eingabeparameter die Werte lngHauptpfad, strPfad und strSchluesselname. Die Funktion ermittelt zunächst mit der Funktion SchluesselOeffnen das Handle auf den gewünschten Registry-Eintrag. Hat dieser nicht den Wert 0, wurde der Eintrag gefunden und die Funktion führt die Anweisungen innerhalb der If...Then-Bedingung aus. In diesem Fall füllt die Funktion die Variable strBuffer mit 255 Leerzeichen und speichert die Länge der Zeichenkette in lngBuffer. Dann ruft sie die API-Funktion RegQueryValueEx auf und übergibt neben dem Handle noch den Namen des zu ermittelnden Schlüsselwertes, die Art des Wertes (REG_SZ für einen String) sowie die zu füllenden Variablen strBuffer und lngBuffer. Bei unserem Beispiel mit dem Wert Dies ist ein Beispieltext. für den Schlüssel Beispieltext liefert lngBuffer den Wert 27, was dem Wert unserer Zeichenkette entspricht. Damit können wir dann den relevanten Teil der zurückgelieferten Zeichenkette aus strBuffer mit der Left-Funktion ermitteln und zu guter Letzt den Schlüssel mit SchluesselSchliessen wieder schließen.

Public Function WertLesenString(lngHauptpfad As eRegistryPfade, strPfad As String, strSchluesselname As String)
     Dim lngHandle As Long
     Dim strBuffer As String
     Dim lngBuffer As Long
     Dim lngResult As Long
     lngHandle = SchluesselOeffnen(HKEY_CURRENT_USER, strPfad)
     If Not lngHandle = 0 Then
         strBuffer = Space(255)
         lngBuffer = Len(strBuffer)
         lngResult = RegQueryValueEx(lngHandle, strSchluesselname, 0&, REG_SZ, ByVal strBuffer, lngBuffer)
         WertLesenString = Left(strBuffer, lngBuffer)
         SchluesselSchliessen lngHandle
     End If
End Function

Listing 6: Hilfsfunktion zum Lesen eines String-Wertes aus der Registry

Ein Beispielaufruf für das Direktfenster sieht wie folgt aus:

  WertLesenString(HKEY_CURRENT_USER, "SOFTWARE\AMVShop\ Beispiele", "Beispieltext")
Dies ist ein Beispieltext. 

Zahlenwerte lesen

Wenn Sie einen zuvor geschriebenen Zahlenwert aus der Registry lesen möchten, nutzen Sie die Wrapper-Funktion WertLesenLong, die Sie in Listing 7 finden. Die Funktion erwartet als Parameter den Hauptpfad, also etwa die Konstante für HKEY_CURRENT_USER, den Pfad sowie den Schlüsselnamen.

Public Function WertLesenLong(lngHauptpfad As eRegistryPfade, strPfad As String, strSchluesselname As String)
     Dim lngHandle As Long
     Dim lngBufferSize As Long
     Dim lngResult As Long
     Dim lngValue As Long
     lngHandle = SchluesselOeffnen(HKEY_CURRENT_USER, strPfad)
     If Not lngHandle = 0 Then
         lngResult = RegQueryValueEx(lngHandle, strSchluesselname, 0&, REG_DWORD, ByVal 0, lngBufferSize)
         lngResult = RegQueryValueEx(lngHandle, strSchluesselname & Chr(0), 0&, REG_DWORD, lngValue, lngBufferSize)
         WertLesenLong = lngValue
         SchluesselSchliessen lngHandle
     End If
End Function 

Listing 7: Hilfsfunktion zum Lesen eines Long-Wertes aus der Registry

Die Funktion ermittelt wieder mit der Funktion Schluessel­Oeffnen das Handle auf den Schlüssel und speichert den entsprechenden Zahlenwert in der Variablen lngHandle. Wurde ein Eintrag gefunden und ist das Handle dementsprechend größer als 0, führt die Funktion die Anweisungen in der If...Then-Bedingung aus. Diesmal folgen zwei Aufrufe der API-Funktion RegQueryValueEx.

Der ersten vier Parameter der beiden Aufrufe sind identisch: Handle, Schlüsselname, 0& und die Art des Wertes (hier REG_DWORD). Die hinteren beiden unterscheiden sich jedoch: Im ersten Aufruf wollen wir zunächst die Größe des Ergebnisses ermitteln und in der Variablen lngBufferSize speichern.

Daher übergeben wir als fünften Parameter den Wert 0 mit dem Schlüsselwort ByVal und als sechsten die zu füllende Variable lngBufferSize. Den Wert von lngBufferSize übergeben wir mit dem zweiten Aufruf erneut, diesmal verwenden wir als fünften Parameter jedoch die Variable lngValue.

Dieser Aufruf füllt dann die Variable lngValue mit dem gewünschten Wert, den wir dann nur noch als Funktionsergebnis an die Variable WertLeseLong übermitteln müssen. Schließlich schließen wir den Schlüssel mit der Funktion SchluesselnSchliessen wieder.

Schlüssel durchlaufen

Gegebenenfalls möchten Sie einmal alle Unterschlüssel eines Schlüssels durchlaufen.

In diesem Fall benötigen Sie die API-Funktion RegEnumKeyEx, die wie folgt deklariert wird:

Declare Function RegEnumKeyEx Lib "advapi32.dll" _
     Alias "RegEnumKeyExA" ( _
     ByVal HKey As Long, _
     ByVal dwIndex As Long, _
     ByVal lpName As String, _
     lpcbName As Long, _
     ByVal lpReserved As Long, _
     ByVal lpClass As String, _
     lpcbClass As Long, _
     lpftLastWriteTime As FILETIME _
) As Long

Diese verwendet einen weiteren Typ namens FILETIME:

Public Type FILETIME
   dwLowDateTime As Long
   dwHighDateTime As Long
End Type

Die Funktion SchluesselDurchlaufen erwartet wiederum den Hauptpfad, also beispielsweise die Konstante HKEY_CURRENT_USER und den Pfad zum zu durchsuchenden Schlüssel als Parameter (s. Listing 8).

Public Function SchluesselDurchlaufen(lngHauptpfad As eRegistryPfade, strPfad As String)
     Dim lngSubkey As Long
     Dim lngEnum As Long
     Dim lngKey As Long
     Dim lngResult As Long
     Dim strBuffer1 As String
     Dim lngBuffer1 As Long
     Dim strBuffer2 As String
     Dim lngBuffer2 As Long
     Dim typFiletime As FILETIME
     lngResult = RegOpenKeyEx(lngHauptpfad, strPfad, 0&, KEY_ALL_ACCESS, lngSubkey)
     If lngResult = 0 Then
         Do
             strBuffer1 = Space$(255)
             lngBuffer1 = Len(strBuffer1)
             strBuffer2 = Space$(255)
             lngBuffer2 = Len(strBuffer2)
             lngResult = RegEnumKeyEx(lngSubkey, lngEnum, strBuffer1, lngBuffer1, 0&, strBuffer2, lngBuffer2, _
                 typFiletime)
             If lngResult = 0 Then
                 Debug.Print Left(strBuffer1, lngBuffer1)
             End If
             lngEnum = lngEnum + 1
         Loop Until Not lngResult = 0
         SchluesselSchliessen lngSubkey
     End If
End Function 

Listing 8: Funktion zum Durchlaufen der Schlüssel unterhalb des angegebenen Schlüssels

Die Funktion prüft zunächst mit der API-Funktion Reg­OpenKeyEx, ob der Schlüssel vorhanden ist, was den Wert 0 für die Variable lngResult schreibt. Gleichzeitig benötigen wir von dieser Funktion den Parameter lngSubkey, welcher das Handle auf diesen Schlüssel liefert.

Wenn lngResult den Wert 0 liefert, ist der Schlüssel vorhanden und wir können diesen nach untergeordneten Schlüsseln durchsuchen. Dazu verwenden wir eine Do...Loop Until-Schleife. Innerhalb der Schleife füllen wir die Variablen strBuffer1 und strBuffer2 mit jeweils 255 Leerzeichen und die Variablen lngBuffer1 und lngBuffer2 mit der Anzahl der in den Stringvariablen gespeicherten Zeichen. Dann rufen wir die API-Funktion RegEnumKeyEx auf und übergeben ihr die folgenden Parameter:

  • lngSubkey: Handle des zu durchsuchenden Schlüssels
  • lngEnum: aktuelle Nummer des zu ermittelnden Unterschlüssels, beim ersten Durchlauf 0
  • strBuffer1: Soll den Namen des Unterschlüssels aufnehmen.
  • lngBuffer1: Soll die Länge des Unterschlüssels aufnehmen.
  • strBuffer2: Ohne Funktion.
  • lngBuffer2: Ohne Funktion.
  • typFILETIME: Ohne Funktion.

Wenn lngResult den Wert 0 enthält, wurde ein Unterschlüssel gefunden, anderenfalls nicht. Im Falle eines Fundes gibt die Funktion den Namen des Unterschlüssels im Direktfenster aus. Dabei ermittelt sie den relevanten Inhalt der Variablen strBuffer1, die nach wie vor 255 Zeichen lang ist, über die Left-Funktion. Diese erwartet als zweiten Parameter die Variable lngBuffer1, welche die relevante Länge enthält.

Dann erhöht die Funktion den Wert der Variablen lngEnum um 1, damit beim nächsten Schleifendurchlauf das nächste Element ermittelt werden kann. Die Schleife wird beendet, wenn lngResult nicht den Wert 0 enthält. Dies ist der Fall, wenn RegEnumKeyEx kein Ergebnis mehr liefert.

Ein Aufruf sieht beispielsweise wie folgt aus – inklusive gekürztem Ergebnis:

Schluesseldurchlaufen HKEY_CURRENT_USER, "SOFTWARE"
Adobe
AMVShop
...
WinRAR
Wow6432Node
Classes

Werte eines Schlüssels durchlaufen

Ein ähnliches Spiel können wir auch noch mit den Werten eines Schlüssels durchführen. Die dazu benötigte API-Funktion heißt RegEnumValue und wird wie folgt deklariert:

Private Declare Function RegEnumValue _
     Lib "advapi32.dll" Alias "RegEnumValueA" ( _
     ByVal HKey As Long, _
     ByVal dwIndex As Long, _
     ByVal lpValueName As String, _
     lpcbValueName As Long, _
     ByVal lpReserved As Long, _
     lpType As Long, _
     lpData As Byte, _
     lpcbData As Long _
) As Long

Die Wrapper-Funktion namens WerteDurchlaufen finden Sie in Listing 9. Sie erwartet die folgenden Parameter:

Public Function WerteDurchlaufen(lngHauptpfad As eRegistryPfade, strPfad As String)
     Dim lngSubkey As Long
     Dim lngEnum As Long
     Dim lngResult As Long
     Dim strBuffer As String
     Dim lngBuffer As Long
     Dim lngType As Long
     Dim bytData(1 To 2048) As Byte
     Dim lngData As Long
     lngResult = RegOpenKeyEx(lngHauptpfad, strPfad, 0&, KEY_ALL_ACCESS, lngSubkey)
     If lngResult = 0 Then
         Do
             strBuffer = Space$(255)
             lngBuffer = Len(strBuffer1)
             Erase bytData
             lngData = UBound(bytData)
             lngResult = RegEnumValue(lngSubkey, lngEnum, strBuffer, lngBuffer, 0&, lngType, bytData(1), lngData)
             If lngResult = 0 Then
                 Debug.Print lngType, Left(strBuffer, lngBuffer)
             End If
             lngEnum = lngEnum + 1
         Loop Until Not lngResult = 0
         SchluesselSchliessen lngSubkey
     End If
End Function 

Listing 9: Funktion zum Durchlaufen der Werte unterhalb des angegebenen Schlüssels

  • lngHauptpfad: Konstante für den Hauptpfad des Schlüssels, also beispielsweise HKEY_CURRENT_USER
  • strPfad: Unterpfad, also etwa SOFTWARE\AMVShop\Beispiele

Sie prüft zunächst wieder mit der Funktion RegOpenKeyEx, ob der angegebene Schlüssel überhaupt existiert. In diesem Fall erhält lngResult den Wert 0 und der letzte Parameter des Aufrufs namens lngSubkey wird mit dem Handle auf diesen Schlüssel gefüllt.

In diesem Fall startet die Funktion wieder eine Do...Loop Until-Schleife. Innerhalb der Schleife füllt sie die Variable strBuffer mit 255 Leerzeichen und die Variable lngBuffer mit der Länge der soeben gefüllten Zeichenkette.

Die nächste wichtige Anweisung ruft die API-Funktion Reg­EnumValue auf und übergibt ihr die zusammengestellten Parameter. Das Ergebnis findet sich in den für uns interessanten Parametern strBuffer, lngBuffer und lngType. lngType enthält den Typ des Wertes, also zum Beispiel 1 für einen String oder 4 für einen DWORD-Wert (also eine Zahl).

Am wichtigsten aber ist der Wert von lngResult, denn wenn dieser nicht 0 ist, wurde mit diesem Aufruf kein Wert gefunden – in diesem Fall endet die Schleife. Anderenfalls gibt die Schleife den Wert der Variablen lngType, also den Typ des Wertes, und den Namen des Wertes aus, der wieder mithilfe der Left-Funktion mit den beiden Werten aus strBuffer und lngBuffer ermittelt wird. Nachdem alle Werte ermittelt wurden, schließt die Funktion mit einem Aufruf der Wrapper-Funktion SchluesselSchliessen den Schlüssel wieder. Ein Beispiel für unsere weiter oben hinzugefügten Beispielwerte sieht wie folgt aus:

WerteDurchlaufen HKEY_CURRENT_USER,  "SOFTWARE\AMVShop\Beispiele"
  4            Datum
  1            Beispieltext

Zusammenfassung und Ausblick

Mit den hier vorgestellten API-Funktionen und den dafür vorbereiteten Wrapper-Funktionen können Sie die wichtigsten Operationen rund um die Registry ausführen.

Interessant wäre es beispielsweise noch, bestimmte Schlüssel samt ihrer Werte rekursiv zu durchlaufen, um diese beispielsweise in die Datenbank zu schreiben und sie gegebenenfalls besser durchsuchbar zu machen. Die Windows-Registry hat ja den Nachteil, dass Sie die Werte immer nur Schritt für Schritt durchsuchen können, was mitunter etwas mühselig ist, wenn Sie nur ein bestimmtes Auftauchen eines Schlüssels suchen. Wenn Sie die Schlüssel hin und wieder in die Tabellen einer Datenbank exportieren, könnten Sie sich alle Fundstellen auf einen Blick ausgeben lassen.

Gegebenenfalls schauen wir uns eine solche Lösung in einem Beitrag in einer der folgenden Ausgaben einmal an.

Kompletten Artikel lesen?

Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

E-Mail:

Download

Download

Die .zip-Datei enthält folgende Dateien:

APIPraxis.accdb

Beispieldateien downloaden

© 2003-2015 André Minhorst Alle Rechte vorbehalten.