Delphi-PRAXiS

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 12:52

Ini-Writes global abfangen?
 
Für ein aktuelles Projekt wäre es nützlich, wenn ich alle Ini-Write-Befehle global abfangen könnte.
Am besten sollte das einfach in eins bestehendes Projekt einzuarbeiten sein (> 100.000 Zeilen).

Ist das irgendwie ohne große Hexerei möglich?

Edit:
bzw.Ini.UpdateFile abzufangen wäre auch nützlich.

SebastianZ 10. Apr 2017 13:15

AW: Ini-Writes global abfangen?
 
Spontan, ohne es getestet zu haben, wäre hier die einfachste Variante, die mir einfällt, einen globalen Classhelper für die TIniFile zu erstellen.
Delphi-Quellcode:
  TIniFileHelper = class helper for TIniFile
  public
    procedure WriteString(const Section, Ident, Value: String);
  end;


procedure TIniFileHelper.WriteString(const Section, Ident, Value: String);
begin
   //hier reagieren

  inherited WriteString(Section, Ident, Value);

end;
Das ganze müsste dann für jede Write-Variante gemacht werden.
Dazu sollte man sich etwas in die Classhelper einlesen:
http://docwiki.embarcadero.com/RADSt...elpers_(Delphi)

Alternativ leitest du die TIniFile-Klasse ab, und änderst alle Verwendungen.

Vielleicht gibt es auch noch andere Varianten die keinen zu großen Aufwand bedeuten, die mir aber im Moment nicht einfallen.

Der schöne Günther 10. Apr 2017 13:16

AW: Ini-Writes global abfangen?
 
Ich hätte auch direkt die Klassenhelfer genannt, aber du hast schneller getippt ;-)

a.def 10. Apr 2017 13:25

AW: Ini-Writes global abfangen?
 
Die class-helper sind wirklich toll. Danke für den Hinweis.
Nur leider funktioniert das nich mit UpdateFile. Eine Fehlermeldung bekomme ich keine und eine Ini-Datei wird auch nicht geschrieben.

Delphi-Quellcode:
 TIniFileHelper = class helper for TCustomIniFile
 public
  procedure UpdateFile; virtual;
 end;

procedure TIniFileHelper.UpdateFile;
begin
 ShowMessage('X');

 inherited UpdateFile;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
 IniF: TCustomIniFile;
begin

 IniF := TMemIniFile.Create('......');

 try
  IniF.WriteString('section', 'ident', 'value');
 finally
  IniF.UpdateFile;
  IniF.Free;
 end;
end;
'X' wird angezeigt, UpdateFile scheinbar aber nicht ausgeführt (da Datei nicht erstellt wird)

SebastianZ 10. Apr 2017 13:34

AW: Ini-Writes global abfangen?
 
Delphi-Quellcode:
 TIniFileHelper = class helper for TCustomIniFile
 public
  procedure UpdateFile;
 end;

procedure TIniFileHelper.UpdateFile;
begin
 ShowMessage('X');

 inherited UpdateFile;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
 IniF: TCustomIniFile;
begin

 IniF := TMemIniFile.Create('......');

 try
  IniF.WriteString('section', 'ident', 'value');
 finally
  IniF.UpdateFile;
  IniF.Free;
 end;
end;
Spontan hätte ich jetzt das "virtual" im Classhelper als schuldigen in verdacht. Nimm das einfach mal weg, dann sollte es laufen.

a.def 10. Apr 2017 13:37

AW: Ini-Writes global abfangen?
 
Leider kein Glück. Nur ein kleines MemoryLeak am Ende.

Daniel 10. Apr 2017 13:40

AW: Ini-Writes global abfangen?
 
Seit wann kann ein Class-Helper Methoden überschreiben und sich damit in die Vererbungs-Hierarchie einklinken? Soweit ich weiß, lassen sich lediglich neue Methoden hinzufügen.
Würde der Class-Helper also eine Methode "Update2" einführen, könnte aus dieser heraus die originale Methode "UpdateFile" ausgerufen werden. Das ist nicht der transparente Ablauf, der gewünscht ist, aber damit sollte am Ende eine INI-Datei geschrieben worden sein.

