| 
| Autor | Beitrag |  
| jaenicke 
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: Di 30.12.08 00:37 
 
Hallo!
 Es gibt ja immer wieder die Frage, wo und wie ich am besten die Einstellungen zu einer Anwendung speichere. Manche möchten eher eine portable Anwendung, andere lieber eine feste Installation. Ideal wäre es doch, wenn man beide Seiten gleichzeitig zufrieden stellen könnte, ohne dafür verschiedene Versionen des Programms erstellen zu müssen.
 Genau das ist möglich, wenn man es richtig angeht. Wie das geht, werde ich hier ausführen und an Hand einer Demo zeigen.
 Die Überlegung dahinter - wie geht es am besten
Der Quelltext dazu konkret
SJConfigUtils - eine Unit, die die Arbeit teilweise abnimmt
Das Demoprojekt
 Zusätzlich gibt es in einem weiteren Beitrag noch eine Erklärung dazu, warum es überhaupt notwendig ist, das Anwendungsdatenverzeichnis zu benutzen und man nicht einfach immer das Verzeichnis für Einstellungen nutzen kann, in dem das Programm selbst liegt:
www.delphi-library.d....php?p=541636#541636 
 Zuletzt bearbeitet von jaenicke am Mi 01.07.09 19:43, insgesamt 6-mal bearbeitet
 |  |  |  
| jaenicke  
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: Di 30.12.08 00:38 
 
1. Die Überlegung dahinter - wie geht es am besten
Es gibt mehrere Orte, an denen Einstellungen abgelegt werden können. Diese gelten teilweise für alle Benutzer des PCs oder werden bei einer Netzwerkanmeldung im Netzwerkprofil gespeichert, so dass auf allen PCs, auf denen sich der Benutzer anmeldet, diese Einstellungen vorhanden sind, und so weiter.
 Ideal wäre also eine Lösung, die alle diese Möglichkeiten unter einen Hut bringt und dem Benutzer die Wahl lässt wo die Einstellungen liegen sollen.
 Die Lösung ist, einfach in einer bestimmten Reihenfolge an den verschiedenen Orten nach den Einstellungen zu suchen (ich gebe hier die Standardpfade an, diese können auch anders lauten     ): Im eigenen Verzeichnis, dort wo die Exe liegt
 
C:\Dokumente und Einstellungen\[user]\Lokale Einstellungen\Anwendungsdaten (2000, XP)
C:\Users\Sebastian\AppData\Local (Vista, 7)
 Diese Einstellungen gelten nur für diesen Benutzer und diesen PC und werden im Falle einer Netzwerkanmeldung nicht auf andere PCs kopiert.
 
 
C:\Dokumente und Einstellungen\[user]\Anwendungsdaten (2000, XP)
C:\Users\Sebastian\AppData\Roaming (Vista, 7)
 Diese Einstellungen gelten nur für diesen Benutzer, werden aber im Falle einer Netzwerkanmeldung kopiert, gelten also überall wo sich der Benutzer anmeldet für diesen Benutzer.
 
 
		                       Quelltext 
 									| 1:
 | C:\Dokumente und Einstellungen\All Users\Anwendungsdaten					 |  C:\ProgramData (Vista, 7)
 Diese Einstellungen gelten für alle Benutzer des PCs, und werden bei einer Netzwerkanmeldung nicht kopiert.
 
 
In der Registry unter HKEY_CURRENT_USER\Software\Hersteller\Softwarename\Version
Diese Einstellungen gelten nur für diesen Benutzer, werden aber im Falle einer Netzwerkanmeldung kopiert, gelten also überall wo sich der Benutzer anmeldet für diesen Benutzer.
 
 
In der Registry unter HKEY_LOCAL_MACHINE\Software\Hersteller\Softwarename\Version
Diese Einstellungen gelten für alle Benutzer des PCs, und werden bei einer Netzwerkanmeldung nicht kopiert.
 Mit dieser Reihenfolge ist gewährleistet, dass die Einstellungen des einzelnen Benutzers vor denen aller Benutzer berücksichtigt werden, und die lokalen für den PC vor denen des Benutzers auf allen PCs.
 Zudem kann man eine portable Version vom USB-Stick mit deren Einstellungen starten ohne dass die Einstellungen einer installierten Version auf dem PC berücksichtigt oder verändert werden. 
 Zuletzt bearbeitet von jaenicke am Mi 01.07.09 19:43, insgesamt 5-mal bearbeitet
 |  |  |  
