Schlüssel kopieren bzw. verschieben


Wozu soll man das überhaupt können?
Im Beispielprogramm haben Sie die Möglichkeit, den Namen eines Schlüssels zu ändern. Dazu klicken Sie einen Eintrag in der List-View an und drücken F2, bzw. Sie klicken noch einmal, um den "in place"-Editor der List-View zu aktivieren. Wenn Sie den Namen dann ändern, muss dieser natürlich irgendwie in die Registry kommen.

Wäre es ein normaler Wert (= Eintrag), kein Problem: Sie hätten den alten Wert gelöscht und mit dem neuen Namen wieder eingetragen. Hier handelt es sich aber um Schlüssel, die wiederum Werte und Unterschlüssel enthalten können. Zugegeben, Schlüssel sind in diesem speziellen Fall sicher nicht zu erwarten, aber wenn Sie einen anderen Registryschlüssel kopieren oder verschieben wollen, dann sollten Sie damit rechnen.

Nun gibt es meines Wissens nach keine Funktion zum Kopieren oder Verschieben. Das PSDK erwähnt lediglich die Shell-Funktion "SHCopyKey", die aber unter Windows 95 und NT4 den IE5 voraussetzt. Da der Registry-Editor meiner Meinung nach auch schon damals Schlüssel umbenennen konnte, existiert entweder eine undokumentierte Funktion, die das PSDK verschweigt, oder Microsoft macht auch nichts weiter als das, was wir jetzt vorhaben ...


Werte enumerieren

Die Enumeration von Schlüsseln haben wir bereits hier besprochen. Schauen wir uns deshalb hier die Funktion RegEnumValue an, die das selbe für Werte macht. Beim Kopieren eines Schlüssels interessiert uns ja weniger, welche Einträge vorhanden sind, sondern es geht eher um die Frage: sind überhaupt Einträge da?

Wie auch beim Auslesen der einzelnen Schlüssel erwartet die Funktion zunächst das Handle des gerade geöffneten Schlüssels von uns

RegEnumValue(src,

Es folgt eine null-basierende Variable, die uns als Zähler dient und für jeden Aufruf der Funktion um Eins erhöht wird

  idx,

Danach wird der Puffer angegeben, der den Namen des Eintrags aufnehmen soll

  ValueName,

Die Puffergröße (max 16.383 Zeichen) wird auch gleich danach angegeben

  dwNameSize,

Zu beachten ist hier die Deklaration! Laut PSDK ist die Größenangabe ein LPDWORD, also ein Zeiger auf einen dword-Wert, so dass normalerweise "@dwNameSize" anzugeben wäre. Borland hat den Parameter in der "Windows.pas" von Delphi 5 jedoch als

var lpcbValueName: DWORD;

deklariert. Es mag sein, dass das in den neueren (oder auch älteren) Versionen wieder anders ist. Benutzen Sie deshalb im Zweifelsfall die Programmierhilfe von Delphi:




Nach der Größe des Namenspuffers folgt ein reservierter Wert, der nicht verwendet wird.

  nil,

Es folgt der Typ des Registryeintrags, der an die Variable im nächsten Parameter übergeben wird.

  @lpType,

Und da wir natürlich auch am Inhalt des Eintrags interessiert sind, benötigen wir zu guter Letzt einen Puffer, der diesen Inhalt aufnimmt

  ValueBuf,

und dessen Größenwert

  @dwBufSize);

Das ist wieder nur die Kurzform für einen Aufruf. Tatsächlich müssen Sie die Funktion solange aufrufen und dabei die schon erwähnte Zählervariable um Eins erhöhen, bis das Funktionsergebnis ERROR_NO_MORE_ITEMS ist. Eine Endlos-Schleife nach dem Muster, das wir beim Aufzählen der Schlüssel verwendet haben, bietet sich also auch hier an.

Und noch eine Ähnlichkeit gibt es: vor jedem Aufruf müssen die Variablen mit den Größenwerten des Namens- und des Inhaltspuffers wieder auf ihre Maximalwerte gesetzt werden. Wieso? Nehmen wir als Beispiel den Eintrag "DisplayName". Vor dem Aufruf der Funktion entspricht der Wert von "dwNameSize" dem Maximum (16.383), durch die Funktion wird die Variable aber auf den Wert 12 zurückgesetzt (das entspricht den 11 Zeichen inkl. des abschließenden Null-Zeichens). Würden Sie die Funktion nun wieder aufrufen und die Variable nicht zurücksetzen, stünde dem nächsten Eintrag nur ein Puffer von 12 Zeichen zur Verfügung. Hat dieser nächste Eintrag allerdings einen längeren Namen, wäre ein Fehler die Folge, bzw. der Eintrag würde ignoriert werden.
Das gleiche gilt auch für den Puffer, der den Inhalt des Eintrags aufnehmen soll. Der Größenwert, den Sie mit der letzten Variablen übergeben, wird auf die Anzahl der tatsächlich in den Puffer kopierten Bytes gesetzt. Vergessen Sie, diesen Wert wieder auf das Maximum des Puffers zu setzen, steht dem nächsten Eintrag augenscheinlich ein kleinerer Puffer zur Verfügung, was (sofern der Inhalt des nächsten Eintrags größer ist als der des eben gelesenen) den Fehler die Fehlermeldung ERROR_MORE_DATA zur Folge hat.

