• Keine Ergebnisse gefunden

Die Registry

Im Dokument Win32API-Tutorials für Delphi (Seite 197-200)

5. Systemfunktionen

5.6. Die Registry

5.6.1. Vorwort

Die Registry ist eine Art Datenbank, in der Anwendungen und Systemkomponenten Konfigurationsdaten speichern, bzw.

aus der sie diese abrufen. Zum Zugriff auf die Registry steht ein eigener API-Befehlssatz zur Verfügung. Im Rahmen dieses Beitrags wollen wir auf die gebräuchlichen Funktionen eingehen, so dass wir am Ende ein kleines Programm zur Anzeige der installierten Software besitzen.

Warum gerade so ein Programm?

Zum einen habe ich den Quellcode dazu bereits fertig ... :o). Zum anderen ist es eine schöne Demonstration kleiner Geheimnisse, denn nicht jede installierte Komponente erscheint auch in der Systemsteuerung.

Wir werden das Programm nun aber nicht Zeile für Zeile durchgehen, sondern wir werden uns nur auf die für uns interessanten Teile der Registry konzentrieren. Alles andere (Fenster erzeugen, Toolbar erzeugen, Listview füllen, usw.) setze ich als bekannt voraus, bzw. im Zweifelsfall lässt sich in den entsprechenden Beiträgen die eine oder andere Sache nachschlagen.

Mein Dank geht an Markus Fuchs, Michael Puff und Christian Seehase, für das Lesen und Korrigieren der alten Versionen und die hilfreichen Anregungen und Empfehlungen zur Verbesserung des Programms.

5.6.2. Die Registry öffnen

Die installierte Software befindet sich, wie Sie sicher wissen, im Schlüssel

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall

Zum Öffnen dieses Schlüssel benutzen wir die Funktion "RegOpenKeyEx". Die Funktion erwartet zunächst einen Root-Schlüssel. Das kann ein zuvor geöffneter Registryschlüssel sein, oder einer der fünf vordefinierten und bekannten Schlüssel. Auf einen solchen müssen wir in dem Fall natürlich zurückgreifen, da wir bisher noch nichts geöffnet haben

if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,

Der nächste Parameter ist der gewünschte Pfad. Das Beispiel benutzt hier zwar eine Konstante, aber zur besseren Übersicht soll hier der tatsächliche Pfad genannt werden

'Software\Microsoft\Windows\CurrentVersion\Uninstall',

Der nächste Parameter ist reserviert und daher Null

0,

Nun geben wir an, wie wir auf den Schlüssel zugreifen wollen. Ausgehend von der Tatsache, dass wir die Namen der installierten Software nur lesen aber nicht ändern wollen, genügt uns hier die Option

KEY_READ,

KEY_READ ist ohnehin aus Sicherheitsgründen vorzuziehen. Da mit Windows XP allmählich ein System auf NT-Basis im Heimbereich Verbreitung findet, darf die Rechte-Frage meiner Meinung nach nicht außer Acht gelassen werden. Bei 9x-Systemen kann im Prinzip jeder Benutzer alles machen; Gutes wie Schlechtes. Bei NT-Betriebssystemen entscheiden die Rechte darüber, was ein Benutzer darf und was nicht. Im Normalfall hat auch nur ein Administrator vollen Zugriff auf den Schlüssel HKEY_LOCAL_MACHINE. Nun ist aber nicht auszuschließen, dass ein "normaler" Benutzer das Programm startet. Wenn Ihr Programm z.B. davon abhängig ist, Daten aus diesem Schlüssel zu lesen und in diesen zu speichern, dann sollten Sie es beenden, wenn keine ausreichenden Rechte vorhanden sind.

In unserem Fall kann diese Prüfung vernachlässigt werden, da wir hier die Daten ja nur lesen wollen. Und dafür genügt die o.g. Option.

Der letzte Parameter ist eine HKEY-Variable, die das Handle des geöffneten Schlüssels enthält. Diese Variable nutzen wir für die folgenden Lese- und (in anderen Fällen!) Schreibzugriffe.

