Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile) (https://www.delphipraxis.net/187850-ini-inhalt-geht-sehr-seltenen-faellen-verloren-tmeminifile.html)

CodeX 8. Jan 2016 17:21

Delphi-Version: XE

Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Ich nutze in meiner Software eine abgeleitete Version von TMemIniFile zum Arbeiten mit Ini-Dateien. Das funktioniert eigentlich auch absolut problemlos. Allerdings gibt es wohl irgendeine Konstellation, die dazu führt, dass der Inhalt der Ini-Datei verloren geht.
Ein Anwender berichtet, dass das bei ihm alle paar Wochen nach dem Neustarten der Fall ist (Software läuft minimiert beim Herunterfahren und startet sich nach der Windowsanmeldung automatisch). Ein zweiter Anwender berichtete, dass dies bei ihm ein Mal passiert ist, als der Rechner während der Verwendung der Software abgestützt ist (hatte nichts mit der Software zu tun und der Ini-Verlust ist nach langer Verwendung erst ein Mal aufgetreten. Da die Datei nach dem Programmstart automatisch erstellt wird, kann ich nicht sagen, ob die ganze Datei weg ist oder "nur" komplett geleert.

Nun bin ich leider etwas ratlos, wie es dazu kommen kann. Da dies so extrem selten auftritt, könnte es sich um ein ganz blödes Timing handeln: TMemIniFile beginnt bei ini.Free den Schreibvorgang (wo aller Inhalt auf ein Mal in die Datei geschrieben wird, also muss sie vermutlich auch erstmal geleert werden), Rechner geht plötzlich aus, ini-Datei leer. Wäre so etwas denkbar oder sollte das TMemIniFile eigentlich verhindern? Gibt es eine Möglichkeit, solche Probleme grundsätzlich zu verhindern? Oder hat jemand andere Ideen, womit es zusammenhängen könnte?

Der schöne Günther 8. Jan 2016 17:38

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Das hatte ich mit TMemIniFile auch einmal. Wenn du dir die Implementierung anschaust dann basiert das auf einer TStringList die erst einmal die gesamte Datei "leert" und dann noch einmal von vorne alles reinschreibt. Wenn dazwischen drin der Saft ausgeht dann hast du eine leere Datei.

Das Abmelden/Herunterfahren/Neustarten finde ich, im Standardfall, bei der VCL eine Katastrophe. Im Regelfall schießt Windows deine Anwendung knallhart ab sobald das Hauptformular zerstört wurde. Ich weiß nicht wie das mit den Dateipuffern ist- Vielleicht übernimmt er nur noch das "leermachen" der Datei, nicht mehr was dort hinein sollte?

himitsu 8. Jan 2016 18:20

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
TMemIniFile schreibt nur in den WindowsFileCache, aber wann das auf der Platte landet, das entscheidet Windows.

Man kann natürlich die TMemIniFile auch so erweitern, daß sie (a) den Cache löscht und (b) notfalls auch noch ein Backup des alten Dateiinhaltes hält, so lange noch geschrieben wird.


Wie groß sind deine INI?

CodeX 8. Jan 2016 22:12

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von himitsu (Beitrag 1326442)
Wie groß sind deine INI?

Unterschiedlich, je nach Verwendung. Sagen wir 10KB - 200KB. Spielt das eine große Rolle?

Zitat:

Zitat von himitsu (Beitrag 1326442)
Man kann natürlich die TMemIniFile auch so erweitern, daß sie (a) den Cache löscht und (b) notfalls auch noch ein Backup des alten Dateiinhaltes hält, so lange noch geschrieben wird.

Ist das ein theoretischer Ansatz oder hast Du das schon irgendwo verwendet/gesehen? Wenn Du da Link oder Codeschnippsel für mich hättest, wäre das toll!

Sir Rufo 8. Jan 2016 22:48

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Grundsätzlich lösen kann man das, wenn man keine Ini-Datei verwendet. :stupid:

Will man die Ini-Datei meiden aber mit
Delphi-Quellcode:
TIniFile
trotzdem arbeiten, dann schreibt man sich einen Ableitung und legt z.B. eine SQlite DB darunter.

Das ist weniger Arbeit als man vermuten mag.

Bjoerk 9. Jan 2016 09:36

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326434)
TMemIniFile beginnt bei ini.Free den Schreibvorgang (wo aller Inhalt auf ein Mal in die Datei geschrieben wird, also muss sie vermutlich auch erstmal geleert werden)..

Das ist nicht richtig. TMemIniFile schreibt lediglich bei UpdateFile.
Delphi-Quellcode:
procedure TMemIniFile.UpdateFile;
var
  List: TStringList;
begin
  List := TStringList.Create;
  try
    GetStrings(List);
    List.SaveToFile(FFileName);
  finally
    List.Free;
  end;
end;

CodeX 9. Jan 2016 14:44

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von Bjoerk (Beitrag 1326483)
Zitat:

Zitat von CodeX (Beitrag 1326434)
TMemIniFile beginnt bei ini.Free den Schreibvorgang (wo aller Inhalt auf ein Mal in die Datei geschrieben wird, also muss sie vermutlich auch erstmal geleert werden)..

Das ist nicht richtig. TMemIniFile schreibt lediglich bei UpdateFile.

Delphi-Quellcode:
destructor TIniFile.Destroy;
begin
  UpdateFile;        // flush changes to disk
  inherited Destroy;
end;

nahpets 9. Jan 2016 15:05

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326434)
Gibt es eine Möglichkeit, solche Probleme grundsätzlich zu verhindern?

