Tuesday, July 5, 2011

delphi 7.0, delphi 2010 and binary files.

My software developed under Delphi 7.0 has been upgraded and developed under Delphi 2010 RAD Studio XE. It also saves or writes user's settings into binary files. The problem I am running into is that my Delphi 2010 application or software is expected to read binary files created by Delphi 7.0 application, but Delphi 2010 application has problem reading the binary file. Both of these software are copy of each other.
I did set the Record Field Alignment option for the project at 1 on Delphi 7.0 and byte on Delphi 2010.
I am always running into exception "read beyond end of line," when the software reads the binary file.
I read and write a binary file by doing the following.

Writing into a binary file:
procedure WriteUnitFile;
var
  unitFile: File;
  x:integer;
  FileHeader:TFileHeader;
begin
  if not DemoMode then
  begin
    FileHeader.id := 'UnitFile';
    FileHeader.version := 7;

    if fileexists(basedir+unitfilename) then
      BackupFile(basedir+unitfilename);

    AssignFile(unitFile,baseDir+unitfilename);

    if UnitList.Count > 0 then
    begin
      Rewrite(unitFile,1);
      BlockWrite(unitFile, FileHeader, SizeOf(FileHeader));
      for x := 0 to UnitList.Count - 1 do
      begin
        TUnit(UnitList[x]).Write(unitFile);
      end;
    end
    else
      DeleteFile(baseDir+unitfilename);

    CloseFile(unitFile);
  end;
end;
 
Reading from a binary file:
procedure ReadUnitFile;
var
  unitFile:File;
  newUnit,simUnit:TUnit;
  ut,TypeCard:TUnitType;
  New_Ver_File:TFileHeader;
  Address:SmallInt;
  State:TUnitState;
  SubAddress:SmallInt;
  MyfDefs:array[1..20] of SmallInt;
  fDefs:array[1..16] of SmallInt;
begin

  if FileExists(baseDir + unitfilename) then
  begin
    oneUnit := false;
    AssignFile(unitFile,baseDir+unitfilename);
    AssignFile(newUnitFile,baseDir+Dummyfilename);
    Reset(unitFile,1);

    BlockRead(unitFile,New_Ver_File,SizeOf(New_Ver_File));

    Reset(UnitFile,1);
    BlockRead(UnitFile,New_Ver_File,SizeOf(New_Ver_File));

    while not Eof(UnitFile) do
    begin
      BlockRead(UnitFile,ut,SizeOf(ut));
      case ut of
        tutSimulator:newUnit := TSimulator.Create;
        tutDX2202:newUnit := TDX2202.Create;
        tutDX8884CS:newUnit:=TDX8884CS.Create;
        tutDX8F44F: newUnit := TDX8F44F.Create;
        tutDX0008:newUnit := TDX0008.Create;
        tutDX0800:newUnit := TDX0800.Create;
        tutDX8000:newUnit := TDX8000.Create;
        tutDX1000:newUnit := TDX1000.Create;
        tutDX0100:newUnit := TDX0100.Create;
        tutDX4404:newUnit := TDX4404.Create;
        tutDX0020:newUnit := TDX0020.Create;
        tutDX0080:newUnit := TDX0080.Create;        
        tutDX8814:newUnit := TDX8814.Create;
        tutDX8814CS:newUnit := TDX8814CS.Create;
        tutDX8884:newUnit := TDX8884.Create;
        else
                newUnit := TUnit.Create;
      end;
      newUnit.Read(unitFile);
      if DemoMode then
      begin
        if oneUnit = true then
        begin
          simUnit := TSimulator.Create;
          simUnit.Assign(newUnit);
          newUnit.Free;
          newUnit := simUnit;
        end
        else
        begin
          oneUnit := true;
        end;
      end;
      unitList.Add(newUnit);
    end;
    CloseFile(unitfile);
    UnitsDlg.UnitGrid.RowCount := unitList.Count+1;
    if UnitsDlg.UnitGrid.RowCount > 1 then
      UnitsDlg.UnitGrid.FixedRows := 1;
    UnitsDlg.FillIn;
  end
  else
  begin
       UnitsDlg.UnitGrid.RowCount := 1;
  end;
end;
 
UPDATE: Here is the user-defined datatypes:
  TFileHeader = record
    id:string[32];
    version:SmallInt;
  end;
TUnitType = (tutUnused,tutSimulator,tutDX2202,tutDX0008,tutDX0800,tutDX8000,
                 tutDX1000,tutDX4404,tutDX0020,tutDX8814,tutDX8814CS,
                 tutDX8884,tutDX8884CS,tutDX8f44f,tutDX0080,tutDX0100,tutLast);
TUnitState = (tusDisabled,tusEnabled);
TUnit = class(TObject)
  changeLink:TUnit;
  Address: SmallInt;
  SubAddress:SmallInt;
  State:TUnitState;
  uType:TUnitType;
  RegCnt:integer;
  fRegs:array[1..20] of SmallInt;
  fDefs:array[1..20] of SmallInt;
  MsgCnt:LongWord;
  RxCnt:LongWord;
  BreakCnt: LongWord;
  LineErrCnt: LongWord;
  OtherErrCnt:LongWord;
  TimeoutCnt: LongWord;
  BadMsgErrCnt:LongWord;
  XSumErrCnt:LongWord;
  OutsFlag:Boolean;
  CommBits:LongWord;
  OfflineCnt:integer;
  Online:Boolean;
  CurReg:integer;
  selectedonce,usedIP:Boolean;
  LastDigitalOut,LastDigitalIn,
  CurRegOut,umsglen,urmsglen,
  dummycount,Unitlocation,
  CommLocation:integer;
private
public
   followed by list of procedures and functions...
end;
 
Is there something I am missing or not understanding very well?

One thing I did forget to mention is that Delphi 2010 application reads and writes into binary files just fine without making any changes to the code, but it has problem only when reading files created by Delphi 7.0. application.

Obviously, this question is related to Record Field Alignment. So, what should I set it on my Delphi 2010 (Byte, Word, Double Word, Quad Word), if Delphi 7.0 is set at 1.

Thanks in advance.

Answer:
The key is in the TFileHeader declaration (and maybe the TUnit and TUnitType declaration). You should add these definitions to your question.
If you define a pure string in either of these records, you need to use ShortString instead of String in Delphi 2010 to make it work.
String in Delphi 7 = 1 byte per char.
String in Delphi 2010 = 2 bytes per char.

Update:

Your last update doesn't really reveal any new information, but the fDefs variable in the ReadUnitFile procedure is defined as an array[1..16] of SmallInt;, but in your TUnit class, fDefs is an array[1..20] of SmallInt;?

The fDefs variable doesn't seem to be used, but this might be the difference that causes the EOL error?
You should define arrays like this as a common Type, anyway, to ensure that they can be passed around and assigned to each other as compatible types (eg. as parameters in methods).

Yet an update:

The error is not in the strings, but in the Size of TObject.
In Delphi 2009, TObject increased the size from 4 bytes to 8 bytes.
The first 4 bytes is a pointer to the objects VMT (as it has been for a long time), and in the last 4 bytes (IIRC), it is possible to contain a reference to a synchronization monitor.
Have a look at this nice article: http://blogs.teamb.com/craigstuntz/2009/03/25/38138/