Delphi-PRAXiS
Seite 3 von 4     123 4      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Ini-Writes global abfangen? (https://www.delphipraxis.net/192336-ini-writes-global-abfangen.html)

a.def 10. Apr 2017 19:09

AW: Ini-Writes global abfangen?
 
Meine finale Unit. Funktioniert im kompletten Projekt einwandfrei:

Delphi-Quellcode:
unit classIni;

interface

uses
 SysUtils, IniFiles;

type
 TMemIniFile = class(IniFiles.TMemIniFile)

 private
  FAutoSave: Boolean;
 public
  constructor Create(const FileName: string; const AutoSave: Boolean = True); overload;
  procedure UpdateFile; override;
 end;

implementation

{TMemIniFile}

constructor TMemIniFile.Create(const FileName: string; const AutoSave: Boolean = True);
begin
 inherited Create(FileName);

 FAutoSave := AutoSave;
end;

procedure TMemIniFile.UpdateFile;
begin
 if FAutoSave then
  inherited;
end;

end.
Aufruf
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
 IniF: TMemIniFile;
begin
 IniF := TMemIniFile.Create(....., <irgendeine-boolsche-variable>);

 try
  IniF.WriteString('section', 'ident', 'value');
 finally
  IniF.UpdateFile; // wird nur wirklich ausgeführt, wenn die irgendeine-boolsche-variable true ist
  IniF.Free;
 end;
end;
Sinn zusammengefasst damit es jeder versteht:
das hier ist dasselbe wie der AutoSave-Parameter ab Berlin.
Mit diesem Code verhindere ich Fehler wenn mein Programm in einem UAC-geschütztem Verzeichnis gestartet wird.

Teamspeak macht es ähnlich. Bei Programmstart kommt in einem UAC-geschützten Verzeichnis die Meldung
Zitat:

---------------------------
Error
---------------------------
TeamSpeak 3 cannot write to the configuration file:
...settings.db

You can use TeamSpeak 3, but all settings will be lost!
---------------------------
Ignore Abort
---------------------------
Ein Klick auf Ignore setzt in diesem Fall eine Art AutoSave auf False (ganz grob ausgedrückt).

Rollo62 11. Apr 2017 09:10

AW: Ini-Writes global abfangen?
 
Wo schreibst du deine Daten denn hin ?
Wäre es nicht besser bei geschützem Verzeichnis in das User-Documents-Verzeichnis, oder
direkt immer in das User-Documents-Verzeichnis mit evtl. weiteren Unterverzeichnisse zu schreiben ?

Ich habe mittlerweile schreibende Zugriffe fast nur dort erlaubt, weil es woanders mal geht und mal nicht.
Obwohl shared oder common kann man nicht immer darauf zugreifen, insbesondere auf verschiedenen Platformen.
Vermutlich geht es dir um die gleiche Problematik.

Der Vorteil wäre: es geht immer.
Der Nachteil: es gibt Steuerdateien im User-Documents-Verzeichnis, die der mal löschen könnte.
Das Thema Sicherheit spielt bei meinen Anwendungen keine Rolle, das könnte natürlich auch noch so ein Punkt sein.

Rollo

Fritzew 11. Apr 2017 10:08

AW: Ini-Writes global abfangen?
 
Warum nicht einen ganz anderen Ansatz?

nur mal so schnell getippt als Anregung:

Das ganze über Interfaces realisieren. Stichwort testbar etc....

3 Units:

Interface
Delphi-Quellcode:
unit my.ini.interfaces;

interface

type
// Hier alles rein was im Prog tatsäclich benötigt wird
   iIniReadWriter = interface
      ['{AE9CC6E5-F0B8-4D39-8F6C-799423C60A37}']
      function ReadString(const Section, Ident, Default: string): string;
      procedure WriteString(const Section, Ident, Value: String);
   end;

implementation

end.
Implementation
Delphi-Quellcode:
unit my.ini.implementations;

interface

uses
   System.SysUtils,
   System.IniFiles,
   my.ini.interfaces;


type
   tiniReadWriterFile = class(TInterfacedObject, iIniReadWriter)
   private
      fchanged : boolean;
      fautosave: boolean;
      fini : TMemIniFile;

   private //  iIniReadWriter implementations
     function ReadString(const Section, Ident, Default: string): string;
      procedure WriteString(const Section, Ident, Value: String);

   public
      constructor Create(const FileName: string; const autosave: boolean = true); overload;
      constructor Create(const FileName: string; const Encoding: TEncoding; const autosave: boolean = true); overload;
      destructor Destroy; override;
   end;

implementation



constructor tiniReadWriterFile.Create(const FileName: string; const Encoding: TEncoding; const autosave: boolean = true);
begin
   inherited Create;
    fini := TMemIniFile.Create(FileName, Encoding);
   fautosave := autosave;
end;

constructor tiniReadWriterFile.Create(const FileName: string; const autosave: boolean = true);
begin
   inherited Create;
   fini := TMemIniFile.Create(FileName);
   fautosave := autosave;
end;

destructor tiniReadWriterFile.Destroy;
begin
   if fautosave and fchanged then
      fini.UpdateFile;

   fini.free;
   inherited;
end;

function tiniReadWriterFile.ReadString(const Section, Ident, Default: string): string;
begin
 result := fini.ReadString(Section, Ident, Default);
end;

procedure tiniReadWriterFile.WriteString(const Section, Ident, Value: String);
begin
  fini.WriteString(Section, Ident, Value);
  fchanged := true;
end;

end.
Factory
Delphi-Quellcode:
unit my.ini.factory;

interface

uses
  my.ini.interfaces;
type
   tIniReadWriterTypes = (tirwFileWrite, tirReadonly, tirwLogging) ;

  function CreateIniReadWriterFactory(aType : tIniReadWriterTypes; const Filename : String): iIniReadWriter;

implementation

uses
  my.ini.implementations;

 function CreateIniReadWriterFactory(aType : tIniReadWriterTypes; const Filename : String): iIniReadWriter;
 begin
   case aType of
      tirwFileWrite: result := tiniReadWriterFile.Create(Filename, true);
      tirReadonly :Result := tiniReadWriterFile.Create(Filename, false);
   //   tirwLogging: result := tiniReadWriterLogger.Create(Filename);
   end;
 end;

end.


Delphi-Quellcode:
unit my.useini;

interface

uses
  my.ini.interfaces;

  type

    tuseini = class
     private
     public
       procedure SaveSettings(Writer : iIniReadWriter);
    end;



implementation

{ tuseini }

procedure tuseini.SaveSettings(Writer: iIniReadWriter);
begin
 assert(Writer <> nil, 'Writer must be set');

 Writer.WriteString('Test', 'Dummy', 'Default');

end;

end.

TestProjekt
Delphi-Quellcode:
program Project13;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  my.ini.implementations in 'my.ini.implementations.pas',
  my.ini.interfaces in 'my.ini.interfaces.pas',
  my.ini.factory in 'my.ini.factory.pas',
  my.useini in 'my.useini.pas';



 const   aFile : String = 'c:\test\Test.ini';


procedure Test;
Var UseIni : tuseini;
begin
    Useini := tuseini.create;
     Writeln('Test Readonly') ;
    Useini.SaveSettings(CreateIniReadWriterFactory(tirReadonly, aFile));
    Writeln('Readonly Done');
    Useini.SaveSettings(CreateIniReadWriterFactory(tirwFileWrite, aFile));
    Writeln('Write Ini Done') ;
    Useini.free;
end;


begin
  try
    Test;

   Writeln('Sucess');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.

a.def 11. Apr 2017 10:44

AW: Ini-Writes global abfangen?
 
Zitat:

Warum nicht einen ganz anderen Ansatz?
Von Interfaces will ich mich eigentlich fernhalten denn ich verstehe die nicht und möchte die Funktionsweise momentan auch nicht erlernen :|
Aber ich gucke mir das trotzdem mal an!

Zitat:

Wo schreibst du deine Daten denn hin ?
Wäre es nicht besser bei geschützem Verzeichnis in das User-Documents-Verzeichnis, oder
direkt immer in das User-Documents-Verzeichnis mit evtl. weiteren Unterverzeichnisse zu schreiben ?
Beim ersten Start meines Programms kann der Nutzer entscheiden, wo Einstellungen und alles andere gespeichert werden sollen.
Er, der Benutzer, hat dabei die Möglichkeit zu wählen, ob die Anwendung portabel einsetzbar sein soll.
Heißt also, es kann im programm-eigenen-Verzeichnis und in AppData\Programmname gespeichert werden.

Die von mir bezogene Änderung bezieht sich nur auf die Variante, wenn Einstellungen im programm-eigenen-Verzeichnis gespeichert werden sollen, es aber wegen fehlender Rechte nicht funktioniert.

Delphi-Laie 11. Apr 2017 21:30

AW: Ini-Writes global abfangen?
 
Zitat:

Zitat von a.def (Beitrag 1367025)
Aufruf
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
 IniF: TMemIniFile;
begin
 IniF := TMemIniFile.Create(....., <irgendeine-boolsche-variable>);

 try
  IniF.WriteString('section', 'ident', 'value');
 finally
  IniF.UpdateFile; // wird nur wirklich ausgeführt, wenn die irgendeine-boolsche-variable true ist
  IniF.Free;
 end;
end;

Hallo a.def, just versuche ich, Dein Projekt nachzuvollziehen, doch ich verstehe es nicht recht. Ich nahm an, daß <irgendeine-boolsche-variable> dazu da ist, um festzustellen (zu "messen"), ob die Ini-Datei erstellt werden kann. Ergo müßte es egal sein, mit welchem booleschen Wert sie in diesen Konstruktor "eintaucht" (was allerdings mit der "const"-Deklaration keinen Sinn ergibt). Doch behält diese <irgendeine-boolsche-variable> ihren Wert nach dem Konstruktor "Create" unabhängig davon, ob in dieses Verzeichnis (die Ini-Datei) geschrieben werden kann oder nicht.

Wann wird denn <irgendeine-boolsche-variable> mit einem sinnvollen Wert gefüllt?

Danke und Gruß

Edit: Der Einwand mit dem const-Parameter war Unfug, ich nehme ihn zurück.

DeddyH 11. Apr 2017 22:03

AW: Ini-Writes global abfangen?
 
Das ist ein Parameter, der angibt, ob gespeichert werden soll und nicht, ob gespeichert werden kann.

Delphi-Laie 11. Apr 2017 22:11

AW: Ini-Writes global abfangen?
 
Zitat:

Zitat von DeddyH (Beitrag 1367242)
Das ist ein Parameter, der angibt, ob gespeichert werden soll und nicht, ob gespeichert werden kann.

Danke, Detlef! Das erhellt.

Gibt für mich zwar spontan keinen Sinn, denn Ini-Dateien sollen doch (eigentlich) immer gespeichert werden, sonst wären es doch keine (echten) Dateien, ohne Datenträger maximal noch Memory-Dateien, aber sei's drum, ich werde mich weiter damit beschäftigen.

himitsu 12. Apr 2017 06:55

AW: Ini-Writes global abfangen?
 
soll: es soll automatisch (bpätestens beim Free/Close gespeichert werden
kann: es sagt nicht aus, dass nicht dennoch später noch gespeichert werden könnte (Update)


Und was einen Sinn betrifft.

Wenn nichts gespeichert werden soll, warum werden dann überhaupt die Write-Funktionen aufgerufen, wenn doch eh nicht gespeichert werden soll?
Eingaben "wortlos" einfach so zu verwerfen ist eines der größten Fehlerpotentiale, die man in eine Komponente einbauen kann.

siehe TStrings.Objects ... ständigt wundert sich jemand, dass die Werte verschwinden, obwohl etwas zugewiesen wurde.
Eine Exception ala "Speichern/Objects nicht implementiert" hätte da sofort den Fehler aufgezeigt.

für mich ist soein Verhalten fast genauso schlimm wie ein
Delphi-Quellcode:
try ... except end;
im Code
und später heult dann jemand rum "mein Programm macht nie das, was ich will und ich weiß nicht warum :cry:".

Jumpy 12. Apr 2017 07:25

AW: Ini-Writes global abfangen?
 
Er hat das doch schon ausführlich erklärt, dass es Konstellationen gibt, bei dem die INI-Datei schreibgeschützt ist und dann sollen die Werte halt nicht (zurück)geschrieben werden und anstatt jetzt an 23.345 Stellen im Code zu sagen
Delphi-Quellcode:
If IniBeschreibbar then Ini.WriteString(...)
wird das schreiben so an einer zentralen Stelle verhindert. Im Hinterkopf behalten: Es geht um TMemIni, d.h. das schreiben an sich funktioniert ja (in die In-Memory Ini), diese wird am Ende ggf. nur nicht wieder als Datei zurück geschrieben.

himitsu 12. Apr 2017 07:38

AW: Ini-Writes global abfangen?
 
Dann sollte die INI das aber auch sagen, wenn sie es nicht will.

Delphi-Quellcode:
uses
  SysUtils, IniFiles;

type
  TAccessIniFile = class(TMemIniFile)
  private
    FReadOnly: Boolean;
    procedure SetReadOnly(Value: Boolean);
  public
    constructor Create(const FileName: string; ReadOnly: Boolean=False); overload;

    procedure WriteString(const Section, Ident, Value: String); override;
    procedure DeleteKey(const Section, Ident: String); override;
    procedure EraseSection(const Section: string); override;
    procedure UpdateFile; override;

    property ReadOnly: Boolean read FReadOnly write SetReadOnly;
  end;

{ TAccessIniFile }

constructor TAccessIniFile.Create(const FileName: string; ReadOnly: Boolean);
begin
  inherited Create(FileName);
  FReadOnly := ReadOnly;
end;

procedure TAccessIniFile.DeleteKey(const Section, Ident: String);
begin
  if FReadOnly then
    raise Exception.Create('ReadOnly');
  inherited;
end;

procedure TAccessIniFile.EraseSection(const Section: string);
begin
  if FReadOnly then
    raise Exception.Create('ReadOnly');
  inherited;
end;

procedure TAccessIniFile.SetReadOnly(Value: Boolean);
begin
  if not FReadOnly and (FReadOnly <> ReadOnly) then
    UpdateFile;
  FReadOnly := ReadOnly;
end;

procedure TAccessIniFile.UpdateFile;
begin
  //if csDestroying in ComponentState then
  //  Exit;
  //if FReadOnly then
  //  raise Exception.Create('ReadOnly');
  //inherited;

  if not FReadOnly then
    inherited;
end;

procedure TAccessIniFile.WriteString(const Section, Ident, Value: String);
begin
  if FReadOnly then
    raise Exception.Create('ReadOnly');
  inherited;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:38 Uhr.
Seite 3 von 4     123 4      

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz