[英]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
声明(也可能是TUnit
和TUnitType
声明)。 您应该将这些定义添加到您的问题中。
如果您在其中任一记录中定义纯字符串,则需要使用ShortString
而不是 Delphi 2010 中的String
才能使其工作。
Delphi 7 中的字符串 = 每个字符 1 个字节。
Delphi 2010 中的字符串 = 每个字符 2 个字节。
您的最后一次更新并没有真正揭示任何新信息,但ReadUnitFile
过程中的fDefs
变量被定义为array[1..16] of SmallInt;
,但在您的TUnit
class 中, fDefs
是array[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"
错误,因此您显然正在处理更改记录大小。 许多事情可能导致:
packed record
,否则编译器假定它仅在内部使用,因此可以自由更改它的 alignment。 由于 CPU(和操作系统)在 D7 - D2010 时间范围内更改了分配,因此可以合理地预期两个编译器之间 alignment 的更改。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.