| jaenicke  
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: Di 30.12.08 00:38 
 
2. Der Quelltext dazu konkret
Hiermit suche ich in FindSettingsFile im Verzeichnis der Exe und in den drei Anwendungsdatenverzeichnissen. In dem FormCreate rufe ich das zuerst auf und suche, wenn keine Datei gefunden wurde, in der Registry. 												| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 21:
 22:
 23:
 24:
 25:
 26:
 27:
 28:
 29:
 30:
 31:
 32:
 33:
 34:
 35:
 36:
 37:
 38:
 39:
 40:
 41:
 42:
 43:
 44:
 45:
 46:
 47:
 48:
 49:
 50:
 51:
 52:
 53:
 54:
 55:
 56:
 57:
 58:
 59:
 
 | function GetSettingsFileName(ARootDir: String): String;begin
 Result := ARootDir + AppDataRootDir + AppDataProjectDir + '\MySettings.txt';
 end;
 
 function FindSettingsFile(var AFileName: String): Boolean;
 begin
 AFileName := ExtractFilePath(ParamStr(0)) + 'MySettings.txt';
 Result := FileExists(AFileName);
 if Result then
 Exit;
 AFileName := GetSettingsFileName(GetSpecialFolder(CSIDL_LOCAL_APPDATA));
 Result := FileExists(AFileName);
 if Result then
 Exit;
 AFileName := GetSettingsFileName(GetSpecialFolder(CSIDL_APPDATA));
 Result := FileExists(AFileName);
 if Result then
 Exit;
 AFileName := GetSettingsFileName(GetSpecialFolder(CSIDL_COMMON_APPDATA));
 Result := FileExists(AFileName);
 if Result then
 Exit;
 AFileName := ''; end;
 
 procedure TForm1.FormCreate(Sender: TObject);
 var
 Reg: TRegistry;
 Filename: String;
 begin
 if FindSettingsFile(Filename) then
 ...
 else
 begin
 Reg := TRegistry.Create;
 try
 Reg.RootKey := HKEY_CURRENT_USER;
 if Reg.OpenKeyReadOnly('Software' + AppDataRootDir + AppDataProjectDir) then
 ...
 else
 begin
 Reg.RootKey := HKEY_LOCAL_MACHINE;
 if Reg.OpenKeyReadOnly('Software' + AppDataRootDir + AppDataProjectDir) then
 ...
 end;
 finally
 Reg.Free;
 end;
 end;
 end;
 |  Das lässt sich natürlich auch anders machen, aber dies sollte nur ein kleines Beispiel dazu sein. 
 Zuletzt bearbeitet von jaenicke am Mi 01.07.09 19:40, insgesamt 3-mal bearbeitet
 Für diesen Beitrag haben gedankt: Xion
 |  |  |  
| jaenicke  
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: Di 30.12.08 00:39 
 
3. SJConfigUtils - eine Unit, die die Arbeit teilweise abnimmt
Diese Unit habe ich hier auch separat als Open Source vorgestellt:
www.delphi-forum.de/viewtopic.php?p=562996 Die Unit übernimmt die Verwaltung der Einstellungen, wo sie gespeichert werden und das Suchen nach Einstellungen beim Start. Aber natürlich kann die Unit die speziellen Einstellungen der Anwendung nicht kennen. Ich habe das so gelöst, dass es eine Klasse gibt, die die Verwaltung übernimmt und dem Programmierer dann praktisch sagt was er machen muss.
 Dafür enthält die Klasse mehrere abstrakte Methoden, die implementiert werden müssen: 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 11:
 12:
 13:
 
 |     procedure LoadFromStream(Data: TStream); override;procedure SaveToXml(Ini: IXMLDocument); override;
 procedure LoadFromINI(Ini: TCustomIniFile); override;
 procedure LoadFromRegistry(Reg: TRegistry; ParentPath: String); override;
 
 procedure SaveToStream(Data: TStream); override;
 procedure LoadFromXml(Ini: IXMLDocument); override;
 procedure SaveToINI(Ini: TCustomIniFile); override;
 procedure SaveToRegistry(Reg: TRegistry; ParentPath: String); override;
 
 procedure RunConfigWizard; override;
 procedure ApplyStandardSettings; override;
 procedure GetProgramInfo(var Author, ProductName, ProductVersion: string); override;
 |  Um diese zu implementieren, muss man nichts weiter wissen, alles nötige bekommt man geliefert.
 Natürlich kann man per Compilerschalter auch einzelne dieser Möglichkeiten deaktivieren. Man muss also nicht z.B. INIs und  die Registry unterstützen, nur die Unit tut es prinzipiell.
 Durch die Unterstützung eines Streams als Ziel kann man beliebige Formate verwenden, von XML bis zu eigenen Speichermethoden.
 Die Unit einzeln sowie eine genauere Erläuterung befinden sich in dem Vorstellungsthread in der Open Source Unit Sparte.
