AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

TStrings.SaveToFileSafety

Ein Thema von himitsu · begonnen am 28. Jun 2010 · letzter Beitrag vom 28. Jun 2010
 
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.338 Beiträge
 
Delphi 12 Athens
 
#1

TStrings.SaveToFileSafety

  Alt 28. Jun 2010, 11:45
Wenn man über SaveToFile z.B. etwas speichert und beim Speichern etwas passiert (z.B. irgendeine Exception), dann sind alle Daten futsch.

Sowohl die Originaldatei, wenn vorher noch eine alte gleichnamige Datei existierte, sowohl eventuell auch die aktuellen Daten.

Diese Funktion legt daher eine temporäre Datei an, speichert darin die Stringliste und tauscht im Nachhinein, wenn alles Erfolgreich war, die Dateien erst aus.

Als Bonus bleibt die Originaldatei sogar noch erhalten.
Ein "normales" SaveToFile überschreibt ja die alten Daten unwiederruflich.
Dabei bleibt das alte Original entweder im Papierkorb erhalten oder es ließe sich eine Weile lang über entsprechende Datenrettungstools aus dem Dateisystem wiederherstellen.
(gelöschte Dateien werden ja nicht sofort im Dateisystem entfernt/überschrieben)

Delphi-Quellcode:
Uses RTLConsts, ShellAPI;

Type TStringsSafetyHelper = Class Helper for TStrings
    Procedure SaveToFileSafety(Const FileName: String;
      BackupToRecycler: Boolean = False); Overload;
    Procedure SaveToFileSafety(Const FileName: String; Encoding: TEncoding = nil;
      BackupToRecycler: Boolean = False); Overload;
  End;

Procedure TStringsSafetyHelper.SaveToFileSafety(Const FileName: String;
    BackupToRecycler: Boolean = False);

  Begin
    SaveToFileSafety(FileName, nil, BackupToRecycler);
  End;

Procedure TStringsSafetyHelper.SaveToFileSafety(Const FileName: String; Encoding: TEncoding = nil;
    BackupToRecycler: Boolean = False);

  Var H, Hs: THandle;
    i: Integer;
    Stream: TStream;
    SHFile: TSHFileOpStruct;

  Begin
    Hs := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
    If Hs <> INVALID_HANDLE_VALUE Then Begin
      H := INVALID_HANDLE_VALUE;
      Try
        i := -1;
        Try
          Repeat
            Inc(i);
            H := CreateFile(PChar(Format('%s.%d', [FileName, i])), GENERIC_READ or GENERIC_WRITE,
              0, nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, Hs);
          Until (H <> INVALID_HANDLE_VALUE) or (i >= $FFFF);
          If H = INVALID_HANDLE_VALUE Then
            Raise EFCreateError.CreateResFmt(@SFCreateErrorEx, [ExpandFileName(FileName), SysErrorMessage(GetLastError)]);
          Stream := THandleStream.Create(H);
          Try
            SaveToStream(Stream, Encoding);
          Finally
            Stream.Free;
          End;
          FlushFileBuffers(H);
          If BackupToRecycler Then Begin
            SHFile.Wnd := 0;
            SHFile.wFunc := FO_DELETE;
            SHFile.pFrom := PChar(FileName + #0);
            SHFile.pTo := nil;
            SHFile.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_NOERRORUI or FOF_NO_UI or FOF_SILENT;
            If not SHFileOperation(SHFile) Then
              Raise EFCreateError.CreateResFmt(@SFCreateErrorEx, [ExpandFileName(FileName), SysErrorMessage(GetLastError)]);
          End Else If not Windows.DeleteFile(PChar(FileName)) Then
            Raise EFCreateError.CreateResFmt(@SFCreateErrorEx, [ExpandFileName(FileName), SysErrorMessage(GetLastError)]);
          If not MoveFile(PChar(Format('%s.%d', [FileName, i])), PChar(FileName)) Then Begin
            i := -1;
            Raise EFCreateError.CreateResFmt(@SFCreateErrorEx, [ExpandFileName(FileName), SysErrorMessage(GetLastError)]);
          End;
        Except
          If i <> -1 Then Windows.DeleteFile(PChar(Format('%s.%d', [FileName, i])));
          Raise;
        End;
      Finally
        CloseHandle(H);
      End;
    End Else SaveToFile(FileName, Encoding);
  End;
So wie die Funktion ist, läßt sie sich nur ab Delphi 2009 nutzen,
aber indem man einfach nur den Encoding-Parameter entfernt, würde es bis runter zu Delphi 2006 / Turbo Delphi funktionieren.
Und für noch ältere Delphi-Versionen muß man sich "nur" eine eigene Klasse/Funktion aus diesem Class-Helper basteln.

Die ganze Speicherroutine liese sich auch auf andere Dateispeicherungen anwenden, nicht nur für Stringlisten.
Dafür müßte man dann halt nur die Speicherroutinen des Objektes anpassen/austauschen und z.B. einen passenden Class-Helper erstellen.
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (28. Jun 2010 um 12:02 Uhr)
  Mit Zitat antworten Zitat
 


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:03 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