Immer dann, wenn was "wichtiges" in die INI reingekommen ist
Delphi-Quellcode:
UpdateFile
aufrufen und nicht nur drauf hoffen, dass es im Destroy automatisch aufgerufen wird.

CodeX 9. Jan 2016 15:15

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von nahpets (Beitrag 1326503)
Immer dann, wenn was "wichtiges" in die INI reingekommen ist
Delphi-Quellcode:
UpdateFile
aufrufen und nicht nur drauf hoffen, dass es im Destroy automatisch aufgerufen wird.

Ich sehe den Zusammenhang zu dem Problem hier nicht. Ich hoffe nicht darauf, dass irgendetwas beim Destroy aufgerufen wird, sondern habe festgestellt, dass es offensichtlich in seltenen Fällen dazu kommen kann, dass der gesamte Ini-Inhalt verloren gehen kann, wenn das Freigeben der Ini unterbrochen wird.

notAssertor 9. Jan 2016 15:51

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Aus der HLP-Datei von Delphi 7 Personal kopiert:

----------------
Das INI-Dateiformat wird noch häufig eingesetzt, besonders in Konfigurationsdateien (z.B. auch in DSK-Dateien). Das Format ist insbesondere für plattformübergreifende Anwendungen nützlich, da die Benutzung einer Systemregistrierung für Konfigurationsdaten nicht immer möglich ist. BaseCLX macht mit den beiden Klassen TIniFile und TMemIniFile das Lesen und Schreiben von INI-Dateien sehr einfach.

TIniFile arbeitet direkt mit der INI-Datei auf der Festplatte, während TMemIniFile alle Änderungen im Speicher puffert und erst nach Aufruf der Methode UpdateFile in die Datei schreibt.

Bei der Instantiierung eines TIniFile- oder TMemIniFile-Objekts übergeben Sie den Namen der INI-Datei als Parameter an den Konstruktor. Ist die Datei nicht vorhanden, wird sie automatisch erstellt. Anschließend können Sie Werte mit einer der Lesemethoden (ReadString, ReadDate, ReadInteger oder ReadBool) abrufen. Das Lesen eines ganzen Abschnitts ist mit der Methode ReadSection möglich. Geschrieben wird mit den Methoden WriteBool, WriteInteger, WriteDate oder WriteString.
----------------

Die fette rote Hervorhebung stammt von mir.

Mir scheint ein beherztes UpdateFile möglich zu sein :cheers:

Was spricht denn in neueren Delphis dagegen, das UpdateFile einfach mal reinzuklatschen? :oops:

Frag' ich ja nur ganz naiv mit D7PE :duck: .

mm1256 9. Jan 2016 15:57

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326504)
...offensichtlich in seltenen Fällen dazu kommen kann, dass der gesamte Ini-Inhalt verloren gehen kann, wenn das Freigeben der Ini unterbrochen wird.

Wann bzw. wie wird denn die INI beim Programmende freigegeben? Das scheint mir der erfolgversprechende Ansatzpunkt zu sein.

nahpets 9. Jan 2016 16:01

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326504)
Zitat:

Zitat von nahpets (Beitrag 1326503)
Immer dann, wenn was "wichtiges" in die INI reingekommen ist
Delphi-Quellcode:
UpdateFile
aufrufen und nicht nur drauf hoffen, dass es im Destroy automatisch aufgerufen wird.

Ich sehe den Zusammenhang zu dem Problem hier nicht. Ich hoffe nicht darauf, dass irgendetwas beim Destroy aufgerufen wird, sondern habe festgestellt, dass es offensichtlich in seltenen Fällen dazu kommen kann, dass der gesamte Ini-Inhalt verloren gehen kann, wenn das Freigeben der Ini unterbrochen wird.

Richtig und deshalb mein Vorschlag, zwischendurch UpdateFile aufzurufen, sonst wird es nur im Destroy aufgerufen und es ist nicht absolut zwingend sichergestellt, dass Destroy aufgerufen wird (z. B. Windows "killt" das Programm beim Runterfahren, der Anwender beendet es per Taskmanager, aus welch anderem Grund auch immer wird es unsauber beendet, stürzt ab, Windows macht die Grätsche...)
Denn offensichtlich wir ja Destroy in seltenen Fällen nicht aufgerufen und daher geht in ebendiesen seltenen Fällen der Inhalt verloren.

@notAssertor :thumb: - nichts anderes wollte ich mit meinem Post sagen.

Der Aufruf von UpdateFile ist nicht verboten, auch wenn UpdateFile automatisch im Destroy aufgerufen wird.

mm1256 9. Jan 2016 16:40

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Hallo,

aber wenn die Vermutung des TE richtig ist, dass das Problem "während" des Destroy auftritt (Windows schießt die App während des Schreibvorganges beim "OnClose" ab), und das ist aus meiner Sicht auch ziemlich naheliegend, dann hilft ein UpdateFile während des Betriebs auch nicht wirklich. Hier würde nur eine Sicherungskopie helfen.

HolgerX 9. Jan 2016 16:54

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326501)
Zitat:

Zitat von Bjoerk (Beitrag 1326483)
Zitat:

Zitat von CodeX (Beitrag 1326434)
TMemIniFile beginnt bei ini.Free den Schreibvorgang (wo aller Inhalt auf ein Mal in die Datei geschrieben wird, also muss sie vermutlich auch erstmal geleert werden)..

Das ist nicht richtig. TMemIniFile schreibt lediglich bei UpdateFile.

Delphi-Quellcode:
destructor TIniFile.Destroy;
begin
  UpdateFile;        // flush changes to disk
  inherited Destroy;
end;

Hmm..

Was hat das UpdateFile bei Destroy vom TIniFile mit dem Destroy vom TMemIniFile zu tun?

Eigendlich doch nichts...

Also, wenn ein TMemIniFile genutzt wird, muss der Programmierer selber dafür sorgen, das per UpdateFile die Daten 'rechtzeitig' gespeichert werden.
Somit spätestens, wenn eine CanCLose Message von Windows kommt...

Bei Verwendung eines TIniFiles wird das Update bei Destroy selber aufgerufen. Somit kann es hier zu dem Problem kommen, das die Ini-Daten bei einem HardKill nicht gespeichert werden..

Welche Anpassungen am TMemIniFile hat de TE denn gemacht?

Wie bereits geschrieben sollten das UpdateData immer nach einer 'wichtigen' Änderung erfolgen.

Besser ist es, die Konfigurationsparameter in einem eigenen Objekt zu puffern und nur zum Laden / Speichern direct auf die IniFiles zuzugreifen.

(Meine Meinung ;) )

nahpets 9. Jan 2016 17:11

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Ausgehend von den Delphi 7-Quellen zu TMemIniFile hat Holger recht:

Es wird nur gespeichert, wenn UpdateFile aufgerufen wird.

Im Destroy vom TMemFileIni (und auch in keiner anderen Routine) ist ein Speichern enthalten.

Daraus schließe ich jetzt mal ganz naiv:
Egal wie das Programm beendet wird, vor dem Beenden ist sicherzustellen, dass UpdateFile aufgerufen wird.

Ist dies sichergestellt und die Datei ist trotzdem leer oder gar nicht vorhanden, so liegt der Fehler ausserhalb des Programmes.

Dies könnte z. B. sein, wenn sie noch im Cache, aber noch nicht auf der Platte ist, der Rechner aber vorher schon aus ist, Rechner-/Windows-/Programmabsturz.

Nach Holgers Anregung stellt sich für mich jetzt die Frage: Wo wird bisher im Programm auf welchem Weg das Speichern durchgeführt.

Mit näherer Kenntnis dieses Vorganges könnten wir eventuell weiterführende Hilfestellung geben.

Bjoerk 9. Jan 2016 17:11

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
TMemIniFile ist nicht von TIniFile abgeleitet (beide von TCustomInifile). Beim Freigeben von TMemIniFile wird definitiv nicht UpdateFile aufgerufen. Jedenfalls ist das so bis D2007.

notAssertor 9. Jan 2016 17:26

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Und wie wäre es bei einem
Delphi-Quellcode:
UpdateFile
bei der CanClose-Abfrage?

Oh, ich sehe gerade die böse böse böse globale TMemIniFile, in der alles gespeichert wird...

Was ist denn mit dem ehrenwerten Sir Rufo los? Offline? Oder lässt er uns nur noch ein Weilchen spielen :cry:

@Sir Rufo: Grätsch' doch bitte ohne <Tgenericks> rein, damit es auch olle D7PE-Gruftis kapieren können :oops: Danke :thumb:

@Alle: Ruft das Destroy des D7-TMemIniFile eigentlich immernoch das UpdateFile in neueren Delphis auf? Sollte man nicht dort zuerst mal nachschauen?

.

