2009-05-18 7 views
5

In C zuweisen ++ es wie das erledigt ist:Wie ein byte [] zu einem Datensatz

tPacket * packet = (tPacket *)data; //data is byte[] array; tPacket is a structure 

In C#:

tPacket t = new tPacket(); 
GCHandle pin = GCHandle.Alloc(data, GCHandleType.Pinned); 
t = (tPacket)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(tPacket)); 
pin.free(); 

Data ist ein Byte-Array als Puffer nach einem Paket erhalten verwendet wird über TCP empfangen. Dieser Code stellt Daten in eine Instanz von tPacket (eine Struktur), damit ich später auf die Struktur zugreifen kann.

Wie ist es in Delphi getan?

+0

Ich hoffe, Ihr C++ tPacket ist ein POD (keine virtuellen Methoden) und enthält keine Zeiger oder Refrences. Und ich hoffe, dass Ihr C# tPacket ein Werttyp ist, der nur aus Werttypen besteht (die nur aus Werttypen bestehen, ... also gibt es in Ihrem Objektdiagramm keinen Referenztyp). – mmmmmmmm

Antwort

4

können Sie verwenden auch das absolute Schlüsselwort beiden Strukturen zu zwingen, die gleiche Speicheradresse zu teilen:

var 
    Data: array[1..SizeOf(TMyStruct)] of byte; 
    s : TMyStruct absolute Data; 

Irgendwelche zu S i geschriebenen Daten s auch als Data verfügbar, ohne dass ein Move oder Pointer Casting ausgeführt werden muss.

+0

Natürlich wird das nicht funktionieren, da dynamische Arrays Referenztypen sind. Das Array muss nicht dynamisch sein. –

+0

Ich deklariere Daten nicht als dynamisches Array. Beim Aufrufen von Recv() - Daten hat Größe 8192 Bytes. :) Danke, es hat funktioniert. –

+0

Nun, es ist schön, dass es für dich funktioniert hat, aber könnte die Antwort bitte zu etwas Brauchbarem bearbeitet werden? Auch der absolute Trick mag für einfache Fälle wie diesen gut sein, aber die idiomatische Art, S als Daten ohne Verschiebung oder Typumwandlung zur Verfügung zu haben, wäre die Verwendung eines Variantensatzes, ähnlich einer C-Union. – mghie

2

Dies kann schwierig sein, da Sie sicherstellen müssen, dass Ihre Quell- und Zielstrukturlayouts identisch sind. Wenn sie sind, dann haben Sie die Wahl zwischen 2, entweder einen Zeiger auf das Array Daten verwenden oder eine Speicherkopie verwenden:

Zeiger:

type 
    // Declare a pointer type for your struct. 
    PMyStruct = TMyStruct^; 

... 

var 
    ptr: PMyStruct; 
begin 
    ptr := PMyStruct(Cardinal(@Data)); 
    // use ptr... 
end; 

Speicherkopie:

var 
    Data: array of Byte; 
    s: TMyStruct; 
begin 
    // fill Data... 
    if SizeOf(s) <> Length(Data) then 
    raise Exception.Create('Input size is not the same size as destination structure.'); 
    CopyMemory(@s, @Data, Length(Data)); 
    // use s... 
end; 
+0

Das Problem ist, dass in der C++/C# -Code ist es nicht identisch. Die Struktur hat Größe 10 Bytes und die Paketgröße könnte über 2000 Bytes. –

+0

Verwenden Sie einfach das erste Beispiel, dann die Zeigermethode. Es ist identisch mit dem C++ - Code. –

+0

Persönlich würde ich zu @data [0] schreiben, da Sie wahrscheinlich den Zeigerwert nicht überschreiben möchten. –

3

Sie können in Delphi genau das tun, was Sie in C++ tun würden. Definieren Sie jedoch einen benannten Typ für den Datensatzzeiger, da Sie mit Delphi keine Typen in Anweisungen definieren können.

type 
    PPacket = ^TPacket; 
var 
    packet: PPacket; 

packet := PPacket(@data[0]); 
  • Der Grund, warum ich @data[0] verwendet habe, ist, dass es funktioniert unabhängig davon, ob Daten ein dynamisches Array ist. Wenn es sich um ein dynamisches Array ist, dann ist data wirklich ein Zeiger auf das erste Byte des Pakets, so dass dies funktionieren würde:

    packet := PPacket(data); // for dynamic array only 
    

    Aber wenn data ist nicht ein dynamisches Array, dann wird diese Art Guss gewonnen‘ t sei gültig.

    packet := PPacket(@data); // for static array only 
    

    Das wird nicht funktionieren, wenn es sich um ein dynamisches Array ist: Sie würden stattdessen einen Zeiger zu data, wie diese benötigen geben Guss. Der erste Code wird in beiden Fällen funktionieren. Aber wenn Sie Bereichsprüfung aktiviert haben (und Sie sollten wahrscheinlich), dann denke ich, der erste Code wird eine Ausnahme auslösen, wenn data ist eine Null-Länge dynamische Array, also sei vorsichtig.


Wenn Sie die C# route stattdessen gehen wollen, wo Sie die Bytes aus dem Array in eine separate TPacket Variable zu kopieren, dann würde ich diese verwenden:

var 
    packet: TPacket; 

// Source param comes first. Params are passed by 
// reference automatically. 
Move(data[0], packet, SizeOf(packet)); 

Sie‘ Ich muss sicherstellen, dass data genug Bytes enthält, um einen gesamten Wert zu füllen.TPacket sollte besser keine vom Compiler verwalteten Typen wie string, Variant, IUnknown oder dynamische Array-Typen enthalten. Weder Zeiger-Casting noch verhalten sich gut, wenn das der Fall ist.

Verwandte Themen