2

我在 Delphi 2009 上测试DelphiModbus 库并没有得到我想要的结果。我认为问题在于 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 当然是以字节为单位的缓冲区大小。

我想知道这是否与unicode转换有关?

4

2 回答 2

4

Indy 的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 2009 无关;自从 Delphi 4 引入动态数组以来,它就一直以这种方式工作。并且Move一直如此。

于 2009-03-07T18:02:47.880 回答
1

在我看来,您缺少几个指针取消引用,因此破坏了内存地址。

如果我没记错的话, Move() 调用应该是:

Move(Buffer^, ReceiveBuffer^, iSize);

I've removed my totally worthless post content (leaving it for posterity and to give someone a good laugh).

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.

I made up a contrived example (actually a pretty useless one):

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;

然后我在结束前的最后一行设置了一个断点,然后运行它。当断点被命中时,我使用 ToolTip Evaluation 查看了 ReceiveBuffer 的内容,一切看起来都很好。我可以看到所有正确的值,包括 ReceiveBuffer.Spare 为 7。然后我单步执行,查看 ReceiveBuffer.UnitID;实际上它的值是 8。

但是,按 F9 继续运行(希望能够关闭窗体并结束应用程序),我最终进入 CPU 窗口并从 Vista 收到一条消息,表明应用程序没有响应。我刚刚在 ntdll.DebugBreakPoint、IIRC 之外,单步执行将我带入了 ntdll.RtlReportException。我不太确定发生了什么,但这并不好。还在找。

Edit2:我再次运行它,结果相同。然而,这一次我注意到在我使用 Ctrl+F2 终止应用程序之前,Vista 给了我一个弹出工具托盘窗口,指示“Project1.exe 已关闭”并提到 DEP(我已在这台机器上的硬件中启用)。

于 2009-03-06T18:42:11.520 回答