Die Registry von Windows ist für den einen oder anderen ein Buch mit sieben Siegeln. Tatsache ist: Dort landen manche wichtigen Informationen, die Sie gegebenenfalls einmal mit VBA auslesen wollen, oder Sie wollen dafür sorgen, dass per VBA bestimmte Elemente in der Registry angelegt werden. Wir stellen einige Routinen vor, die Ihnen die Arbeit mit der Registry erleichtern. Gleichzeitig liefern wir den Code in 64-Bit-kompatibler Form.
Per VBA mit der Registry arbeiten
Wann und wo Sie auf eine Anforderung stoßen, die mit dem Lesen oder Schreiben von Registry-Werten per VBA zu tun hat, lassen wir einmal dahingestellt. Wichtig ist allein, dass Sie nach der Lektüre dieses Beitrags auf den Ernstfall vorbereitet sind!
Daher schauen wir uns in diesem Beitrag nicht nur einen Satz von Funktionen an, welche wiederum die API von Windows für den lesenden und schreibenden Zugriff auf die Registry nutzen, sondern liefern auch noch eine Reihe von Beispielen für die Anwendung dieser Funktionen.
Unser konkreter Anlass, uns mit dem Thema VBA-Zugriff auf die Registry auseinanderzusetzen, war der Beitrag Optionen per VBA für Access 2019 (www.access-im-unternehmen.de/1320). Hier haben wir untersucht, welche Einstellungen des Dialogs Access-Optionen in der Registry landen. Um schnell prüfen zu können, ob sich nach dem Ändern einer Option unter Access einer der Einträge in der Registry geändert hat, haben wir eine Tabelle erstellt, die alle bereits in der Registry vorhandenen Werte enthält. Diese Werte haben wir initial einmal aus dem entsprechenden Bereich der Registry ausgelesen.
Anschließend haben wir nach jeder Änderung eine Prozedur durchlaufen lassen, welche alle Werte der Registry mit den bestehenden Werten in der Tabelle abgeglichen und eventuelle Unterschiede oder gar neue Werte im Direktbereich ausgegeben hat.
Access-Einstellungen in der Registry
Den Bereich der Registry, der die Access-Optionen enthält, findet sich unter HKEY_CURRENT_USER im Ordner SOFTWARE\Microsoft\Office\16.0\Access\Settings. Der Screenshot aus Bild 1 zeigt einige der Einstellungen.
Bild 1: Dieser Bereich der Registry speichert die meisten der Access-Optionen.
Auf diesen Ordner wollen wir uns in diesem Beitrag konzentrieren – wie lesen die Informationen aus, legen neue Werte an, erstellen Unterordner und so weiter. Vorher schauen wir uns allerdings noch die dazu notwendigen Funktionen im Modul mdlRegistry an.
Das Modul mdlRegistry und die Registry-Funktionen
Da das Modul die Deklaration von API-Funktion enthält sowie dafür vorgesehene Wrapperfunktionen, benötigen wir auch einige Enum– und Const-Elemente. Diese sehen wie folgt aus:
Public Enum Key HKEY_CLASSES_ROOT = &H80000000 HKEY_CURRENT_USER = &H80000001 HKEY_LOCAL_MACHINE = &H80000002 HKEY_USERS = &H80000003 End Enum Public Enum DataType dtString = 1 dtNumber = 4 End Enum Global Const ERROR_NONE = 0 Global Const ERROR_BADDB = 1 Global Const ERROR_BADKEY = 2 Global Const ERROR_CANTOPEN = 3 Global Const ERROR_CANTREAD = 4 Global Const ERROR_CANTWRITE = 5 Global Const ERROR_OUTOFMEMORY = 6 Global Const ERROR_INVALID_PARAMETER = 7 Global Const ERROR_ACCESS_DENIED = 8 Global Const ERROR_INVALID_PARAMETERS = 87 Global Const ERROR_NO_MORE_ITEMS = 259 Global Const KEY_ALL_ACCESS = &H3F Global Const REG_OPTION_NON_VOLATILE = 0
Neben diesen Elementen benötigen wir die Deklarationen der eigentlichen API-Funktionen. Dabei handelt es sich um 13 Funktionen, deren Deklaration Sie in Listing 1 finden.
Declare PtrSafe Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long Declare PtrSafe 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, ByVal lpSecurityAttributes As Long, phkResult As Long, lpdwDisposition As Long) As Long Declare PtrSafe 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 Declare PtrSafe Function RegQueryValueExString Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, _ ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, ByVal lpData As String, lpcbData As Long) As Long Declare PtrSafe Function RegQueryValueExLong Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, _ ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, lpData As Long, lpcbData As Long) As Long Declare PtrSafe Function RegQueryValueExNULL Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, _ ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, ByVal lpData As Long, lpcbData As Long) As Long Declare PtrSafe Function RegSetValueExString Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, _ ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, ByVal lpValue As String, _ ByVal cbData As Long) As Long Declare PtrSafe Function RegSetValueExLong Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, _ ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpValue As Long, ByVal cbData As Long) _ As Long Declare PtrSafe Function RegDeleteKey& Lib "advapi32.dll" Alias "RegDeleteKeyA" (ByVal hKey As Long, _ ByVal lpSubKey As String) Declare PtrSafe Function RegDeleteValue& Lib "advapi32.dll" Alias "RegDeleteValueA" (ByVal hKey As Long, _ ByVal lpValueName As String) Declare PtrSafe Function RegOpenKey Lib "advapi32.dll" Alias "RegOpenKeyA" (ByVal hKey As Long, _ ByVal lpSubKey As String, phkResult As Long) As Long Declare PtrSafe 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 Any) As Long Declare PtrSafe 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
Listing 1: Die benötigten API-Funktionen
Wrapperfunktion zum Auslesen von Werten
Die erste Wrapperfunktion soll das Auslesen eines Wertes für einen Eintrag eines Schlüssels/Unterschlüssels ermöglich. Diese Funktion erwartet drei Parameter:
- lngKey: Einen der Werte der Enum-Auflistung Key, zum Beispiel HKEY_CURRENT_USER
- strKeyName: Den Unterschlüssel, zum Beispiel SOFTWARE\Microsoft\Office\16.0\Access\Settings
- strValueName: Den Namen des zu ermittelnden Elements, zum Beispiel Form Template
Die Funktion nutzt die API-Funktion RegOpenKeyEx, um den angegebenen Schlüssel zu öffnen. Dann liest sie mit RegQueryValueEx das gewünschte Element aus. Der Parameter varValue nimmt den gesuchten Wert auf.
Dieser wird in den folgenden Schritten noch untersucht und von nicht erwünschten Zeichen befreit. Schließlich wird dieser Wert zurückgegeben und der Registry-Schlüssel mit der API-Funktion RegCloseKey wieder geschlossen (siehe Listing 2)
Public Function QueryValue(lngKey As Key, strKeyName As String, strValueName As String) Dim lngRetVal As Long Dim hKey As Long Dim varValue As Variant lngRetVal = RegOpenKeyEx(lngKey, strKeyName, 0, KEY_ALL_ACCESS, hKey) lngRetVal = QueryValueEx(hKey, strValueName, varValue) varValue = Trim(varValue) If varValue <> "" Then If Asc(Right(varValue, 1)) > 127 Then varValue = Left(varValue, Len(varValue) - 1) End If End If If varValue <> "" Then If Asc(Right(varValue, 1)) < 21 Then varValue = Left(varValue, Len(varValue) - 1) End If End If QueryValue = varValue RegCloseKey hKey End Function
Listing 2: Die Wrapperfunktion QueryValue
Wrapperfunktion zum Auslesen aller Einträge eines Schlüssels
Die Wrapperfunktion EnumKeyValues ermittelt alle Einträge der mit den Parametern übergebenen Schlüssel/Unterschlüssel-Kombination.
Die Funktion erwartet diese beiden Parameter:
- lngKey: Einen der Werte der Enum-Auflistung Key, zum Beispiel HKEY_CURRENT_USER
- strKeyName: Den Unterschlüssel, zum Beispiel SOFTWARE\Microsoft\Office\16.0\Access\Settings
Die Funktion öffnet wieder mit RegOpenKey den Schlüssel. Dann startet sie eine Do…Loop-Schleife, die dann endet, wenn die Funktion RegEnumValue den Wert 0 zurückgibt. Diese erhält in der Schleife wiederum einen Index als zweiten Parameter, der mit jedem Schleifendurchlauf erhöht wird, sowie als dritten Parameter eine mit 255 Leerzeichen vorbelegte Zeichenkette. Diese wird, sofern der aktuelle Index aus lngIndex ein gültiges Ergebnis hergibt, mit dem Namen des Eintrags gefüllt. Der Inhalt von strSave wird dann mit der Funktion StripTerminator von vbNullChar-Zeichen befreit und an das Array strKeys angehängt. Dieses gibt die Funktion schließlich als Ergebnis zurück:
Public Function EnumKeyValues(lngKey As Key, _ strSubkey As String) As String() Dim hKey As Long Dim lngIndex As Long Dim strSave As String Dim strKeys() As String RegOpenKey lngKey, strSubkey, hKey Do strSave = String(255, 0) If RegEnumValue(hKey, lngIndex, strSave, 255, _ 0, ByVal 0&, ByVal 0&, ByVal 0&) <> 0 Then Exit Do Else ReDim Preserve strKeys(lngIndex) strKeys(lngIndex) = StripTerminator(strSave) lngIndex = lngIndex + 1 End If Loop RegCloseKey hKey EnumKeyValues = strKeys End Function