Delphi 2009 and copying memory
-
03-07-2019 - |
Question
I'm testing DelphiModbus library on Delphi 2009 and don't get quite the results I want. I think the problem lies with the following line on IdModbusClient.pas:
Move(Buffer, ReceiveBuffer, iSize);
It looks like ReceiveBuffer is set to some garbage.
Buffer is defined as TIdBytes (from Indy components)
ReceiveBuffer is defined as 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 }
And iSize is of course the size of the Buffer in bytes.
I wonder if this has anything to do with unicode conversion?
Solution
Indy's TIdBytes
type is a dynamic array, defined in IdGlobal.pas:
type
TIdBytes = array of Byte;
You can't pass a variable of that type directly to Move
and expect it to work because it will only copy the four-byte reference stored in that variable. (And if you told it to copy more than four bytes, then it will proceed to copy whatever else resides in memory after that variable — who knows what.) Given these declarations:
var
Buffer: TIdBytes;
ReceiveBuffer: TCommsBuffer;
The way to call Move
on those variables is like this:
if Length(Buffer) > 0 then
Move(Buffer[0], ReceiveBuffer, iSize);
It works like that because Move
's parameters are untyped, so you need to pass the value that you want to copy, not a pointer or reference to the value. The compiler handles the referencing by itself.
The code is a little weird because it looks like you're just copying one byte out of Buffer
, but don't let it bother you too much. It's a Delphi idiom; it's just the way it works.
Also, this has nothing to do with Delphi 2009; it's worked this way ever since Delphi 4, when dynamic arrays were introduced. And Move
has been this way forever.
OTHER TIPS
It looks to me like you're missing a couple of pointer dereferences, and therefore corrupting memory addresses.
If I'm not mistaken, the Move() call should be:
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;
I then set a breakpoint on the last line before the end, and ran it. When the breakpoint was hit, I looked at the contents of ReceiveBuffer using ToolTip Evaluation, and everything looked perfectly fine. I could see all of the proper values, including the ReceiveBuffer.Spare being 7. I then single stepped, and looked at ReceiveBuffer.UnitID; it in fact had a value of 8.
However, pressing F9 to continue running (expecting to be able to just close the form and end the application), I ended up in the CPU window and got a message from Vista that the application wasn't responding. I was just outside ntdll.DebugBreakPoint, IIRC, and single stepping brought me into ntdll.RtlReportException. I'm not quite sure what's happening, but it ain't good. Still looking.
Edit2: I ran it again, with the same results. However, this time I noticed before I used Ctrl+F2 to terminate the app that Vista was giving me a popup tooltray window indicating that "Project1.exe has been closed" and mentioning DEP (which I have enabled in hardware on this machine).