繁体   English   中英

delphi 7.0、delphi 2010 和二进制文件

[英]delphi 7.0, delphi 2010 and binary files

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

我确实将项目的记录字段 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 创建的文件时才会出现问题。 应用。

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

提前致谢。

关键在于TFileHeader声明(也可能是TUnitTUnitType声明)。 您应该将这些定义添加到您的问题中。

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

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

更新:

您的最后一次更新并没有真正揭示任何新信息,但ReadUnitFile过程中的fDefs变量被定义为array[1..16] of SmallInt; ,但在您的TUnit class 中, fDefsarray[1..20] of SmallInt; ?

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

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

还有一个更新:

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

在 Delphi 2009 中, TObject将大小从 4 字节增加到 8 字节。
前 4 个字节是指向对象VMT的指针(长期以来一直如此),在最后 4 个字节 (IIRC) 中,可以包含对同步监视器的引用。

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

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

我不知道答案,但我可以为您提供一些有关如何自己正确识别问题的指导。 一旦确定了问题,解决方案通常会立即出现。

如何调试问题

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

  • Alignment 发生变化。 除非记录被定义为packed record ,否则编译器假定它仅在内部使用,因此可以自由更改它的 alignment。 由于 CPU(和操作系统)在 D7 - D2010 时间范围内更改了分配,因此可以合理地预期两个编译器之间 alignment 的更改。
  • 基础数据类型大小更改。 Delphi 2009 更改为 Unicode,所以 D2009+(包括 D2010)有两个字节的字符。 如果您的记录包含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的修饰符和所有手动添加的填充。 运行显示字段大小和 alignment 的代码,特别注意各个字段的大小:如果您的记录中有任何Char字段,则需要将它们更改为AnsiChar 幸运的是 Delphi 7 和 Delphi 2010 都知道AnsiChar并认为它是 1 字节长。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM