2009-03-06 15 views
2

Je suis en train de tester DelphiModbus library sur Delphi 2009 et je ne reçois pas tout à fait les résultats que je veux. Je pense que le problème est lié à la ligne suivante sur IdModbusClient.pas:Delphi 2009 et la mémoire de copie

Move(Buffer, ReceiveBuffer, iSize); 

Il ressemble à ReceiveBuffer est réglé sur des ordures.

tampon est définie comme TIdBytes (à partir de composants Indy)

ReceiveBuffer est défini comme 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 } 

Et iSize est bien sûr la taille de la mémoire tampon en octets.

Je me demande si cela a quelque chose à voir avec la conversion Unicode?

+0

Je vous suggère de toujours utiliser la taille * destination * comme le nombre d'octets à déplacer. Sinon, si pour une raison quelconque quelqu'un peut passer un tampon source plus grand, il peut déborder de votre tampon de destination - avec tous les risques associés. –

Répondre

4

Indy's TIdBytes est un tableau dynamique, défini dans IdGlobal.pas:

type 
    TIdBytes = array of Byte; 

Vous ne pouvez pas passer une variable de ce type directement à Move et attendez-vous à ce qu'il fonctionne car il ne copiera que la référence à quatre octets stockée dans cette variable. (Et si vous l'avez dit à copier plus de quatre octets, il procédera à copier tout ce réside dans la mémoire après cette variable - qui sait quoi.) Compte tenu de ces déclarations:

var 
    Buffer: TIdBytes; 
    ReceiveBuffer: TCommsBuffer; 

La façon d'appeler Move sur ces variables est comme ceci:

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

Il fonctionne comme ça parce que les paramètres de Move sont untyped, vous devez donc passer la valeurque vous souhaitez copier, pas un pointeur ou une référence à la valeur. Le compilateur gère le référencement par lui-même.

Le code est un peu bizarre parce qu'il ressemble vous simplement copier un octet de Buffer, mais ne le laissez pas vous déranger trop. C'est un idiome Delphi; C'est juste comme ça que ça fonctionne.

En outre, cela n'a rien à voir avec Delphi 2009; ça a fonctionné de cette façon depuis Delphi 4, quand les tableaux dynamiques ont été introduits. Et Move a toujours été comme ça.

+0

Merci, cela a du sens. Je me demande si cette bibliothèque a déjà travaillé avec Indy 10, pour laquelle ce code est spécifique. Je vais devoir tester plus tard. – Harriv

1

Il me semble qu'il vous manque quelques déréférences de pointeur, et donc corrompre les adresses de mémoire.

Si je ne me trompe pas, l'appel doit être Move():

Type 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).

+0

Cela ne compile pas, message d'erreur: [Erreur DCC] IdModBusClient.pas (296): E2017 Type de pointeur requis – Harriv

+0

Vous avez absolument raison. Apprends-moi à lire lentement avant de poster. Je suis moins idiot de cette façon. :-( Comment sont définis TModBufDataBuffer et TModBusFunction? –

+0

Ce ne sont que des octets, un octet unique et un tableau J'ai modifié la question d'origine. – Harriv

Questions connexes