Delphi 7.0, Delphi 2010 и двоичные файлы - PullRequest
0 голосов
/ 03 мая 2011

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

Я установил для параметра «Выравнивание полей записи» для проекта значение 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.application.

Очевидно, этот вопрос относится к выравниванию поля записи.Итак, что я должен установить на моем Delphi 2010 (Байт, Слово, Двойное слово, Четыре слова), если Delphi 7.0 установлен на 1.

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

Ответы [ 3 ]

4 голосов
/ 04 мая 2011

Ключ указан в объявлении 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/

0 голосов
/ 04 мая 2011

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

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

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

  • Изменение выравнивания.Если запись не определена как 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 байт.

0 голосов
/ 04 мая 2011

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

...