delphi 7.0,delphi 2010和二进制文件

时间:2011-05-03 19:15:09

标签: delphi delphi-7 binaryfiles

我在Delphi 7.0下开发的软件已在Delphi 2010 RAD Studio XE下升级和开发。它还将用户的设置保存或写入二进制文件。我遇到的问题是我的Delphi 2010应用程序或软件应该读取由Delphi 7.0应用程序创建的二进制文件,但Delphi 2010应用程序在读取二进制文件时遇到问题。这两个软件都是彼此的副本。

我确实将项目的Record Field Alignment选项设置为Delphi 7.0上的1和Delphi 2010上的字节。

当软件读取二进制文件时,我总是遇到异常“读取超出行尾”。

我通过执行以下操作来读取和写入二进制文件。

写入二进制文件:

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;

从二进制文件中读取:

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;

更新 以下是用户定义的数据类型:

  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;

我有什么遗漏或不太了解吗?

我忘记提到的一件事是,Delphi 2010应用程序读取和写入二进制文件就好了而不对代码进行任何更改,但只有在读取Delphi 7.0创建的文件时才会出现问题。应用

显然,这个问题与记录字段对齐有关。那么,如果将Delphi 7.0设置为1,我应该在Delphi 2010(字节,Word,双字,四字)上设置它。

提前致谢。

3 个答案:

答案 0 :(得分:4)

密钥位于TFileHeader声明中(可能是TUnitTUnitType声明)。您应该将这些定义添加到您的问题中。

如果您在其中任何一个记录中定义纯字符串,则需要在Delphi 2010中使用ShortString而不是String来使其正常工作。

Delphi 7中的字符串=每个字符1个字节 Delphi 2010中的字符串=每个字符2个字节。

更新

您的上次更新并未真正显示任何新信息,但fDefs过程中的ReadUnitFile变量定义为array[1..16] of SmallInt;,但在TUnit类中,fDefsarray[1..20] of SmallInt;

似乎没有使用fDefs变量,但这可能是导致EOL错误的差异?

无论如何,您应该将这样的数组定义为通用Type,以确保它们可以作为兼容类型传递并相互分配(例如,作为方法中的参数)。

然而是更新:

错误不在字符串中,而是在TObject的大小中。

在Delphi 2009中,TObject将大小从4个字节增加到8个字节 前4个字节是指向对象VMT的指针(因为它已经存在了很长时间),在最后4个字节(IIRC)中,可以包含对同步监视器的引用。

看看这篇好文章:http://blogs.teamb.com/craigstuntz/2009/03/25/38138/

答案 1 :(得分:0)

在Delphi 7和2010中用packed关键字标记您写入文件的所有记录。

答案 2 :(得分:0)

我不知道答案,但我可以就如何正确识别问题给你一些指示。一旦发现问题,解决方案通常会立即跟进。

如何调试问题

由于您收到"read beyond end of file"错误,您显然正在处理更改记录大小。许多事情都可能导致:

  • 对齐更改。除非记录定义为packed record,否则编译器会假定它仅在内部使用,因此可以自由更改它的对齐方式。由于CPU(和OS')在D7 - D2010时间范围内改变了分配,因此可以预期两个编译器之间的对齐方式会发生变化。
  • 基础数据类型大小更改。 Delphi 2009改为Unicode,因此D2009 +(包括D2010)有两个字节的Chars。如果您的记录包含Char数据,则可能导致记录大小发生变化。

回到调试。您可以编写一些简单的代码来显示记录本身的大小,每个字段的大小以及每个字段的偏移量。从D7和D2010运行此代码,记下所有数字并解决可能的差异:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type

  TTestRecord = record
    IntField: Integer;
    ShortStrField1: string[6];
    ShortStrField2: string[5];
    D: Double;
  end;

var R: TTestRecord;

begin
  WriteLn('TTestRecord size: ', SizeOf(R));
  WriteLn('IntField size: ', SizeOf(R.IntField), ', offset: ', Integer(@R.IntField) - Integer(@R));
  WriteLn('ShortStrField1 size: ', SizeOf(R.ShortStrField1), ', offset: ', Integer(@R.ShortStrField1) - Integer(@R));
  WriteLn('ShortStrField2 size: ', SizeOf(R.ShortStrField2), ', offset: ', Integer(@R.ShortStrField2) - Integer(@R));
  WriteLn('D size: ', SizeOf(R.D), ', offset: ', Integer(@R.D) - Integer(@R));
  ReadLn;
end.

获得此信息后,您可以更改记录,使其在Delphi 7和Delphi 2010上看起来都一样。我将从Delphi 7平台开始;我首先将定义更改为packed record,然后在记录中添加额外的填充字节以保持字段偏移。新类型定义如下所示:

TTestRecord = packed record
  IntField: Integer;
  ShortStrField1: string[6];
  ShortStrField2: string[5];
  _DummyPadding: array[0..6] of Byte; // Added to fix D's alignment after adding "packed"
  D: Double;
end;

完成此操作后,转到Delphi 2010,保留packed修饰符和所有手动添加的填充。运行显示字段大小和对齐的代码,特别注意各个字段的大小:如果您的记录中有任何Char字段,则需要将其更改为AnsiChar 。幸运的是,Delphi 7和Delphi 2010都知道AnsiChar并认为它长1个字节。