rgHandle) = ERROR_SUCCESS) then try

{ ... } finally

RegCloseKey(rgHandle);

end;

Neben der Prüfung, ob der Zugriff (in dem Fall: das Öffnen des Schlüssels) erfolgreich war, empfiehlt es sich, die nun folgenden Anweisungen in einen try-finally-Block einzuklammern. Im Fehlerfall wird der Schlüssel dann wenigstens noch mit

RegCloseKey(rgHandle);

geschlossen.

RegOpenKeyEx-Definition

LONG RegOpenKeyEx(

HKEY hKey, // Handle eines zuvor geöffneten Schlüssels, bzw.

// HKEY_CLASSES_ROOT, usw.

LPCTSTR lpSubKey, // Pfad

DWORD ulOptions, // reserviert (Null) REGSAM samDesired, // Zugriffsrechte

PHKEY phkResult // HKEY-Variable mit dem Schlüssel-Handle für weitere Zugriffe

);

RegCloseKey-Definition

LONG RegCloseKey(

HKEY hKey // geöffneter Schlüssel );

Die wichtigsten Zugriffsrechte

Wert Bedeutung

KEY_READ kombiniert die Standardrechte (DELETE, READ_CONTROL, WRITE_DAC,

WRITE_OWNER, SYNCHRONIZE) mit KEY_QUERY_VALUE,

KEY_ENUMERATE_SUB_KEYS und KEY_NOTIFY KEY_QUERY_VALUE erforderlich um Werte auslesen zu können

KEY_ENUMERATE_SUB_KEYS erforderlich um vorhandene Schlüssel "aufzählen" zu können

KEY_WRITE kombiniert die Standardrechte mit KEY_SET_VALUE und

KEY_CREATE_SUB_KEY

KEY_SET_VALUE erforderlich um einen Registrywert erzeugen, löschen oder ändern zu können KEY_CREATE_SUB_KEY erforderlich um einen Schlüssel anlegen zu können

KEY_ALL_ACCESS kombiniert die Standardrechte mit den Rechten von KEY_READ und KEY_WRITE sowie mit KEY_CREATE_LINK

Hinweis

Wenn Sie die "Registry"-Unit verwenden, wird die Registry durch einen Aufruf von

reg := TRegistry.Create;

{ ... }

standardmäßig mit KEY_ALL_ACCESS geöffnet. Das kann, insbesondere auf NT-Systemen, zu den bereits geschilderten Rechte-Problemen führen, wenn Sie auf Schlüssel zugreifen, die nicht für jeden Benutzer zugänglich sind. Aber auch hier lässt sich ein anderes Zugriffsrecht verwenden. Es wird auch in der Delphi-Hilfe erwähnt, nur in den meisten Fällen leider überlesen oder sogar ignoriert. Unser API-Beispiel vom Anfang könnte z.B. so aussehen:

reg := TRegistry.Create(KEY_READ);

with reg do try

RootKey := HKEY_LOCAL_MACHINE;

if(OpenKey('Software\Microsoft\Windows\CurrentVersion\Uninstall',

Im letzten Kapitel haben wir die Registry geöffnet und befinden uns nun im Hauptpfad der installierten Software. Da wir nicht wissen können, welche Software der Anwender installiert hat, brauchen wir eine Möglichkeit, die vorhandenen Schlüssel der Reihe nach auszulesen.

Diese Möglichkeit heißt "RegEnumKeyEx".

Zur Ausführung benötigen wir zunächst natürlich ein Zeichenarray (pchar). Es bleibt dabei Ihnen überlassen, wie Sie vorgehen. Sie können das Array so deklarieren:

var pBuf : array[0..MAX_PATH]of char;

Oder Sie ermitteln die Größe des Puffers zur Laufzeit und deklarieren die Variable dann so:

var

pBuf : pchar;

Es funktioniert deshalb beides, weil (laut PSDK) die Länge für einen Schlüsselnamen max. 255 Zeichen beträgt. Und wenn Sie ein statisches Array verwenden wollen (s. erste Deklaration von "pBuf"), umfasst dieses durch MAX_PATH ja sowieso 260 Zeichen.

Aufpassen sollten Sie, wenn Sie mit der Unicode-Version, "RegEnumKeyExW", arbeiten wollen. Sie liefert einen Zeiger auf ein WideChar-Array, bzw. sie erwartet als Puffer ein solches Array; und hier belegt jedes Zeichen im Array zwei Bytes.

Sie sollten also die doppelte Puffergröße verwenden.

Neben dem Zeichenarray benötigen wir eine dword-Variable, die die Länge des Puffers angibt. Diese Länge muss vor jedem neuen Aufruf von "RegEnumKeyEx" auf den Maximalwert des Puffers zurückgesetzt werden! Warum?

Die Variable wird an die Funktion übergeben und von dieser auf die tatsächliche Länge des ermittelten Schlüsselnamens gesetzt. Nehmen wir als Beispiel an, der ermittelte Name lautet "IE40". Dann wird der Wert der dword-Variablen auf 4 gesetzt, da ja nur 4 Zeichen kopiert wurden.

Würden wir die Funktion nun ein zweites Mal aufrufen und dabei die Länge nicht auf die Puffergröße zurücksetzen,

Wenn der nächste Name allerdings mehr Zeichen aufweist, wäre der Fehler ERROR_MORE_DATA die Folge.

Und zu guter Letzt benötigen wir eine Zählervariable, die wir auf Null setzen und solange um Eins erhöhen, bis die Funktion ERROR_NO_MORE_ITEMS als Ergebnis zurückliefert.

Schauen wir uns zunächst das einfachere Beispiel an, bei dem wir ein "array[0..MAX_PATH]of char" als Puffer verwenden:

i := 0;

while(true) do begin

ZeroMemory(@pBuf,sizeof(pBuf));

dwLen := sizeof(pBuf);

Wie bereits angesprochen: wir setzen die Zählervariable "i" auf Null, da sie als Ausgangspunkt unserer Enumeration dient. Dann treten wir in eine Endlosschleife ein, leeren den Puffer und setzen die dword-Variable ("dwLen") auf die Puffergröße.

Jetzt rufen wir die Funktion auf und geben zuerst das Handle unseres geöffneten Schlüssels an:

retcode := RegEnumKeyEx(rgHandle,

Es folgt unsere Zählervariable

i,

unser Puffer

pBuf,

und dessen Größe

dwLen,

Die vier weiteren Parameter sind für uns nicht von Bedeutung und werden von uns daher auch ignoriert:

nil, nil, nil, nil);

Wenn es keine weiteren Schlüssel gibt, dann sollten wir die Schleife natürlich verlassen:

if(retcode = ERROR_NO_MORE_ITEMS) then break;

Wenn die Funktion erfolgreich ausgeführt werden konnte, dann befindet sich nun der Name des ersten bzw. nächsten Unterschlüssels im Zeichenpuffer, und wir können ihn in unsere Liste eintragen oder anderweitig be- oder verarbeiten

if(retcode = ERROR_SUCCESS) then begin

{ ... } end;

Und damit wir kein "hängendes" Programm basteln, das sich auf Nimmerwiedersehen verabschiedet, sollten wir unbedingt unsere Zählervariable erhöhen, damit wir irgendwann den Fehler ERROR_NO_MORE_ITEMS provozieren und die Schleife verlassen können.

inc(i);

end;

Sie können auch auf Nummer Sicher gehen und nur auf ERROR_SUCCESS prüfen; sollte diese Prüfung negativ verlaufen, verlassen Sie die Schleife mit "break". Auf die Weise ist sichergestellt, dass andere evtl. auftretende und unvorhergesehene Fehler Ihr Programm nicht abschießen.

Im Dokument Win32API-Tutorials für Delphi (Seite 197-200)