Wenn die Funktion erfolgreich ausgeführt wurde, dann besitzen wir jetzt zum einen den Namen des Eintrags und seinen Inhalt. Beides nutzen wir und legen in unserem neuen Schlüssel eine Kopie davon an. Dazu verwenden wir wie gehabt die Funktion "RegSetValueEx", die wir bereits im Kapitel Werte in die Registry schreiben kennen gelernt haben.


Schlüssel erzeugen

Es fehlt also noch ein logischer Schritt beim Kopieren eines Schlüssels: seine Unterschlüssel müssen erstens der Reihe nach gesucht und zweitens im Zielschlüssel neu erstellt werden. Wie gesagt, die Enumeration von Schlüssel haben wir uns bereits angesehen. Bleibt also nur noch das Erstellen. Dazu dient die Funktion RegCreateKeyEx.

Gehen wir also im Folgenden davon aus, dass wir gerade dabei sind, die einzelnen Schlüsselnamen mit "RegEnumKeyEx" der Reihe nach zu bestimmen. Jeden so ausgelesenen Schlüsselnamen reichen wir dann an "RegCreateKeyEx" weiter, wobei wir zuerst das Handle des Zielschlüssels angeben

RegCreateKeyEx(dest,

Es folgt natürlich der Name des Schlüssels, der im Ziel angelegt werden soll

  ValueName,

Der nächste Parameter wird wieder einmal nicht verwendet

  0,

Der nächste Parameter würde auf den Puffer mit den Klassennamen zeigen. Da wir diese aber beim Auslesen auch nicht berücksichtigt haben, können wir sie hier ignorieren

  nil,

Für den nächsten Parameter stehen drei Konstanten zur Verfügung. Wir verwenden die Vorgabe (Null, oder REG_OPTION_NON_VOLATILE)

  0,
Wert Bedeutung
REG_OPTION_NON_VOLATILE Vorgabe. Der Schlüssel ist "nicht flüchtig" (non volatile). Das heißt, die Informationen werden gespeichert und stehen z.B. nach einem Neustart des Systems wieder zur Verfügung.
REG_OPTION_VOLATILE Dieses Flag wird unter 9x/ME ignoriert. Hier wird ein Standardschlüssel (non volatile) erzeugt, und die Funktion liefert ERROR_SUCCESS als Ergebnis.
Unter NT/2000/XP wird mit diesem Flag ein "flüchtiger" (volatile) Schlüssel erzeugt. Die Informationen sind zwar in der Registry sichtbar, werden aber nur im Speicher gehalten und gehen beim Entladen des dazu gehörenden Registryteils verloren. Bei HKEY_CURRENT_USER z.B. nach dem Abmelden des aktuellen Benutzers, bei HKEY_LOCAL_MACHINE beim Shutdown des Systems.
Dieses Flag wird ignoriert, wenn der zu erzeugende Schlüssel bereits existiert.
REG_OPTION_BACKUP_RESTORE Dieses Flag ist für NT/2000/XP von Interesse. Wenn Sie es nutzen, wird der nächste Parameter (s. Codeausriss) ignoriert. Das Flag erfordert das "SE_BACKUP_NAME"-Recht.


Im nächsten Parameter geben wir die Zugriffsrechte auf den zu erzeugenden Schlüssel an. Da wir davon ausgehen müssen, dass der zu kopierende Schlüssel seinerseits wieder Daten und weitere Unterschlüssel enthält, verwenden wir

  KEY_READ or KEY_WRITE,

Der nächste Parameter spielt für uns keine Rolle

  nil,

Der nächste Parameter ist das Handle auf den neu erzeugten Schlüssel

  NewTo,

und zu guter Letzt ein Parameter, den ich ignoriert habe

  nil)

Wenn Sie ihn verwenden wollen, geben Sie hier einen Zeiger auf eine dword-Variable an und prüfen Sie das Ergebnis, das nur zwei Werte annehmen kann:

Wert Bedeutung
REG_CREATED_NEW_KEY Der Schlüssel existierte nicht und wurde erzeugt.
REG_OPENED_EXISTING_KEY Der Schlüssel existierte bereits und wurde einfach nur geöffnet.


Das Beispielprogramm enthält eine Prozedur namens "Reg_MoveKey" mit allen angesprochenen Aspekten.