Registry - SaveKey/ReadKey



  • Für die FAQ:
    Thema: Verwendung von SaveKey zur Sicherung von Registry-Schlüsseln in Win9x und NT-Systemen. Dabei wird auf die besonderen Merkmale der einzelnen OS eingegangen, die zum Teil verschieden Voraussetzungen zum Sichern von Registry-Schlüsseln verlangen.
    Einen Teilauszug von Code zum Wiederherstellen/Wiedereinlesen findet ihr weiter unten.

    //Mit GetLastError könnte man bei Fehlern den genauen Grund der Fehlermeldungen
    //auslesen. Darauf wurde hier verzichtet, um die Übersichtlichkeit zu waren.
    //Ausserdem ist es für den Anwender eigentlich unwichtig, in welchem Punkt die
    //Sicherung fehlschlägt. Ausnahme: Der User soll an den Entwickler zurück melden,
    //warum die Sicherung fehlschlug.
    //---------------------------------------------------------------------------
    #include <vcl.h>
    //#include <Registry.hpp>
    #include <windows.h>
    
    #pragma hdrstop
    
    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    //nachfolgende Funktion dient der Privilegbehandelung unter WinNT
    //Dies ist nötig, da selbst Admin-Rechte nicht automatisch eine
    //Freigabe der Registrymanipulation bedeuten.
    
    BOOL EnablePrivilege(LPCSTR lpszPrivilege)
    {
      TOKEN_PRIVILEGES TokenPrivileges = {0};
      HANDLE hToken = NULL;
      TokenPrivileges.PrivilegeCount = 1;
      TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
      if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES,
            &hToken) == FALSE) return FALSE;
      if(LookupPrivilegeValue(NULL, lpszPrivilege,
            &TokenPrivileges.Privileges[0].Luid) == FALSE) return FALSE;
      if(AdjustTokenPrivileges(hToken, FALSE, &TokenPrivileges,
            0, NULL, NULL) == FALSE) return FALSE;
      if(GetLastError() != ERROR_SUCCESS) return FALSE;
      if(CloseHandle(hToken) == FALSE) return FALSE;
      return TRUE;
    }
    //---------------------------------------------------------------------------
    //nachfolgende Funktion dient der Abfrage der Windowsversion, da in
    //None-NT-Systemen keine Privilegeinstellung nötig ist und diese
    //,falls aufgerufen, immer als vermeintlich fehlgeschlagen zurückkehrt.
    
    char *GetOperationSystemName()
    {
      OSVERSIONINFO osinfo;
        osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    
      if (!GetVersionEx(&osinfo)) return "Unbekannt";
        GetVersionEx(&osinfo);
        switch (osinfo.dwPlatformId)
        {
            case VER_PLATFORM_WIN32s:
                return "Win32s detected";
            case VER_PLATFORM_WIN32_WINDOWS:
                return "Win95 or Win 98 detected";
            case VER_PLATFORM_WIN32_NT:
                return "Windows NT detected";
        }
    }
    //---------------------------------------------------------------------------
    //eigentlicher Aufruf des Sichern's:
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    AnsiString sReg = Edit1->Text; // -- Key muß existieren, sonst Fehlermeldung beim Lesen
    AnsiString sFile = "D:\\temp\\savereg";
    HKEY hKey;
    //nachfolgend Einstellung des Hauptschlüssels via RadioGroup
    switch (RadioGroup1->ItemIndex){
      case 0:
        hKey = HKEY_CLASSES_ROOT;
        break;
      case 1:
        hKey = HKEY_CURRENT_USER;
        break;
      case 2:
        hKey = HKEY_LOCAL_MACHINE;
        break;
      case 3:
        hKey = HKEY_USERS;
        break;
      case 4:
        hKey = HKEY_CURRENT_CONFIG;
        break;
      }
    //nachfolgend wird geprüft, ob schon eine Sicherung existiert, und wenn ja,
    //wird abgefragt, ob diese alte Sicherung gelöscht werden darf.
    //Dies ist nötig, da bei Erstellung einer Save-Datei der Dateiname noch nicht
    //existieren darf!
    if (FileExists(sFile))
      {
        AnsiString Frage = "Wollen Sie die vorhandene Datei 'SaveReg' überschreiben?";
        if (MessageDlg(Frage, mtConfirmation, TMsgDlgButtons() << mbYes << mbNo, 0) == mrYes)
            DeleteFile(sFile); //löschen, falls erwünscht
        else return;
      }
    //Privilege freigeben: (Warum siehe oben)
    AnsiString System = GetOperationSystemName(); //Abfrage, welches OS - warum siehe oben
    if ((EnablePrivilege(SE_BACKUP_NAME)== true) || System != "Windows NT detected")
      {
    /*  //.........TRegistry-Variante........
    
        //-------------------------W I C H T I G !!!-------------------
        //  Wenn TRegistry benutzt wird, bitte unbedingt die Zeile
        //  Registry.hpp wieder einkommentieren (siehe 3.Zeile) und die
        //  WinAPI-Variante auskommentieren.
        //-------------------------W I C H T I G !!!-------------------
    
        TRegistry * Registry = new TRegistry();
        Registry->RootKey = hKey;
        if (!Registry->SaveKey( sReg, sFile))ShowMessage("Save nicht geschrieben!");
        else ShowMessage("Save erfolgt!");
        //.....TRegistry-Variante ENDE.......
    */
        //.........WinAPI-Variante...........
        HKEY rHandle;
        if (RegOpenKeyEx(hKey, sReg.c_str(), NULL,
                            KEY_ALL_ACCESS, &rHandle) != ERROR_SUCCESS)
            {
              ShowMessage("Der Schlüssel wurde nicht geöffnet oder gefunden!");
              return;
            }
        if (RegSaveKey(rHandle, sFile.c_str(), NULL) != ERROR_SUCCESS)
            ShowMessage("Save nicht geschrieben!");
        else ShowMessage("Save erfolgt!");
        //.......WinAPI-Variante ENDE..........
      }
    else
      {
        AnsiString meldg = "Privileg nicht einstellbar oder None-NT-System nicht erkennbar.";
        meldg = meldg + "\nErkanntes System: " + System;
        ShowMessage(meldg);
      }
    }
    //---------------------------------------------------------------------------
    

    [ Dieser Beitrag wurde am 07.12.2002 um 13:07 Uhr von Jansen editiert. ]



  • // -- zur Vervorständigung das Wiederherstellen der Registry 
    // -- aus dem erzeugten Registryfile.. (Auszug aus meiner App.)
    // -- zu beachten ist unbedingt, daß die Registry mit keinem anderen
    // -- Programm (z.B. RegEdit) geöffnet ist
    
    void __fastcall TMainForm::RestoreDefaultRegClick(TObject *Sender)
    {
       TRegistry * Registry = new TRegistry();   // -- Registryobject erzeugen
       AnsiString sReg = Edit1->Text;            // .. s.o.
       AnsiString sFile = "d:\\temp\\savereg";   // -- File ohne ext.
       AnsiString sMsg = "\tDefault Registryabbild wiederherstellen aus \n";
    
       // -- Sicherheitsabfrage, Registry wird nämlich sonst "wortlos" überschrieben
       sMsg += "\nFileName : <AppPath>\\" ;
       sMsg += sFile;
       sMsg += "\n Achtung alle veränderten Einstellungen gehen verloren !!";
       if (Application->MessageBox(sMsg.c_str(),"Info",MB_YESNO ) == IDYES)
       {
         // -- Freischalten der benötigten Sicherheitsprivilegien
         if (EnablePrivilege(SE_RESTORE_NAME) && (EnablePrivilege(SE_BACKUP_NAME)) )
         {
            // -- welchen Basispfad will man in der Reg benutzen
            // -- Alternative sind die im obigen Beispiel aufgeführten
            Registry->RootKey = HKEY_CURRENT_USER;
            // -- erzeugen des (Basis-)Keys, falls nicht vorhanden
            // -- in einer Doku steht, daß dieser Subkey vorhanden sein muss!!
            Registry->OpenKey(sReg,true); // -- durch true wird dieser erzeugt !!
            // -- schliessen nicht vergessen, sonst kann nicht imp. werden
            Registry->CloseKey();  
            // -- .. und eintragen der Werte aus dem File
            Registry->RestoreKey(sReg, sFile);
            Registry->CloseKey();
         }
       }
    }
    

    ?? Warum gehen meine Struktureinrückungen verloren ??

    <edit>Weil das [/cpp] nach dem Codeblock hingehört und nciht direkt nach [code type="C++"] (-;</edit>

    [ Dieser Beitrag wurde am 05.12.2002 um 09:38 Uhr von junix editiert. ]



  • hm, dass mit den EnablePrivilege würde ich aber dann schon korrekt machen und bei jedem Api-Aufruf auf GetLastError() prüfen...

    in etwa so:

    //---------------------------------------------------------------------------
    // system-plattform abfragen
    // liefert:
    //   -1                         für Fehlerfall
    //   VER_PLATFORM_WIN32s        für Win32s (Win 3.1)
    //   VER_PLATFORM_WIN32_WINDOWS für Win95, Win98, WinME
    //   VER_PLATFORM_WIN32_NT      für WinNT, Win2000, WinXP
    //---------------------------------------------------------------------------
    int GetSystemPlatformID()
    {
      OSVERSIONINFO osinfo;
    
      osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    
      if (!GetVersionEx(&osinfo)) return -1;
    
      return osinfo.dwPlatformId;
    }
    
    //---------------------------------------------------------------------------
    // formatierte fehlerausgabe erstellen (von GetLastError)
    // benutzt standard-system-sprache
    //---------------------------------------------------------------------------
    void ErrorOut(char Errstring[30])
    {
      DWORD  Error    = GetLastError();
      LPVOID lpMsgBuf;
    
      // fehlermeldung formatieren
      FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, Error,
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, 0);
    
      // fehlermeldung ausgeben
      MessageBox(Application->Handle,
                 (AnsiString((LPCTSTR) lpMsgBuf) + "\n[" + AnsiString(Errstring) +
                 "] Errorcode: " + IntToStr(Error)).c_str(),
                 "Fehler", MB_OK + MB_ICONERROR);
    
      // speicher freigeben
      LocalFree(lpMsgBuf);
    }
    
    //---------------------------------------------------------------------------
    // prozess-privileg setzen (setzt nur das privileg)
    // erwartet: Server   -> Name des Rechners  (NULL wenn lokal)
    //           Privileg -> Name des Privilegs (zu finden in winnt.h)
    //---------------------------------------------------------------------------
    void SetProcessPrivilege(char Server[30], char Privileg[30])
    {
      HANDLE              handle;
      LUID                luid;
      TOKEN_PRIVILEGES    tp;
      LUID_AND_ATTRIBUTES l_a;
    
      // LUID zum Privileg abfragen
      if (!LookupPrivilegeValue(Server, Privileg, &luid))
        ErrorOut("LookupPrivilegeValue");
      else
      {
        // prozess öffnen (geht nur wenn man das recht dazu besitzt)
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &handle))
          ErrorOut("OpenProcessToken");
        else
        {
          l_a.Luid          = luid;
          l_a.Attributes    = SE_PRIVILEGE_ENABLED;
    
          tp.PrivilegeCount = 1;
          tp.Privileges[0]  = l_a;
    
          // prozess-privilegien ändern
          if (!AdjustTokenPrivileges(handle, false, &tp, 0, NULL, NULL))
            ErrorOut("AdjustTokenPrivileges");
    
          // handle schliessen
          CloseHandle(handle);
        }
      }
    }
    //---------------------------------------------------------------------------
    

    aufruf über:

    // privilegien brauchen nur bei nt, 2000 oder xp gesetzt zu werden
    if (GetSystemPlatformID() == VER_PLATFORM_WIN32_NT)
      SetProcessPrivilege(NULL, SE_BACKUP_NAME);
    

    und bitte gewöhnt euch mal an, den quellcode ordentlich zu formatieren, sonst kann das doch kein schwein lesen!

    ciao

    [ Dieser Beitrag wurde am 05.12.2002 um 15:03 Uhr von Sunday editiert. ]



  • hm, dass mit den EnablePrivilege würde ich aber dann schon korrekt machen

    Ich auch. Deshalb mußt Du nach dem erfolgreichen Aufruf von AdjustTokenPrivilegs() trotzdem noch GetLastError() aufrufen, um sicher zu sein, daß das Privilege freigeschaltet ist. Es kann Dir passieren, daß die Funktion TRUE zurückliefert, GetLastError() aber ERROR_NOT_ALL_ASSIGNED. Bei nur einem Privilege ist dann also gar nichts passiert.

    BTW: Wenn Ihr das Privilege nicht mehr benötigt, solltet Ihr es wieder von SE_PRIVILEGE_ENABLED auf SE_PRVILEGE_USED_FOR_ACCESS zurücksetzen.



  • Hallo

    Habe den Code für SaveKey kommentiert - war natürlich berechtigte Kritik.
    @Sunday: über Formatierungen kann man sehr stark streiten. Ich persönlich mag auseinandergezogenen Quellcode, wo sich drei Befehle über (übertrieben 😉 ) 17 Programmzeilen hinziehen überhaupt nicht. Ich mag es eher kompakt. So ist es halt -jeder mag es anders - das sollte man akzeptieren.
    @king: Recht hast du - das Zurücksetzen sollte man absolut nicht vergessen. Danke für den Hinweis.

    @All: zu GetLastError - jeder sollte selbst entscheiden, ob er wissen möchte, in welcher Programmzeile der Fehler auftrat. Ich halte eine Meldung am Ende für den User völlig ausreichend. Und diese kommt spätestens, wenn die Speicherung fehlschlägt. Der User sollte nicht unbedingt mit den Fehlermeldungen überfordert werden. Dies ist Aufgabe des Entwicklers mögliche Abbrüche abzufangen und nicht des Users!


Anmelden zum Antworten