Machen wir einen Schritt zurück und rufen uns den Teil in Erinnerung, in dem wir (hoffentlich erfolgreich!) die Funktion "RegEnumKeyEx" zum Ermitteln der Schlüsselnamen verwendet haben. Immer wenn der Funktionsaufruf erfolgreich war, öffnet das Beispielprogramm den ermittelten Schlüssel direkt, was verkürzt so aussieht:
if(retcode = ERROR_SUCCESS) and (RegOpenKeyEx(rgHandle,pBuf,0,KEY_READ,ukey) = ERROR_SUCCESS) then try { ... } finally RegCloseKey(ukey); end else break;
Die nachfolgenden Erläuterungen spielen sich zwischen dem try und finally ab, und daher ist auch "ukey" das Handle, das wir ab jetzt benutzen müssen. Das Beispielprogramm benutzt eine eigene Funktion zum Auslesen der Werte. Auf diese Weise müssen die selben Anweisungen nicht ständig neu geschrieben werden; zumal die Funktion auch gleichzeitig eine Art "value exists"-Prüfung enthält.
Und das ist auch gleich das erste, das wir uns ansehen wollen. Unser jeweiliger Unterschlüssel ist ja nun geöffnet, und nun wollen wir den Displaynamen (den Sie in der Systemsteuerung sehen) und den Uninstall-String auslesen. Zuerst sollten wir aber sicherstellen, dass der Wert überhaupt existiert, dass er dem erwarteten Typ entspricht, und dass er auch Inhalt hat. Dazu benötigen wir zwei dword-Variablen
var lpType, cbData : dword;
Zum Zugriff auf Werte steht uns die API-Funktion
Zuerst ein wenig Vorarbeit:
lpType := REG_NONE;
Mit dieser Zeile setzen wir zuerst einen nicht definierten (allgemeinen) Typ. Notwendig ist das nicht, weil die Funktion dieser Variablen später nur den ermittelten Typ zuweist, den vorher eingestellten Wert selbst aber nicht liest. Man sollte aber die Möglichkeit einkalkulieren, dass der Aufruf der Funktion bzw. das Zuweisen des Typs nicht klappen könnte. Und in dem Fall halte ich einen Standardwert für zweckmäßig, damit evtl. Vergleiche o.ä. keine Probleme verursachen.
Die Puffergröße wird auf Null gesetzt
cbData := 0;
Dann rufen wir die Funktion auf und übergeben zuerst wieder das Handle auf den Schlüssel
RegQueryValueEx(ukey
An zweiter Stelle kommt der Name des Wertes, der uns interessiert
'DisplayName',
Der dritte Parameter wird nicht benutzt
nil,
Nun folgt unser vorher festgelegter, allgemeiner Typ
@lpType,
Den Puffer ignorieren wir (wie schon gesagt)
nil,
Und zuletzt geben wir die Variable an, die die Puffergröße enthält.
@cbData)
Was passiert nun? Im Idealfall gibt es einen Wert mit dem gesuchten Namen. Unserer Variablen "lpType" wird nun der tatsächliche Typ des gefundenen Wertes zugewiesen.
| Wert | Bedeutung |
|---|---|
| REG_BINARY | binäre Form der Daten |
| REG_DWORD | 32-Bit-Wert |
| REG_SZ | null-terminierter String |
| REG_EXPAND_SZ | null-terminierter String, der Referenzen auf Umgebungsvariablen enthalten kann (z.B. "%PATH%") |
(im PSDK finden Sie weitere Typen)
Die Variable "cbData" enthält nach dem Funktionsaufruf die Anzahl der kopierten oder benötigten Bytes. Wenn Sie einen Wert angeben, der kleiner als der benötigte Puffer ist, dann gibt die Funktion den Fehler ERROR_MORE_DATA zurück, ansonsten ist das Ergebnis ERROR_SUCCESS. Damit wäre der weitere Weg bereits festgelegt:
if(RegQueryValueEx(ukey,'DisplayName',nil,@lpType,nil,@cbData) = ERROR_SUCCESS) and (lpType in[REG_SZ,REG_EXPAND_SZ]) and (cbData > 0) then begin { ... } end;
Im Fall des Beispielprogramms halte ich es für zweckmäßig sowohl REG_SZ als auch REG_EXPAND_SZ als Typ zu berücksichtigen. Es wäre ja möglich, dass ein Uninstall-String in letzterem Format gespeichert wird bzw. werden muss, weil er eine Referenz auf eine Umgebungsvariable enthält.
Wenn also unsere Prüfung geklappt hat, dann setzen wir den Puffer auf die gewünschte Länge und rufen die Funktion erneut auf:
SetLength(s,cbData); if(RegQueryValueEx(ukey,'DisplayName',nil,nil, @s[1],@cbData) = ERROR_SUCCESS) then SetLength(s,cbData-1) else s := '';
Unser Puffer ist hier eine string-Variable, die wir diesmal beim Funktionsaufruf natürlich auch angeben. Im Erfolgsfall enthält "s" nun den Inhalt des ausgelesenen Wertes.
Nicht unerwähnt bleiben darf und soll, dass dieser Weg nicht unter Windows NT, 2000 und XP funktioniert, wenn Sie auf Daten im Schlüssel HKEY_PERFORMANCE_DATA zugreifen. Hier würde die Funktion den Fehler ERROR_MORE_DATA auslösen, aber nicht die benötigte Puffergröße in die dword-Variable schreiben.
Das liegt daran, dass sich die Daten im genannten Schlüssel von einem Aufruf zum nächsten ändern können. Microsoft schlägt daher im PSDK eine Art Schleife vor, in der Sie den Wert der dword-Variablen solange erhöhen, bis die Funktion erfolgreich ist. Beispielsweise
cbData := 0;
while(RegQueryValueEx(ukey,'DynData',nil,@lpType,nil,@cbData) <> ERROR_SUCCESS) do inc(cbData);
Die zweite Möglichkeit wäre, die erforderlichen Puffergrößen über "RegQueryInfoKey" zu ermitteln. Etwa:
RegQueryInfoKey(rgHandle,nil,nil,nil,nil,nil,nil, nil,nil,@cbData,nil,nil);
In unserem Fall können wir dieses Problem zumindest vernachlässigen, weil wir zu keinem Zeitpunkt auf den Schlüssel HKEY_PERFORMANCE_DATA zugreifen.
Für unser Beispielprogramm benötigen wir nun zwar nur Strings, aber Sie stehen vielleicht einmal vor der Aufgabe, andere Formate auslesen zu müssen. Als Beispiel möchte ich den Registryeintrag
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\NoDriveTypeAutoRun
anführen. Normalerweise ist dieser Wert "0x00000095". Daher startet z.B. ein Programm automatisch, sobald Sie eine CD-ROM einlegen (sofern die CD entsprechend ausgerüstet ist, natürlich!). Würden Sie den Wert auf "0x000000b5" ändern, wäre der Autostart der CDs unterbunden. Das Interessante an diesem Wert ist, dass er unter Windows 98 als Binärwert gespeichert ist, unter Windows XP jedoch als dword.
Das ändert nun aber nichts am Prinzip; wir müssen lediglich das Betriebssystem prüfen und den passenden Typ vergleichen. Zum Auslesen selbst können wir in beiden Fällen eine dword-Variable benutzen, da ja auch die binäre Form (Win98) nur 4 Bytes groß ist. Das funktioniert, weil der Binärwert (wie in einer typischen Datei unter Windows) mit dem niederwertigen Byte zuerst gespeichert wird. Hätten wir z.B. eine dword-Variable mit dem Hex-Wert "0x01020304", würde dies in einer Datei (und auch im binären Format in der Registry) so aussehen:
04 03 02 01
Dieses "umgedrehte" Format interessiert uns allerdings nicht, weil wir ja nicht direkt auf die Festplatte zugreifen sondern Funktionen des Betriebssystems dafür verwenden. Also ist es hier Sache von Windows, in welcher Form solche Daten gespeichert werden. Müssten wir den Wert nun aus einer Datei lesen, bräuchten wir nur den Offset und könnten dann mit folgender Beispielanweisung aktiv werden:
BlockRead(f,AutorunSettings,sizeof(dword));
Windows sorgt im Hintergrund dafür, dass die Variable den richtigen Wert (quasi "richtig herum") enthält. Und beim binären Format der Registry ist es auch so, weshalb wir in beiden Fällen (ob nun REG_DWORD oder REG_BINARY) die selbe dword-Variable nutzen können.
Wir prüfen also, wie gehabt, ob der Funktionsaufruf erfolgreich war
if(RegQueryValueEx(rgHandle,'NoDriveTypeAutoRun',nil,@lpType,nil,@cbData) = ERROR_SUCCESS) and
Dann sollten wir prüfen ob der Typ ein Binärwert ist
(((lpType = REG_BINARY) or
oder ein dword-Wert; wenn Windows XP läuft
((lpType = REG_DWORD) and (WinXP))) and
Und natürlich sollte unser Wert genau 4 Bytes (die Größe eines dword) umfassen
(cbData = 4) then
Dann (und nur dann) sollten wir die Funktion erneut aufrufen und diesmal unsere Variable als Zeiger angeben:
RegQueryValueEx(rgHandle,'NoDriveTypeAutoRun',nil,@lpType,@AutorunSettings,@cbData);