Uwe Raabe 9. Jan 2016 17:51

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von notAssertor (Beitrag 1326518)
@Alle: Ruft das Destroy des D7-TMemIniFile eigentlich immernoch das UpdateFile in neueren Delphis auf? Sollte man nicht dort zuerst mal nachschauen?

In D10 Seattle ruft
Delphi-Quellcode:
TMemInifile.Destroy
jedenfalls immer noch kein
Delphi-Quellcode:
UpdateFile
auf.

himitsu 9. Jan 2016 20:06

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
NTFS reagiert bei Datenfehlern in der Verwaltung ganz nett ... es löscht einfach alles, was es nicht mag und schon ist eine Datei leer, wenn der PC abgestürzt ist.

CodeX 9. Jan 2016 22:31

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von Bjoerk (Beitrag 1326517)
Beim Freigeben von TMemIniFile wird definitiv nicht UpdateFile aufgerufen.

Ihr habt in dem Punkt recht, sorry. :oops:
Ich setze wie gesagt eine von TMemIniFile abgeleitete Variante ein, wo ich vor langer Zeit den Destructor entsprechend angepasst habe, dass ein Free das UpdateFile auslöst, damit man es nicht jedes Mal selbst ausführen muss. Ich nutze das so selbstverständlich, dass ich tatsächlich vergessen hatte, dass dies normalerweise nicht geschieht. Meine Variante hatte ich übrigens vor ein paar Jahren hier mal vorgestellt (MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien).

Selbstverständlich arbeite ich nicht mit einer globalen Ini-Datei, sondern erzeuge sie punktuell und gebe sie auch direkt wieder frei, da wo was gelesen/gespeichert wird.

Beim manuellen Beenden werden ein paar Programmeigenschaften gespeichert (Position, Größe, Ansicht, etc.). Das braucht nicht bei jedem Verschieben eines Fensters gespeichert zu werden. Ich habe allerdings darauf geachtet, dass dies nur beim manuellen Beenden geschieht, nicht jedoch beim Herunterfahren. Ich habe das heute nochmal mit einem ausführlichen Log überprüft.

Allerdings rede ich von Anfang an ja von unerwartetem Beenden. Dabei können in der Anwendung aktive Objekte (ggf. in eigenen Threads) auf die Ini-Datei (schreibend) zugreifen während das Herunterfahren oder ein Windowsabsturz geschieht. Darüber hat die Anwendung keinen Einfluss. Wäre es eine häufige Konstellation, würde ich eine ganz andere Lösung suchen. Aber es passiert in einem von vielen tausend Fällen. Das klingt wenig, aber das ist für genau den Anwender auch schon zu viel, da es keine kleine Fehlermeldung ist, sondern die gesamte Arbeit vernichtet.

Ich suche eine Lösung, damit dieser Fall generell nicht mehr passieren kann. Also sowas wie "ganz oder gar nicht" bzw. "erst schreiben, dann löschen". Aber die manuellen Lösungen, die mir dazu einfallen, laufen auf das Führen von 2 Ini-Dateien hinaus, wo bei jedem Schreibvorgang der Inhalt der einen in die andere geschrieben wird, bevor der Inhalt der ersten gelöscht wird. Aber das klingt für mich nicht sehr performant, was natürlich insbesondere dadurch blöd ist, da es für die meisten Fälle/Anwender noch nie relevant war oder sein wird.

Meine Hoffnung war, dass sich darüber jemand schon mal ausführlich Gedanken gemacht hat und irgendetwas implementiert hat, das dies auf elegante Weise löst. Oder eben einen konkreten Hinweis, wie man das angehen könnte (bzw. irgendwelche Windowsmechanismen dafür nutzen könnte).

Edit: Oder das Problem hat mit dem Schreibvorgang in UpdateFile gar nichts zu tun, sondern hat einen ganz anderen Grund. Ersteres ist ja nur eine theoretische Vermutung, die sich nicht so einfach bzw. schnell überprüfen lässt. Ggf. sowas wie das was himitsu gesagt hat...

Uwe Raabe 9. Jan 2016 23:23

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Du kannst in deiner Ableitung von TMemIniFile das UpdateFile überschreiben. Dort benennst du die aktuelle Ini-Datei um, z.B. in <wasauchimmer>.sav, rufst dann das inherited auf und wenn das sauber zurück kommt, kannst du die sav-Datei löschen.

Beim Create schaust du, ob die INI-Datei existiert (und vielleicht noch, ob sie nicht leer ist). Andernfalls suchst du nach einer sav-Datei und benennst die wieder in .ini um.

CodeX 10. Jan 2016 14:39

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1326535)
Du kannst in deiner Ableitung von TMemIniFile das UpdateFile überschreiben. Dort benennst du die aktuelle Ini-Datei um, z.B. in <wasauchimmer>.sav, rufst dann das inherited auf und wenn das sauber zurück kommt, kannst du die sav-Datei löschen.

