Delphi 2009 и копирование памяти - PullRequest
2 голосов
/ 06 марта 2009

Я тестирую Библиотека DelphiModbus на Delphi 2009 и не получаю желаемых результатов Я думаю, что проблема заключается в следующей строке на IdModbusClient.pas:

Move(Buffer, ReceiveBuffer, iSize);

Похоже, что ReceiveBuffer настроен на какую-то фигню.

Буфер определяется как TIdBytes (из компонентов Indy)

ReceiveBuffer определен как TCommsBuffer:

  TModBusFunction = Byte;

  TModBusDataBuffer = array[0..256] of Byte;

  TCommsBuffer = packed record
    TransactionID: Word;
    ProtocolID: Word;
    RecLength: Word;
    UnitID: Byte;
    FunctionCode: TModBusFunction;
    MBPData: TModBusDataBuffer;
    Spare: Byte;
  end; { TCommsBuffer }

И размер iSize, конечно, равен размеру буфера в байтах.

Интересно, это как-то связано с преобразованием юникода?

Ответы [ 2 ]

4 голосов
/ 07 марта 2009

Indy's TIdBytes - это динамический массив, определенный в IdGlobal.pas :

type
  TIdBytes = array of Byte;

Нельзя передать переменную этого типа непосредственно в Move и ожидать, что она будет работать, потому что она скопирует только четырехбайтовую ссылку , хранящуюся в этой переменной. (И если вы сказали ему копировать более четырех байтов, то он продолжит копирование всего, что находится в памяти после этой переменной - кто что знает.) С учетом этих объявлений:

var
  Buffer: TIdBytes;
  ReceiveBuffer: TCommsBuffer;

Способ вызова Move для этих переменных выглядит следующим образом:

if Length(Buffer) > 0 then
  Move(Buffer[0], ReceiveBuffer, iSize);

Это работает так, потому что параметры Move нетипизированы , поэтому вам нужно передать значение , которое вы хотите скопировать, а не указатель или ссылка на значение. Компилятор обрабатывает ссылки самостоятельно.

Код немного странный, потому что он выглядит как , вы просто копируете один байт из Buffer, но не позволяйте ему слишком беспокоить вас. Это идиома Delphi; это просто так работает.

Кроме того, это не имеет ничего общего с Delphi 2009; это работало так с Delphi 4, когда были представлены динамические массивы. И Move был таким всегда.

1 голос
/ 06 марта 2009

Мне кажется, что вы пропустили пару разыменований указателя и, следовательно, портите адреса памяти.

Если я не ошибаюсь, вызов Move () должен быть:

Move(Buffer^, ReceiveBuffer^, iSize); </strike></p> <p>I've removed my totally worthless post content (leaving it for posterity and to give someone a good laugh).</p> <p>I don't see anything that would be affected by Unicode at all. I'm going to edit the tags to include Delphi (without the 2009), as some of the CodeGear Delphi developers are currently posting there. Perhaps one of them can see what's happening.</p> <p>I made up a contrived example (actually a pretty useless one):</p> <pre>uses IdGlobal; type TModBusFunction = Byte; TModBusDataBuffer = array[0..256] of Byte; TCommsBuffer=packed record TransactionID: Word; ProtocolID: Word; RecLength: Word; UnitID: Byte; FunctionCode: TModBusFunction; MBPData: TModBusDataBuffer; Spare: Byte; end; procedure TForm1.FormShow(Sender: TObject); var Buffer: TIdBytes; ReceiveBuffer: TCommsBuffer; //iSize: Word; begin FillChar(ReceiveBuffer, SizeOf(ReceiveBuffer), 0); ReceiveBuffer.TransactionID := 1; ReceiveBuffer.ProtocolID := 2; ReceiveBuffer.RecLength := 3; ReceiveBuffer.UnitID := 4; ReceiveBuffer.FunctionCode := 5; FillChar(ReceiveBuffer.MBPData[0], SizeOf(ReceiveBuffer.MBPData), 6); ReceiveBuffer.Spare := 7; SetLength(Buffer, SizeOf(ReceiveBuffer)); Move(ReceiveBuffer, Buffer, SizeOf(ReceiveBuffer)); Move(Buffer, ReceiveBuffer, SizeOf(ReceiveBuffer)); ReceiveBuffer.UnitID := 8; end;

Затем я установил точку останова в последней строке перед концом и запустил ее. Когда была достигнута точка останова, я просмотрел содержимое ReceiveBuffer с помощью ToolTip Evaluation, и все выглядело идеально. Я мог видеть все правильные значения, включая ReceiveBuffer.Spare равным 7. Затем я пошагово и посмотрел на ReceiveBuffer.UnitID; на самом деле он имел значение 8.

Однако, нажав F9 для продолжения работы (ожидая, что можно будет просто закрыть форму и завершить приложение), я оказался в окне ЦП и получил сообщение от Vista, что приложение не отвечает. Я был только за пределами ntdll. DebugBreakPoint, IIRC, и один шаг привел меня в ntdll.RtlReportException. Я не совсем уверен, что происходит, но это не хорошо. Все еще смотрю.

Edit2: я запустил его снова, с теми же результатами. Тем не менее, на этот раз я заметил, что прежде чем использовать Ctrl + F2 для завершения работы приложения, Vista открыла мне всплывающее окно с треем инструментов, указывающее, что «Project1.exe был закрыт», и упоминание DEP (которое я включил в аппаратном обеспечении на этом компьютере).

...