www.delphi-forum.de/viewtopic.php?p=562996 Ein Anwendungsbeispiel folgt in der Vorstellung des Demoprojektes  direkt anschließend. 
 Zuletzt bearbeitet von jaenicke am Mi 01.07.09 10:20, insgesamt 5-mal bearbeitet
 |  |  |  
| jaenicke  
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: Di 30.12.08 00:39 
 
4. Das Demoprojekt
Beim Start muss lediglich die abgeleitete Klasse instantiiert werden und schon kann man die Einstellungen lesen: 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 
 |   TfrmMain = class(TForm)...
 public
 
 Config: TAppConfig;
 end;
 
 ...
 
 procedure TfrmMain.FormCreate(Sender: TObject);
 begin
 Config := TAppConfig.Create;
 edtUserName.Text := Config.UserName;
 ...
 end;
 
 procedure TfrmMain.FormDestroy(Sender: TObject);
 begin
 Config.Free;
 end;
 |  Die Klasse TAppConfig wiederum sieht z.B. so aus, hier einmal nur für XML-Dateien: 												| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 21:
 22:
 23:
 24:
 25:
 26:
 27:
 28:
 29:
 30:
 31:
 32:
 33:
 34:
 35:
 36:
 37:
 38:
 39:
 40:
 41:
 42:
 
 |   TAppConfig = class(TAppConfigManager)private
 ...
 protected
 ...
 
 implementation
 
 procedure TAppConfig.RunConfigWizard;
 begin
 frmConfigWizard := TfrmConfigWizard.Create(nil);   frmConfigWizard.ShowModal(Self);
 end;
 
 procedure TAppConfig.ApplyStandardSettings;
 begin
 fUserName := 'Standardname';
 end;
 
 procedure TAppConfig.GetProgramInfo(var Author, ProductName,
 ProductVersion: string);
 begin
 Author := 'Sebastian Jänicke';
 ProductName := 'SJ Config Utils Demo';
 ProductVersion := '1.0';
 end;
 
 procedure TAppConfig.LoadFromXml(Ini: IXMLDocument);
 begin
 if AccessManager.InitReadLocation('Userinfo', sIniSectionOpenError) then
 begin
 fUserName := AccessManager.ReadString('Username', sIniValueNotFound);
 end;
 end;
 
 procedure TAppConfig.SaveToXml(Ini: IXMLDocument);
 begin
 if AccessManager.InitWriteLocation('Userinfo', sIniSectionOpenError) then
 begin
 AccessManager.WriteString('Username', fUserName);
 end;
 end;
 |  Das komplette Demoprojekt gibt es im Vorstellungsthread der Open Source Unit:
www.delphi-forum.de/viewtopic.php?p=562996 |  |  |  
| jaenicke  
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: Mi 01.07.09 19:51 
 
Abschließend bleibt zu sagen, dass die SJ Config Utils sicher nicht besonders umfangreich sind, die wirklich gleichen Aufgaben aber nach Möglichkeit abnehmen. Manche Features fehlen bewusst, weil es nicht so sinnvoll war, die einzubauen.
 Der Sinn von diesem Thread ist auch vor allem zu zeigen wie man die verschiedenen Möglichkeiten der Speicherung (portabel, nicht portabel) sinnvoll kombinieren kann. Denn da gibt es eben viele, die portabel gut finden, und viele, die das nicht gut finden. Deshalb ist für viele Programme eine solche Kombilösung eine gute Alternative zu einer Festlegung auf eine bestimmte Speicherung.
 Wenn jemand Änderungswünsche oder Ergänzungsvorschläge zu den SJ Config Utils hat, dann ist der Vorstellungsthread  der Unit der richtige Ort, unter anderem dafür habe ich die Threads überhaupt so getrennt. |  |  |  |