Mein Ansatz wäre eine eigene INI-Klasse.

SebastianZ 10. Apr 2017 13:42

AW: Ini-Writes global abfangen?
 
Danke für den Hinweis Daniel, hab nicht mit gedacht -.-
Vorher war auch nicht die Rede vom Custom sondern nur von TIniFile

a.def 10. Apr 2017 13:43

AW: Ini-Writes global abfangen?
 
Mhh jetzt bin ich hin und her gerissen.

Heißt das, dass ich am Ende doch gezwungen bin im ganzen Projekt Anpassungen vorzunehmen?

Ich verwende überall TCustomIniFile und TMemIniFile. Das abzuändern in TMeineIniKlasse wäre ja möglich.
Aber wie würdest du denn dann UpdateFile aufrufe? Ein eigenes UpdateFile was dann das richtige UpdateFile aufruft?

SebastianZ 10. Apr 2017 13:46

AW: Ini-Writes global abfangen?
 
Wenn man schon die Klasse ableitet, spricht aus meiner sicht nichts dagegen auch das UpdateFile zu überschreiben. Die Grundfunktion bleibt ja die gleiche und wird erweitert.

Daniel 10. Apr 2017 13:48

AW: Ini-Writes global abfangen?
 
Naja, mehrere Wege führen nach Rom.
Eine weitere Option wäre ein sog. "Virtual Method Interceptor". Damit kannst Du eine Art Benachrichtigungs-Mechanismus implementieren, der auf Methoden anderer Klassen reagiert.

http://docwiki.embarcadero.com/CodeE...eptor_(Delphi)

Je nachdem, was Dein eigentliches Ziel ist, setzt Du dies auf .UpdateFile oder gleich auf die WriteXX-Methoden an. Diese Lösung ist dahingehend sehr mächtig, Du kannst die Werte der an die eigentliche Methode zu übergebenen Parameter abfragen und ändern oder den Aufruf gänzlich unterbinden.

a.def 10. Apr 2017 13:49

AW: Ini-Writes global abfangen?
 
Um euch aufzuklären:
wenn eine bestimmte Situation im Programm gegeben ist, sollte UpdateFile nichts schreiben dürfen.
Das war mein Hintergedanke. Ich könnte auch vor jedes UpdateFile einfach ein Abfrage schreiben aber das wäre eine echte 0815-Lösung.

Fritzew 10. Apr 2017 14:00

AW: Ini-Writes global abfangen?
 
Warum nicht einfach eine Kopie der System.Inifiles Unit im das Projekt aufnehmen
und die gewünschten Änderungen dort einpflegen?
Dann sind keine Änderungen ansonsten im Code notwendig

a.def 10. Apr 2017 14:02

AW: Ini-Writes global abfangen?
 
Daran habe ich auch schon gedacht. Aber darf ich das so einfach machen?
Leider habe ich "AutoSave" noch nicht (ab Berlin). Das würde ich mir dann in der eigenen Unit nachrüsten.

Fritzew 10. Apr 2017 14:05

AW: Ini-Writes global abfangen?
 
Zitat:

Zitat von a.def (Beitrag 1367003)
Daran habe ich auch schon gedacht. Aber darf ich das so einfach machen?
Leider habe ich "AutoSave" noch nicht (ab Berlin). Das würde ich mir dann in der eigenen Unit nachrüsten.

Klar kannst Du das machen. Solange Du es nicht als Source weitergibst ist das kein Problem

a.def 10. Apr 2017 14:38

AW: Ini-Writes global abfangen?
 
Meine Lösung für alle die es interessiert:

Delphi-Quellcode:
unit classIni;

interface

uses
 SysUtils, IniFiles;

type
 TMemIniFile_ = class(TMemIniFile)

 private
  FAutoSave: Boolean;
 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

{TMemIniFile}

constructor TMemIniFile_.Create(const FileName: string; const Encoding: TEncoding; const AutoSave: Boolean = True);
begin
 inherited Create(FileName, Encoding);
 FAutoSave := AutoSave;