Beim Create schaust du, ob die INI-Datei existiert (und vielleicht noch, ob sie nicht leer ist). Andernfalls suchst du nach einer sav-Datei und benennst die wieder in .ini um.

Ja, eine solche Lösung habe ich aktuell im Blick.
Ich hatte nur gehofft, dass es eine elegantere und performanetere Lösung gibt, als mit zwei Dateien zu arbeiten. Irgendwas Richtung Schattenkopien, NTFS-Möglichkeiten, wasauchimmer (wenn ich genau wüsste, was die beste Lösung ist, würde ich nicht fragen :stupid: )

nahpets 10. Jan 2016 15:00

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Die einfachste Methode für zwei Dateien wäre doch, die INI-Datei vor UpdateFile mit
Delphi-Quellcode:
if FileExists(ChangeFileExt(FFileName,'.Save')) then DeleteFile(ChangeFileExt(FFileName,'.Save'));
RenameFile(ChangeFileExt(FFileName,'.Save'), NewName);
umzubenennen und dann

Delphi-Quellcode:
UpdateFile;
auszuführen.

Beim Laden der INI-Datei wird geprüft, ob sie leer ist, wenn ja, wird geprüft, ob es die Umbenannte gibt, wenn ja, wird diese geladen.

Das ist performant und sollte im Bereich von ein paar wenigen Millisekunden ablaufen.

Uwe Raabe 10. Jan 2016 15:08

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von nahpets (Beitrag 1326563)
Die einfachste Methode für zwei Dateien wäre doch, die INI-Datei vor UpdateFile mit
Delphi-Quellcode:
if FileExists(ChangeFileExt(FFileName,'.Save')) then DeleteFile(ChangeFileExt(FFileName,'.Save'));
RenameFile(ChangeFileExt(FFileName,'.Save'), NewName);
umzubenennen und dann

Delphi-Quellcode:
UpdateFile;
auszuführen.

Beim Laden der INI-Datei wird geprüft, ob sie leer ist, wenn ja, wird geprüft, ob es die Umbenannte gibt, wenn ja, wird diese geladen.

Und worin unterscheidet sich das jetzt von meinem Vorschlag :gruebel:

Uwe Raabe 10. Jan 2016 15:12

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326562)
Ich hatte nur gehofft, dass es eine elegantere und performanetere Lösung gibt, als mit zwei Dateien zu arbeiten.

Performanteres als das wirst du kaum finden. Bei UpdateFile wird immer eine neue Datei erzeugt und damit eine eventuell bestehende gelöscht. Das Umbenennen der Datei verbraucht auch kaum Zeit, da es ausschließlich im Verzeichniseintrag stattfindet und die Datei selbst gar nicht angefasst wird.

Und das Schönste: es ist so stumpf einfach, daß man auch nach Jahren noch versteht, was da passiert.

Der schöne Günther 10. Jan 2016 15:18

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Ohne zu prüfen, einfach reingebrabbelt: Ich würde in diesem speziellen Fall bewusst nicht die RTL-Funktionen "RenameFile" oder was auch immer nehmen, sondern direkt die WinApi. Bei MoveFileEx scheint man atomares Verhalten bekommen zu können, sprich: Wenn der Strom genau beim "Umbenennen" der Datei flöten geht hast du garantiert entweder die alte oder die neue Datei, aber keinen Müll.

Siehe auch: https://social.msdn.microsoft.com/Fo...rum=windowssdk

CodeX 10. Jan 2016 15:24

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Spricht etwas dagegen, statt des Umbenennens der Datei bei UpdateFile direkt zu Beginn eine Kopie der aktuellen Datei zu erstellen (immer an der selben Stelle, sodass die Backup-Datei immer überschrieben wird) und anschließend per inherited den normalen Speicherprozess durchzuführen? Das würde jegliche Eventualität umgehen, dass beim Umbenennen etwas schief läuft und erscheint mir nicht Aufwändiger als der andere Weg.

Der schöne Günther 10. Jan 2016 15:38

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Dabei vergisst du dass du dir auch einen "Recovery"-Mechanismus für dieses Backup bauen musst.

Folgendes Szenario:
- UpdateFile() wird aufgerufen
- Du machst deine Kopie
- Die Datei wird geleert
- Es wird angefangen, die Datei zu beschreiben
- Der Strom geht aus

Beim nächsten Einschalten hast du deine leere Datei und ein Backup von dem die Software (noch) nichts weiß.

CodeX 10. Jan 2016 15:41

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Das ist schon klar. :)
Es ging ja erstmal nur um den ersten Teil (der performance-technisch relevantere, da er während der Benutzung ja ggf. sehr oft ausgeführt wird).
Das Wiederherstellen ist imho kein Problem, da nur ein Mal beim Programmstart geprüft werden muss, ob die Datei da ist und nicht leer.

Uwe Raabe 10. Jan 2016 16:13

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326569)
Spricht etwas dagegen, statt des Umbenennens der Datei bei UpdateFile direkt zu Beginn eine Kopie der aktuellen Datei zu erstellen (immer an der selben Stelle, sodass die Backup-Datei immer überschrieben wird) und anschließend per inherited den normalen Speicherprozess durchzuführen?

Ist aber deutlich weniger performant, da das Umbenennen selbst kein Schreiben der Datei erfordert, aber das Kopieren der alten Datei schon. Insbesondere da beim nachfolgenden UpdateFile ja die alte Datei eh überschrieben wird (was minimalst langsamer ist, als wenn die alte Datei gar nicht da wäre).

Im Moment kann ich keinen besseren, schnelleren und sichereren Ansatz erkennen, als den von mir beschriebenen. Die Einwände von Günther bezüglich des MoveFileEx sind hier weniger relevant, da hier ja keine Datei verschoben, sondern lediglich im selben Verzeichnis umbenannt wird. Windows ist hier schon so clever und ändert nur den Eintrag im Directory des Volumes.

Im Normalfall existiert die Backup-Datei ja nicht - beim Create wird also die reguläre INI gelesen. Im erweiterten UpdateFile wird die aktuelle INI in die Backup-Datei umbenannt und dann die neue INI geschrieben (eventuell muss man hier noch den Windows- und/oder Festplatten-Cache bemühen, wenn man auch einen Windows-Absturz überleben will). Die neue INI-Datei ist nun in Ordnung und die Backup-Datei wird gelöscht und wir stehen wieder auf Anfang.

Passiert irgendetwas vor dem Umbenennen, ist die Originaldatei noch da -> OK

Passiert etwas nach dem Umbenennen und vor dem Löschen der Backupdatei, fehlt die neue INI-Datei oder sie ist da und enthält keine Daten (oder Müll) oder sie ist in Ordnung. Daher prüfen wir beim Create, ob die aktuelle INI-Datei gültig ist (z.B. durch einen Sentinel-Eintrag am Ende der Datei). Ist sie in Ordnung, wird eine eventuell vorhandene Backupdatei gelöscht. Andernfalls wird die defekte INI-Datei gelöscht (falls überhaupt vorhanden) und die Backup-Datei in INI umbenannt. Gibt es weder eine INI-Datei noch eine Backup-Datei liegt offenbar eine Neuinstallation vor und es werden die Defaultwerte verwendet.

In allen Fällen wird das einmalige Schreiben in UpdateFile nur durch Umbenennen und/oder Löschen ergänzt - alles recht performante Operationen im Vergleich zum eigentlichen Schreiben der Datei. Ein Performancverlust wird somit kaum messbar sein.

CodeX 10. Jan 2016 16:45

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1326565)
Bei UpdateFile wird immer eine neue Datei erzeugt und damit eine eventuell bestehende gelöscht.

Das Datei-Attribut "Erstellt" zeigt immer den Zeitpunkt des ersten Erstellens. Müsste sich dieser dann nicht jedes Mal ändern, wenn die Datei vollständig neu erzeugt wird?

Grundsätzlich gäbe es ja auch noch die Möglichkeit, komplett auf Datei-Operationen zu verzichten und in UpdateFile per 2xSaveToFile stets in zwei Dateien zu schreiben. Bricht der Schreibvorgang beim ersten Schreiben ab, ist die Original-Datei noch vorhanden. Bricht der Schreibvorgang beim zweiten Schreiben ab, ist die Backupdatei bereits erstellt worden.
Das würde den Aufwand quasi genau verdoppeln. Ob das jetzt viel mehr als die Löschen/Umbenennen/Erstellen-Varianten sind, mag ich nicht zu urteilen. Auf jeden Fall können sich dann keine Probleme durch Dateioperationen ergeben.

Uwe Raabe 10. Jan 2016 16:57

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326574)
Das würde den Aufwand quasi genau verdoppeln. Ob das jetzt viel mehr als die Löschen/Umbenennen/Erstellen-Varianten sind, mag ich nicht zu urteilen.

Ich schon, aber das hängt natürlich stark davon ab, was du "viel mehr" nennst und wie groß die INI-Datei ist. Für den Benutzer wird das bei einer handlichen INI-Datei nicht bemerkbar sein. Bei einem Speedtest wirst du einen Faktor von knapp 2 messen können.

Zitat:

Zitat von CodeX (Beitrag 1326574)
Auf jeden Fall können sich dann keine Probleme durch Dateioperationen ergeben.

Na, dann...

nahpets 11. Jan 2016 12:59

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1326564)
Zitat:

Zitat von nahpets (Beitrag 1326563)
Die einfachste Methode für zwei Dateien wäre doch, die INI-Datei vor UpdateFile mit
Delphi-Quellcode:
if FileExists(ChangeFileExt(FFileName,'.Save')) then DeleteFile(ChangeFileExt(FFileName,'.Save'));
RenameFile(ChangeFileExt(FFileName,'.Save'), NewName);
umzubenennen und dann

