delphi 7.0、delphi 2010 和二进制文件

发布于 2024-11-05 07:55:47 字数 4516 浏览 0 评论 0原文

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

我确实将项目的记录字段对齐选项在 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 上设置什么(字节、字、双字、四字)。

提前谢谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

苏辞 2024-11-12 07:55:47

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

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

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

更新:

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

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

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

但更新:

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

在 Delphi 2009 中,TObject 将大小从 4 字节增加到 8 字节。
前 4 个字节是指向对象 VMT 的指针(与之前的情况一样)很长一段时间),并且在最后 4 个字节(IIRC)中,可以包含对同步监视器的引用。

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

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/

忘羡 2024-11-12 07:55:47

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

Mark all your records you write to file in blocks with packed keyword both in Delphi 7 and 2010.

日久见人心 2024-11-12 07:55:47

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

如何调试问题

由于您收到 "read Beyond end of line file" 错误,因此您显然正在处理更改记录大小。有很多因素会导致这种情况:

  • 对齐方式发生变化。除非记录被定义为打包记录,否则编译器会假定它仅在内部使用,因此可以随意更改其对齐方式。由于 CPU(和操作系统)在 D7 - D2010 时间范围内更改了分配,因此可以合理地预期两个编译器之间的对齐方式会发生变化。
  • 基础数据类型大小发生变化。 Delphi 2009 对 Unicode 进行了更改,因此 D2009+(包括 D2010)具有两个字节的 Char。如果您的记录包含 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 平台;我首先将定义更改为打包记录,然后向记录添加额外的填充字节以维护字段偏移量。新的类型定义如下所示:

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 字节长。

I don't know the answer, but I can give you some directions on how to properly identify the problem yourself. Once the problem is identified, the solution would usually immediately follow.

How to debug the issue

Since you're getting "read beyond end of line file" errors, you're obviously dealing with changing record size. A number of things can cause that:

  • Alignment changes. Unless the record is defined as packed record the compiler assumes it's only used internally so it's free to change it's alignment. Since CPU's (and OS'es) changed allot in the D7 - D2010 time frame, is reasonable to expect changes in alignment between the two compilers.
  • Underlying data type size changes. Delphi 2009 made the change to Unicode, so D2009+ (including D2010) has two-bytes Chars. If your record contains Char data, that can cause the change in record size.

Back to debugging. You can write a bit of trivial code that displays the size of the record itself, the size of each of it's fields and the offset for each of the fields. Run this code from both D7 and D2010, make a note of all the numbers and address the possible differences:

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.

Once you get this information you can change the record so it looks the same on both Delphi 7 and Delphi 2010. I'd start on the Delphi 7 platform; I'd first change the definition to packed record and then add extra padding bytes to the record to maintain field offsets. The new type definition would look like this:

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;

Once this is done move to Delphi 2010, keeping the packed modifier and all the manually added padding. Run the code that displays field size and alignment, pay special attention to the size of the individual fields: if you've got any Char fields in your record, you'll need to change them to AnsiChar. Fortunately both Delphi 7 and Delphi 2010 know about AnsiChar and consider it to be 1 byte long.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文