Reputation: 35532
In C++ it's done like that:
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 is a byte array used as a receive buffer after a packet is received over TCP. That code puts data in an instance of tPacket (a structure) so I can access the structure later.
How is it done in Delphi?
Upvotes: 5
Views: 3673
Reputation: 15548
You can also use the absolute keyword to force both structures to share the same memory address:
var
Data: array[1..SizeOf(TMyStruct)] of byte;
s : TMyStruct absolute Data;
Any data written to S is also available as Data without having to perform a move or pointer casting.
Upvotes: 4
Reputation: 163357
You can do in Delphi exactly what you would do in C++. Define a named type for the record pointer, though, since Delphi doesn't let you define types in statements.
type
PPacket = ^TPacket;
var
packet: PPacket;
packet := PPacket(@data[0]);
The reason I've used @data[0]
is that it works regardless of whether data is a dynamic array. If it's a dynamic array, then data
is really a pointer to the first byte of the packet, so this would work:
packet := PPacket(data); // for dynamic array only
But if data
is not a dynamic array, then that type-cast won't be valid. You'd instead need to type-cast a pointer to data
, like this:
packet := PPacket(@data); // for static array only
That won't work if it's a dynamic array. The first code will work in both cases. But if you have range checking enabled (and you probably should), then I think the first code will raise an exception if data
is a zero-length dynamic array, so be careful.
If you want to go the C# route instead, where you copy the bytes from the array into a separate TPacket
variable, then I'd use this:
var
packet: TPacket;
// Source param comes first. Params are passed by
// reference automatically.
Move(data[0], packet, SizeOf(packet));
You'll need to ensure that data
contains enough bytes to fill an entire TPacket
value. TPacket
had better not contain any compiler-managed types, like string
, Variant
, IUnknown
, or dynamic-array types. Neither pointer type-casting nor Move
behave well when that's the case.
Upvotes: 3
Reputation: 10582
This can be tricky as you have to make sure your source and destination structure layouts are identical. If they are, then you have 2 choices, either use a pointer to the Data array, or use a memory copy:
Pointer:
type
// Declare a pointer type for your struct.
PMyStruct = TMyStruct^;
...
var
ptr: PMyStruct;
begin
ptr := PMyStruct(Cardinal(@Data));
// use ptr...
end;
Memory copy:
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;
Upvotes: 2