Entwickler-Ecke

Dateizugriff - D2009: ReadBuffer + SizeOf-Problem nach Umstieg von D2007


AScomp - Mi 11.03.09 12:27
Titel: D2009: ReadBuffer + SizeOf-Problem nach Umstieg von D2007
Hallo zusammen,

habe mal wieder ein Konvertierungsproblem. Und zwar habe ich zwei Funktionen, mit denen ich gespeicherte Daten laden bzw. Daten speichere (Dateiname und Dateidatum werden aus einem Record in eine Datei geschrieben).

Hier mal zur Veranschaulichung der Sourcecode:


Delphi-Quelltext
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:
type
  THeader = record
    DataCount: DWord;
    Reserved: DWord;
  end;
  PLogRec = ^TLogRec;
  TLogRec = record
    szPath: array[0..MAX_PATH] of Char;
    lTime: Longint;
  end;
  TLogData = array of TLogRec;

function LoadLogData(const AFilename: Stringvar LogData: TLogData): Boolean;
var
   Header: THeader;
   FS: TFileStream;
   I: Integer;
begin
     result := false;
     if not FileExists(AFilename) then
        exit;
     FS := TFileStream.Create(AFilename, fmOpenRead or fmShareDenyNone);
     try
        if FS.Size < SizeOf(THeader) then
           exit;
        FS.ReadBuffer(Header, SizeOf(THeader));
        SetLength(LogData, Header.DataCount);
        for I := 0 to Header.DataCount - 1 do
            FS.ReadBuffer(LogData[I], SizeOf(TLogRec));
     finally
        FS.Free;
     end;
end;

function SaveLogData(const AFilename: Stringvar LogData: TLogData): Boolean;
var
   Header: THeader;
   FS: TFileStream;
   I: Longint;
begin
     result := False;
     if LogData = nil then
        exit;
     QuickSort(LogData);
     FS := TFileStream.Create(AFilename, fmCreate or fmShareDenyNone);
     try
        Header.DataCount := Length(LogData);
        FS.WriteBuffer(Header, SizeOf(THeader));
        for I := Low(LogData) to High(LogData) do
            FS.WriteBuffer(LogData[I], SizeOf(TLogRec));
        result := True;
     finally
        FS.Free;
     end;
end;


Die Logdateien alter Programmversionen, die mit Delphi 2007 programmiert worden sind, sollten natürlich genauso geladen werden können wie neu geschriebene Dateien.

Das Problem ist jetzt folgendes: In Zeile 29

Delphi-Quelltext
1:
FS.ReadBuffer(LogData[I], SizeOf(TLogRec));                    

gibt es eine Exception (EReadError). Nicht beim ersten Durchlauf, sondern ich schätze relativ am Ende. Der Header wird noch korrekt gelesen, danach aber nichts mehr (die Dateinamen in LogData enthalten keine korrekten Daten) - ich vermute ein Problem mit der Länge (Unicode).

Viele Grüße,

Andy

Moderiert von user profile iconNarses: Delphi-Tags hinzugefügt


Delete - Mi 11.03.09 16:07

Ich habe den Code nur kurz überflogen, aber möglicherweise liegt das Problem hier:

Delphi-Quelltext
1:
2:
3:
4:
TLogRec = record
    szPath: array[0..MAX_PATH] of Char;
    lTime: Longint;
  end;


Wenn Du szPath mal als array of AnsiChar definierst, geht es dann?


AScomp - Mi 11.03.09 17:05

Hab's jetzt nicht getestet, aber selbst wenn es gehen würde: Die Logdatei könnte dann wieder keine Dateien mit Unicode-Zeichen verwalten. Dies ist aber dringend notwendig.

Hast du sonst noch eine Idee?


jaenicke - Mi 11.03.09 18:03

user profile iconAScomp hat folgendes geschrieben Zum zitierten Posting springen:
Hab's jetzt nicht getestet, aber selbst wenn es gehen würde: Die Logdatei könnte dann wieder keine Dateien mit Unicode-Zeichen verwalten. Dies ist aber dringend notwendig.
Du widersprichst dir...
user profile iconAScomp hat folgendes geschrieben Zum zitierten Posting springen:
Die Logdateien alter Programmversionen, die mit Delphi 2007 programmiert worden sind, sollten natürlich genauso geladen werden können wie neu geschriebene Dateien.
Die alten Dateien enthalten Buchstaben mit jeweils einem Byte pro Buchstabe (Char = 1 Byte bei Delphi <= 2007), die neuen Buchstaben mit 2 Byte (Char = 2 Byte bei Delphi 2009).

Damit hat dein TLogRec jeweils eine unterschiedliche Größe. Entweder speicherst du das mit ab oder die Kompatibilität wird nicht funktionieren. Wenn du dies mit abspeicherst, dann kannst du für das alte Format von Delphi 2007 AnsiChar und für das neue unter Delphi 2009 Char benutzen. Das neue kann dann aber von Delphi <= 2007 nicht gelesen werden, es sei denn mit Zusatzbibliotheken.


AScomp - Mi 11.03.09 18:30

Zitat:
Du widersprichst dir...


Nö. ;-)

Leider wird das nicht mit abgespeichert. Gibt es keine Möglichkeit zu ermitteln, ob es sich um einen Ansi- oder einen Unicode-Record handelt, der in der Datei abgelegt ist?

Oder alternativ: Wie kann man ermitteln, ob eine Datei im Ansi-Format oder im Unicode-Format gespeichert wurde?


jaenicke - Mi 11.03.09 18:50

user profile iconAScomp hat folgendes geschrieben Zum zitierten Posting springen:
Leider wird das nicht mit abgespeichert. Gibt es keine Möglichkeit zu ermitteln, ob es sich um einen Ansi- oder einen Unicode-Record handelt, der in der Datei abgelegt ist?

Oder alternativ: Wie kann man ermitteln, ob eine Datei im Ansi-Format oder im Unicode-Format gespeichert wurde?
Gar nicht, denn du speicherst das ja selbst ab. Und deshalb gibt es auch keine Signaturen, an denen man das sonst unterscheidet.

Du kannst höchstens versuchen durch eine Division der Dateigröße minus dem Header durch die Datensatzgrößen zu schauen welche Recordgröße glatt aufgeht. Aber sauber ist das nicht und es kann zufällig auch mit beiden klappen.

Die einzige saubere Lösung wäre ein neues Dateiformat für Delphi 2009. Dieses könntest du an einer Signatur im Header identifizieren. Da das neue Format ohnehin nicht mit Delphi 2007 und früher lesbar ist, sollte das auch kein Problem sein.


AScomp - Mi 11.03.09 18:53

Okay, thanks!