Delphi 7.0, Delphi 2010 и двоичные файлы

Мое программное обеспечение, разработанное в Delphi 7.0, было обновлено и разработано в Delphi 2010 RAD Studio XE. Он также сохраняет или записывает настройки пользователя в двоичные файлы. Проблема, с которой я сталкиваюсь, заключается в том, что мое приложение или программное обеспечение Delphi 2010 должно считывать двоичные файлы, созданные приложением Delphi 7.0, но приложение Delphi 2010 испытывает проблемы с чтением двоичного файла. Оба этих программного обеспечения являются копиями друг друга.

Я установил опцию Record Field Alignment для проекта на 1 на Delphi 7.0 и байт на 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 2010 (байт, слово, двойное слово, Quad Word), если Delphi 7.0 установлен на 1.

Заранее спасибо.

3 ответа

Решение

Ключ находится в TFileHeader декларация (и, возможно, TUnit а также TUnitType декларация). Вы должны добавить эти определения к вашему вопросу.

Если вы определяете чистую строку в любой из этих записей, вам нужно использовать ShortString вместо String в Delphi 2010, чтобы заставить его работать.

Строка в Delphi 7 = 1 байт на символ.
Строка в Delphi 2010 = 2 байта на символ.

Обновить:

Ваше последнее обновление на самом деле не раскрывает никакой новой информации, но fDefs переменная в ReadUnitFile процедура определяется как array[1..16] of SmallInt;, но в вашем TUnit учебный класс, fDefs является array[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/

Я не знаю ответа, но могу дать вам несколько советов о том, как правильно определить проблему самостоятельно. Как только проблема идентифицирована, решение, как правило, сразу же следует.

Как отладить проблему

Так как вы получаете "read beyond end of линия file" ошибки, вы, очевидно, имеете дело с изменением размера записи. Ряд причин может вызвать это:

  • Выравнивание изменений. Если запись не определена как packed record компилятор предполагает, что он используется только для внутреннего использования, поэтому он может свободно изменять выравнивание. Поскольку ЦП (и ОС) изменились во временном интервале D7 - D2010, разумно ожидать изменений в выравнивании между двумя компиляторами.
  • Изменения размера базового типа данных. 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 модификатор и все вручную добавленные отступы. Запустите код, который отображает размер поля и выравнивание, обратите особое внимание на размер отдельных полей: если у вас есть какие-либо Char поля в вашей записи, вам нужно изменить их на AnsiChar, К счастью, и Delphi 7, и Delphi 2010 знают о AnsiChar и считать его длиной 1 байт.

Отметьте все свои записи, которые вы пишете в файл, с блоками packed ключевое слово в Delphi 7 и 2010.

Другие вопросы по тегам