Delphi-Quellcode:
UpdateFile;
auszuführen.

Beim Laden der INI-Datei wird geprüft, ob sie leer ist, wenn ja, wird geprüft, ob es die Umbenannte gibt, wenn ja, wird diese geladen.

Und worin unterscheidet sich das jetzt von meinem Vorschlag :gruebel:

Garnicht, hatte nur den Eindruck, dass Dein Vorschlag nicht so ganz vorstanden worden ist, und dahinter ein deutlich komplexerer Lösungsansatz vermutet wurde.

Uwe Raabe 11. Jan 2016 13:14

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von nahpets (Beitrag 1326638)
hatte nur den Eindruck, dass Dein Vorschlag nicht so ganz vorstanden worden ist, und dahinter ein deutlich komplexerer Lösungsansatz vermutet wurde.

Den Eindruck habe ich mittlerweile aber auch...

nahpets 11. Jan 2016 14:40

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Hab' mal so zum Spass 'nen INI-Historienkrimskram zusammengedaddelt:
Delphi-Quellcode:
const
  ciMaxKopieen       = 10; // Anzahl der maximal anzulegenden Sicherungskopieen.
  ciMindestIniGroesse = 21; // Die Ini-Datei muss eine Mindestgröße haben,
                            // unterhalb dieser Größe fehlen auf jeden Fall
                            // irgendwelche Einträge...
                            // Sei der Mindestinhalt der INI-Datei
                            // [Section]
                            // Ident=
                            // so muss ihre Größe mindestens 21 Byte sein.

function GetFileSize(const sFile: String): Int64;
var
  fFile : THandle;
  wfd  : TWIN32FINDDATA;
begin
  Result := 0;
  if not SysUtils.FileExists(sFile) then exit;
  fFile := Windows.FindFirstfile(pchar(sFile),wfd);
  if fFile = INVALID_HANDLE_VALUE then exit;
  Result := (wfd.nFileSizeHigh * (MAXDWORD)) + wfd.nFileSizeLow;
  Windows.FindClose(fFile);
  // Hier könnte man jetzt noch weitere Plausibilitätsprüfungen für
  // die INI-Datei machen...
end;

procedure Irgendwas;
var
          i        : Integer;
          ini      : TMemIniFile;
          sIniFile : String;
          sBakFileA : String;
          sBakFileB : String;
begin
  // Beim Programmstart:
  // Den Namen der INI-Datei festlegen.
  sIniFile := ChangeFileExt(Application.ExeName,'.ini');
  // Fehlt diese Datei oder ist ihre Größe kleiner der Mindestdateigröße?
  if not FileExists(sIniFile) or (GetFileSize(sIniFile) < ciMindestIniGroesse) then begin
    // Prüfen, ob wir eine der letzten 10 Sicherungskopien finden...
    for i := 1 to ciMaxKopieen do begin
      // Name der Sicherungskopie i erstellen.
      sBakFileA := Format('%s.%.3d',[sIniFile,i]);
      // Gibt es sie und ist sie größer/gleich der Mindestdateigröße?
      if FileExists(sBakFileA) and (GetFileSize(sBakFileA) >= ciMindestIniGroesse) then begin
        // Sicherungskopie umbennen auf den eigentlich gewünschten Dateinamen.
        RenameFile(sBakFileA,sIniFile);
        // Schleife verlassen.
        break;
      end;
    end;
  end;
  // Ini-Datei laden...
  ini := TMemIniFile.Create(sIniFile);

  // ... Weitere Programmlogik ...
  ini.WriteString('Section','Ident',sBakFileA);
  // ...

  // Beim Programmende:
  // (eventuell) vorhandene Sicherungskopieen löschen bzw. umbennen,
  // so dass wir immer eine Historie von maximal ciMaxKopieen Ini-Dateien haben.
  for i := ciMaxKopieen downto 2 do begin
    // Name der Sicherungskopie i und i - 1 erstellen.
    sBakFileA := Format('%s.%.3d',[sIniFile,i]);
    sBakFileB := Format('%s.%.3d',[sIniFile,i - 1]);
    // Sicherungskopie mit der höheren Nr. in der Endung löschen.
    DeleteFile(sBakFileA);
    // vorhergehende Sicherungskopie umbenennen.
    RenameFile(sBakFileB,sBakFileA);
  end;
  // Dateinamen für die erste Sicherungskopie erstellen
  sBakFileA := Format('%s.%.3d',[sIniFile,1]);
  // und die letzte INI-Datei in die erste Sicherungskopie umbenennen.
  RenameFile(sIniFile,sBakFileA);
  // Ini-Datei speichern.
  ini.UpdateFile;
  // und Schluss ist.
  ini.Free;
end;
Sinnvollerweise baut man sich 'nen Nachfolger von TMemIniFile und gibt diesem Nachfolger statt der Konstanten entsprechende Attribute.

Den "Kram" vom Programmstart übernehme man in das überschriebene Create, den "Kram" zum Programmende ins überschriebene Destroy oder zusammen mit UpdateFile in eine eigene Routine, die man auch separat zu wichtigen Zeitpunkten aufrufen kann und zusätzlich im Destroy aufruft.

CodeX 11. Jan 2016 20:31

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1326641)
Zitat:

Zitat von nahpets (Beitrag 1326638)
hatte nur den Eindruck, dass Dein Vorschlag nicht so ganz vorstanden worden ist, und dahinter ein deutlich komplexerer Lösungsansatz vermutet wurde.

Den Eindruck habe ich mittlerweile aber auch...

Hm, sind meine Bedenken wirklich so daneben? Immerhin versuche ich hier ein Problem zu lösen, das eigentlich gar nicht auftreten dürfte, es aber wohl aufgrund ganz blöder Umstände doch tun kann. Der Vorschlag mit dem Umbenennen beinhaltet aber ja wiederum einen für mich intransparenten Zustand: Was passiert während des Umbenennens? Wenn dieser Befehl intern aus mehreren Teilschritten besteht und der Prozess genau dazwischen Abbricht, kann die Datei dann ja auch wieder verloren gehen. Ich sage nicht dass das so ist, aber zumindest sehe ich hier eine (für mich) Unbekannte.

Sir Rufo 11. Jan 2016 20:42

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Eine Aktion kann eben erfolgreich oder nicht erfolgreich sein - liegt halt im Wesen einer Aktion.

Habe ich mehrere Aktionen, die voneinander abhängig sind und zudem noch wie hier sich gegenseitig beeinflussen, dann sollte man das mit einer Transaktion absichern.

NTFS unterstützt solche Transaktionen.

Innerhalb so einer Transaktion kannst du Dateien löschen, Dateien erzeugen, Dateien ändern und erst wenn du die Transaktion mit einem Commit abschließt, werden diese Änderungen festgeschrieben.

Tritt irgendein unvorhergesehenes Ereignis auf (Programm-Absturz, Rechner aus, ...) passiert einfach nichts.

Also genau das was du suchst :)

notAssertor 11. Jan 2016 20:54

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Wichtige Daten sollte man in Stein meißeln lassen. Sollte der Steinmetz beim Einhämmern versterben, hat man Pech gehabt!

Falls während einer zentral wichtigen Millisekunde der Rechner abschmiert, hat man halt die 100%ige Datensicherheit knapp verfehlt!

Wie wäre es denn mit einer Vollkaskoversicherungen gegen umweltbedingten Polleneinflug beim Einatmen, die Nasenreizung zuverlässig verhindert oder entschädigt?

OMG - SCNR :oops:

Uwe Raabe 11. Jan 2016 21:02

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von CodeX (Beitrag 1326675)
Was passiert während des Umbenennens? Wenn dieser Befehl intern aus mehreren Teilschritten besteht und der Prozess genau dazwischen Abbricht, kann die Datei dann ja auch wieder verloren gehen.

Ich kann es leider nicht direkt durch einen Artikel im MSDN belegen, aber ich hatte schon mehrfach erwähnt, daß das Umbenennen einer Datei innerhalb desselben Verzeichnisses lediglich den Verzeichniseintrag verändert. Der korrekte Ausdruck dafür ist MetaData Operation, welche unter NTFS als sicher gelten.

Zitat:

That said, note that NTFS does have recovery at the metadata level - in other words, updates concerning file system metadata are always atomic. The NTFS metadata will not become corrupted during a sudden reboot
siehe hier

Anhand der weiter oben beschriebenen Vorgehensweise existiert zum Zeitpunkt des Umbenennens die Sicherungsdatei nicht (wurde spätestens beim Create gelöscht). Wenn man aber sicher gehen will, kann man sie auch noch vor dem Umbennenen löschen. Das ist insofern unkritisch, weil die Original-INI ja noch existiert (soll ja gleich umbenannt werden). Wenn die Zieldatei nicht existiert, ist das simple Rename (wohlgemerkt unter NTFS und im selben Verzeichnis!) atomar.

Bei FAT-Systemen kann das allerdings schon wieder anders aussehen.

Uwe Raabe 11. Jan 2016 21:05

AW: Ini-Inhalt geht in sehr seltenen Fällen verloren (TMemIniFile)
 
Zitat:

Zitat von Sir Rufo (Beitrag 1326676)
NTFS unterstützt solche Transaktionen.

Wäre da nicht dieser Hinweis (TxF = Transactional NTFS):
Zitat:

TxF may not be available in future versions of Microsoft Windows


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:51 Uhr.
Seite 1 von 2  1 2      

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