end;

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

 FAutoSave := AutoSave;
end;

destructor TMemIniFile_.Destroy;
begin
 if FAutoSave then
  UpdateFile;

 inherited;
end;

end.
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
 IniF: TCustomIniFile;
begin
 IniF := TMemIniFile_.Create('.....', False);

 try
  IniF.WriteString('section', 'ident', 'value');
 finally
  IniF.Free;
 end;
Info zum Parameter False:
da sollte natürlich nicht einfach nur False stehen, da es nicht wirklich Sinn ergibt.
Mein Problem bestand darin, dass mein Programm keine Ini-Dateien schreiben soll, wenn es im aktuellen Verzeichnis keine Schreibrechte hat.
Das prüft mein Programm beim start und setzt ggf. eine globale Variable (eigene Unit, Record) auf False. Diese Variable übergebe ich dann als zweiten "AutoSave"-Parameter.
Wer mindestens Berlin hat, braucht das nicht.

Gefunden hier: http://www.delphipraxis.net/1352811-post18.html, von Fritzew

stahli 10. Apr 2017 14:49

AW: Ini-Writes global abfangen?
 
Mal eine Konkretisierung von Daniels Vorschlag:

Du könntest eine Unit uMyIniFiles erstellen und TIniFiles "überschreiben".

Delphi-Quellcode:
  TIniFile = class(IniFiles.TIniFile)
  public
    Blocking: Boolean;
    procedure UpdateFile; override;
  end;

...
procedure TIniFile.UpdateFile;
begin
  if not Blocking then
    inherited;
end;
Wenn Du jetzt in allen uses-Blöcken deines Projektes IniFiles durch uMyIniFiles ersetzt kannst Du nun Blocking verwenden und damit UpdateFiles beeinflussen.

Du kannst so eine bestehende Klasse verändern (bei Beibehaltung des Klassennamens), wenn die betreffende Funktionalität überschreibbar ist.

a.def 10. Apr 2017 14:57

AW: Ini-Writes global abfangen?
 
Bei dieser Umsetzung habe ich jedoch eine Frage.
Wie sieht die konkrete Unit aus, wenn ich TMemIniFile benutzen möchte?
Denn eine Deklaration wie myIniFile: TCustomIniFile; funktioniert mit der Unit ja dann nicht mehr.

himitsu 10. Apr 2017 15:37

AW: Ini-Writes global abfangen?
 
PS: UpdateFile hilft nur bei der MemIni.
Die normale INI-API ändert die Datei immer sofort, mit jedem einzelnem WriteXyz-Befehl.

a.def 10. Apr 2017 15:39

AW: Ini-Writes global abfangen?
 
Zitat:

Zitat von himitsu (Beitrag 1367011)
PS: UpdateFile hilft nur bei der MemIni.
Die normale INI-API ändert die Datei immer sofort, mit jedem einzelnem WriteXyz-Befehl.

Das weiß ich. Deswegen benutze ich ja MemIniFile :-D
Aber ich glaube ich sollte überall TCustomIniFile durch TMemIniFile ersetzen. Weil ich benutze TIniFile ja eh nicht.

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;

a.def 12. Apr 2017 09:13

AW: Ini-Writes global abfangen?
 
Zitat:

Zitat von Delphi-Laie (Beitrag 1367244)
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.

Habe das AutoSave ja jetzt eh durch ReadOnly ausgetauscht. Die Funktionalität ist also etwas anders.

Zitat:

Dann sollte die INI das aber auch sagen, wenn sie es nicht will.
Tut mein Programm doch ganz zu Anfang :P

Hier meine aktuelle Unit. Den Parameter "AutoSave" gibt es nicht mehr. Stattdessen werden Write-Befehle nicht ausgeführt, wenn ReadOnly True ist.

Delphi-Quellcode:
unit classIni;

interface

uses
 SysUtils, IniFiles;

