Autor |
Beitrag |
jaenicke
      
Beiträge: 19309
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, 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: 19309
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, 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: 19309
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, 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: 19309
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, 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: 19309
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, 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: 19309
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, 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.
|
|
|