type
 TMemIniFile = class(IniFiles.TMemIniFile)

 private
  FModified, FReadOnlyMode: Boolean;
  function IsNumeric(const aString: string; const bAcceptNegativeNumbers: Boolean = True): Boolean;
  procedure FlushModifications;
 public
  constructor Create(const FileName: string; const ReadOnlyMode: Boolean = False); overload;
  constructor Create(const FileName: string; const Encoding: TEncoding; const ReadOnlyMode: Boolean = False); overload;
  destructor Destroy; {* reintroduce; *} override;
  procedure UpdateFile; override;

  procedure WriteString(const Section, Ident, Value: string); reintroduce; overload;
  procedure WriteInteger(const Section, Ident: string; Value: Integer); reintroduce; overload;
  procedure WriteBool(const Section, Ident: string; Value: Boolean); reintroduce; overload;

  function ReadInt64(const Section, Ident: string; Default: Int64): Int64;
  procedure WriteInt64(const Section: string; const Ident: string; Value: Int64);
 end;

implementation

// Helper
// ==============================================================================================================================================
function TMemIniFile.IsNumeric(const aString: string; const bAcceptNegativeNumbers: Boolean = True): Boolean;
var
 bRes: Boolean;
begin
 bRes := StrToInt64Def(aString, 0) = StrToInt64Def(aString, 1);

 if bRes and (not bAcceptNegativeNumbers) and (StrToInt64(aString) < 0) then
  bRes := False;

 Result := bRes;
end;

procedure TMemIniFile.FlushModifications;
begin
 // Flush the modifications to disk
 if (not FReadOnlyMode) and FModified then
  begin
   inherited UpdateFile;

   FModified := False;
  end;
end;
// ==============================================================================================================================================

// Create / Update
// ==============================================================================================================================================
constructor TMemIniFile.Create(const FileName: string; const ReadOnlyMode: Boolean = False);
begin
 inherited Create(FileName);

 FReadOnlyMode := ReadOnlyMode;
 FModified := False;
end;

constructor TMemIniFile.Create(const FileName: string; const Encoding: TEncoding; const ReadOnlyMode: Boolean = False);
begin
 inherited Create(FileName, Encoding);

 FReadOnlyMode := ReadOnlyMode;
 FModified := False;
end;

destructor TMemIniFile.Destroy;
begin
 // If not already flushed to disk, flush modifications now
 FlushModifications;

 inherited Destroy;
end;

procedure TMemIniFile.UpdateFile;
begin
 // Flush the modifications to disk
 FlushModifications;
end;
// ==============================================================================================================================================

// Writes / Reads
// ==============================================================================================================================================
procedure TMemIniFile.WriteString(const Section, Ident, Value: string);
begin
 if not FReadOnlyMode then
  begin
   inherited WriteString(Section, Ident, Value);

   FModified := True;
  end;
end;

procedure TMemIniFile.WriteInteger(const Section, Ident: string; Value: Integer);
begin
 WriteString(Section, Ident, IntToStr(Value));
end;

procedure TMemIniFile.WriteBool(const Section, Ident: string; Value: Boolean);
const
 Values: array [Boolean] of string = ('0', '1');
begin
 WriteString(Section, Ident, Values[Value]);
end;

function TMemIniFile.ReadInt64(const Section: string; const Ident: string; Default: Int64): Int64;
var
 sTmp: string;
begin
 sTmp := ReadString(Section, Ident, SysUtils.IntToStr(Default));

 if IsNumeric(sTmp, True) then
  Result := StrToInt64(sTmp)
 else
  Result := Default;
end;

procedure TMemIniFile.WriteInt64(const Section: string; const Ident: string; Value: Int64);
begin
 WriteString(Section, Ident, SysUtils.IntToStr(Value));
end;
// ==============================================================================================================================================

end.

DeddyH 12. Apr 2017 09:59

AW: Ini-Writes global abfangen?
 
Zitat:

Delphi-Quellcode:
destructor Destroy; reintroduce; override;

Wozu das reintroduce?

a.def 12. Apr 2017 10:01

AW: Ini-Writes global abfangen?
 
War noch ein Überbleibsel von gestern, muss natürlich weg. Gestern hatte ich das Problem, dass der Destruktor gar nicht erst aufgerufen wurde. Ist jetzt aber behoben.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:00 Uhr.

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