Delphi 7 Buffer Pointer Overlay Has Different Data - delphi

I define a large buffer and pointer as an Array of Byte in Delphi 7.
This is the buffer for the Win32 ReadEventLog call.
I define a second pointer as a record structure for the buffer data.
This is an overlay to add structure to an array of bytes.
Problem: data at the two pointers is different.
Buffer^ data is not the same as RecordPtr^ data.
The addresses of both pointers are the same.
Question: what is the correct method of declaring an overlay pointer?
Type
TEventRecord = record
Length : dword;
Reserved : dword;
RecordNumber : dword;
end;
TByteArray = array of byte;
Var
Buffer : TByteArray;
RecordPtr : ^TEventRecord = #Buffer;
MyData : TEventRecord;
begin
Setlength (Buffer, $FFF0);
// ReadEventLog....
// Watch of Buffer^ and RecordPtr^ shows different data
end.

Dynamic array Buffer: array of byte is a pointer, so #Buffer is pointer to pointer.
To make per-byte access to the contents of TEventRecord, you can assign pointer to the beginning of byte array data:
type
PEventRecord = ^TEventRecord;
var
PEventRecord_Var: PEventRecord;
begin
PEventRecord_Var := #Buffer[0];

Why dont u do it like this?
Type
TEventRecord = record
Length : dword;
Reserved : dword;
RecordNumber : dword;
end;
TEventLog = array of TEventRecord ;
Var
Buffer : TEventLog;
begin
Setlength (Buffer, $FFF0);
// ReadEventLog(#buffer[0],...)
end.

You could do it like this:
type
TEventRecord = record
Length: DWORD;
Reserved: DWORD;
RecordNumber: DWORD;
end;
TByteArray = array of Byte;
Var
Buffer: TByteArray;
RecordPtr: ^TEventRecord = Pointer(Buffer); // Buffer is a pointer
MyData: TEventRecord;
begin
SetLength(Buffer, $FFF0); { Assuming that is big enough }
{ ReadEventLog(..., Pointer(Buffer), Length(Buffer), .... }
{ Watch of Buffer and RecordPtr^ shows same data. }
end.
Buffer is a pointer, and it points to the first of a (big) number of bytes. If you want to use this buffer to let ReadEventLog load into a byte array, simply cast Buffer to a Pointer and assign it to RecordPtr.
But...
if I understand you well, an alternative is:
Buffer: array of TEventRecord;
...
SetLength(Buffer, 6000); { 6000 event records! }
if ReadEventLog(..., Pointer(Buffer), Length(Buffer) * SizeOf(Buffer[0]), ...) then ...
Now you can easily access each event record from the Buffer. Why use a Byte array, if, instead, you can easily read an array of event records directly?
(The above would work for normal records, but I was told by #BruceGavin that event records have a variable size tail, so that won't work)

You can achieve what you are looking for like this :
Var
Buffer : TByteArray;
RecordPtr : ^TEventRecord absolute Buffer;
This make both variable occupy the same 4 bytes in memory (or 8 bytes in 64 bits).

Related

Typecasting dynamic array over static one in Delphi

Q: Is it safe to cast dynamic array over a static one ?
type
Bytearray = array of byte;
function calcCRC(buf1: pointer) : dword;
var
buf: ByteArray ;
outbuffer : array [1..30] of byte;
begin
buf := bytearray(buf1^); // <- is it safe ?
outbuffer[1] := buf[0];
end;
procedure test;
var
testarr : array [1..30] of byte ;
begin
calccrc(#testarr);
end ;
Such snippet compiles and works correctly, even in quite large program with FastMM4. But something tells me, that it can be dangerous ...
Any advice ?
I guess you need to revert your logic. Casting a static array into a dynamic array is not safe at all (it has a counter and reference count before the actual data), whereas the opposite - casting a dynamic array into a static array pointer - is perfectly safe, if you take care of the buffer length.
Your initial code will trigger some random GPF (Access Violations) due to the fact that the reference counter will be modified in calcCRC() in some hidden code - look at the ASM using F2: there is more than you may expect, especially a call DynArrayClear which is pretty unsafe. You didn't have any problem yet because of what was in your memory. But if e.g. a 1 integer is stored before the data, it would trigger a GPF, since leaving calcCRC() would let the Delphi RTL try to release the dynamic array instance.
If you expect to access bytes of memory using indexes, you need to use a pointer to a static array, not a dynamic array.
The code could be the following:
Type
TByteDynArray = array of byte ;
TByteArray = array[0 .. (maxInt div sizeof(byte)) - 1] of byte;
PByteArray = ^TByteArray;
function calcCRCptr(buf1: PByteArray; buf1len: integer): dword;
var
outbuffer : array [1..30] of byte;
begin
result := 0;
// ensure you don't create any access violation by using big indexes
if buf1len < 1 then
exit; // avoid GPF
outbuffer[1] := buf1[0];
...
end;
function calcCRCdynarray(const buf1: TByteDynArray): dword;
begin
// you can use length(buf1) to get the number of items/bytes
result := calcCRCptr(pointer(buf1), length(buf1));
end;
procedure test ;
var
testdynarr: TByteDynArray;
teststaticarray: array[0..10] of byte;
begin
Setlength(testdynarr, 100);
calccrcdynarray(testdynarr) ; // safe
calccrcptr(pointer(testdynarr), length(testdynarr)); // direct call
calccrcptr(#teststaticarray, 11); // OK
end;
Also ensure that you don't mess with pointers, e.g. that you name your variables with proper Delphi convention about values (T...) and pointers (P....). Also follow the same convention to distinguish static arrays and dynamic arrays in your code.
This approach is certainly not safe. The static array has a different memory layout from the dynamic array. The dynamic array has metadata containing reference count and length. You might get away with it in this short excerpt but it is not a technique to be recommended.
Whatever the problem is, this is not the solution. Possible solutions might involve working with open arrays or pointers to byte.

How to convert a null terminated string to string?

I want to store to disk some data records. I want to have each data record of equal size so I can quickly compute and jump to a certain record.
So, I store the only string in my record as an array of chars:
type TFileNameIO = array[1..512] of Char;
After I read the string from disk, the content of my string is like this:
c:\windows#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 etc
I want to truncate the #0 characters, and I do something like this:
function GetFileName(DiskName: TFileNameIO): string;
VAR PC: PChar;
begin
SetString(Result, PChar(#DiskName), Length(DiskName)); <----- here Result is: c:\windows#0#0#0 etc
PC:= PChar(Result);
Result:= WideCharToString(PC);
end;
There is a better way to do it?
You can assign a null-terminated PChar directly to a String:
function GetFileName(DiskName: TFileNameIO): string;
begin
Result := PChar(#DiskName);
end;
Just make sure the TDiskName always contains a null terminator. If it can ever be completely full of characters without a null, you will have to do this instead:
function GetFileName(DiskName: TFileNameIO): string;
var
Len, I: Integer;
begin
Len := 0;
For I := Low(DiskName) to High(DiskName) do
begin
if DiskName[I] = #0 then Break;
Inc(Len);
end;
SetString(Result, PChar(#DiskName), Len);
end;
Your function is not necessary because you can simply assign a character array to a string variable.
type
TCharArray = array [1..512] of Char;
....
var
arr: TCharArray;
str: string;
....
arr := ...;
str := arr;
This results in str being assigned the contents of arr. The compiler implements this with a call to System._UStrFromWArray which first of all looks for a null-terminator. If it finds one, that determines the length of str. Otherwise, the entire contents of the array are copied, and Length(str)=Length(arr).
As a broader point, I would recommend that you change to using zero-based character arrays. From the documentation:
Zero-based character arrays are compatible with PChar and PWideChar. When you use a character array in place of a pointer value, the compiler converts the array to a pointer constant whose value corresponds to the address of the first element of the array.
This makes it easier for you to interop with functions that expect PChar arguments.
As an aside, passing TFileNameIO by value is inefficient because it involves a memory copy. Pass such types by reference. For instance as a const parameter.

Pass static byte array in parameter in Delphi

I have a static array:
myArray: array [0..15] of byte;
I wish to pass this array (not a copy of the array) into a function. Inside the function, I will manipulate the data in array.
Different lengths of STATIC byte array may be passed in to Init at different times. Therefore, I do not wish to declare Init to be able to receive only a certain length of byte array. (e.g. in this case it is a 16 bytes. At other times it may be 65000 bytes)
This is the way I used the function:
Init(#myArray[0]); //passing in pointer to static array
I tried to declare Init like this:
procedure Init(x: array of byte);
begin
//Do some stuff with x, for e.g.
Length(x); //I do not intend to pass in the length as a parameter,
//but determine it in this function itself.
end;
Is this also a correct way of declaring the Init function parameter?
Instead of Init(#myArray[0]) use Init(myArray). Delphi will transfer the myArray by reference
You declared x as open array of bytes. The open array parameters are type compatible with the array variables with the same element type. Delphi will transfer fixed array length as a hidden parameters. You can access it with Length(x) and e.g. for i := Low(x) to High(x) do.
Open array parameters cannot be set to nil. If you need to set x to nil, then you have to declare x as pointer to Byte or pointer to array[..] of Byte.
For static arrays there is no information about their length at runtime, but only at compile time.
So if you loose the type information at passing a static array to a function, there is no way to find out the length of the array within the function.
=> If you want to use static arrays with variable sizes, you must pass the size as additional parameter.
The clean way would be to work with pointer aritmetic rather than arrays here.
PByte = ^byte;
procedure Init(arr: PByte; len: Integer);
var
i: Integer;
begin
//Write 0, 1, 2 ... to arr
for i:= 0 to len-1 do
begin
arr^:= i;
Inc(arr);
end;
end;
Unclean solution:
TByteArray = array [0..$FFFFFFFF] of Byte;
PByteArray = ^TByteArray;
procedure Init(arr: PByteArray; len: Integer);
begin
arr^[0]:= 0;
arr^[1]:= 1;
...
end;
...
var
myAray: array[0..10] of byte;
begin
Init(PByteArray(#myArray[0]), 10);
end;
First, declaring
procedure Init(x: array of byte);
declare an open array parameter (not a dynamic array, unlike some other stated).
To do what you want, you need to declare like this :
type
TMyArray: array [0..15] of byte;
var
MyArray : TMyArray;
Procedure Init(var X : TMyArray)
Still, I'm not sure why you try to set the variable to nil. A static array variable is a little bit like a record, you can't set it to nil. And IF you pass a pointer to the function instead, you will need to assign your array to a variable first anyway(or allocate the memory directly in the function). Like this :
type
TMyArray : Array[0..15] of byte;
pTMyArray : ^TMyArray;
Procedure Init(var X : pTMyArray);
procedure DoSomething;
var P : pTMyArray;
begin
New(P); //or if you array is global, simply do "P := #MyArray"
try
//code here
Init(P);
//code here
finally
Dispose(P);
end;
end
But that would be a bad construct IMO, because if you set your array pointer to nil inside the Init function, you will get a memory leak. So this is pretty bad to start with. But I guess if it's your Init function that reserve the memory that would be OK.
If you settle for an open array parameter, I'd suggest using the Low and High to determine the bounds of the array.
I think that's the best info I can give you until you give more detail on what you are trying to achieve.
EDIT
Ok, if you need the function to accept an array of different size, you need an open array parameter.
You did it right in your exemple. But you can't set it to nil within the function.
I don't think it's possible to pass to a function a pointer to an open array type. If you really want a pointer to set it to nil, you will need to pass it as PByteArray... But you will need to also pass the size of the array.
first when your are declarating this
procedure Init(x: array of byte);
you are using a dynamic array open array parameter instead of a static array.
to pass a static array as a parameter , you must define a type
like this
type
Arr15 = array [0..15] of byte;
and then declare the procedure like this, using the keyword var to pass by reference.
procedure Init(var x: Arr15);
about the nil assign , you cannot set to nil a static array.
another alternative is use a dynamic array
check this sample
type
ByteArray = array of byte;
procedure Init(var x : ByteArray);
const
LenArray=15;
var
i : Byte;
begin
SetLength(x,LenArray); //alocate the memory and set the length of the array.
for i:=0 to LenArray-1 do //fill the array
x[i]:=i;
Writeln(Length(x)); //show the len of the array
SetLength(x,0); //now dealocates de memory
Writeln(Length(x)); //show the len of the array again
end;
Var
myArray : ByteArray;
begin
try
Init(myArray);
Readln;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.

Convert Byte Array to Integer in Delphi

type B02 = array [01..02] of byte ;
...
var b : B02;
...
//here i read from tcp socket
socket.ReadBuffer(b, 2);
The question is: how to convert B02 to an integer?
You could declare a Word/Smallint at the same memory location, like this:
var
b : B02;
myInt: smallint absolute B02;
Then again, is there any particular reason why you don't just create the smallint and pass it to ReadBuffer instead of an array? I don't know exactly what class you're using, but that looks a lot like the way you read from a TStream, and it'll accept variables of any type, along with a size in bytes. Why not just declare your buffer as the integer type you're looking for and cut out the middleman?
You can just cast it:
var
a: array[01..02] of Byte;
i: Integer;
begin
i := PWORD(#a)^;
end;
or if you need to change the byte order:
i := Swap(PWORD(#a)^);
If the data is being transmitted in "network" order (highest byte first) and not in "Intel" order (lowest byte first), you can do some byte shufling yourself.
uses
SysUtils;
var
b: B02;
w: word; //two bytes represent a word, not an integer
socket.ReadBuffer(b, 2);
WordRec(w).Hi := b[1];
WordRec(w).Lo := b[2];
Mghie suggested following approach in comments (and I agree with him):
uses Winsock;
var
w: word;
socket.ReadBuffer(w, 2);
w := ntohs(w);

How to assign multiple values to an Open or Fixed-size array?

I want to do the following,but I get errors:
procedure JumpToCodeCave(CurrentLocation:DWORD;Destination:Pointer;out_JmpBack:Pointer);
var calc:DWORD;
jmppatch:Array[0..3] of byte absolute calc;
Buffer:Array[0..9] of byte;
begin
calc := (Cardinal(Destination) - $5)-(CurrentLocation + $4);
Buffer := [$90,$90,$90,$90,$E9,jmppatch,$90]; //<< Error here <<
WriteProcessmemory(Handle,Pointer(CurrentLocation),Pointer(Buffer),10,nil);
out_JmpBack^ := Currentlocation + $A;
end;
Buffer should look like this:
0x90,0x90,0x90,0xE9,jmppatch[0],jmppatch[1],jmppatch[2],jmppatch[3],0x90
The function calculates the value that should be written to jump from one address(current) to another address(CodeCave).The result is converted into bytes and written into the process,but I can't put the bytes in the Buffer like I did above.
I'm sorry for the stupid question,but I have forgotten Delphi after I began my education with C#.
Delphi doesn't support array literals like that, especially not ones that would accept a four-byte value and turn it into four one-byte values.
You can have array constants, as Kcats's answer demonstrates. You can also have open-array literals, but then you can only pass it to a function expecting an open-array parameter.
I'd do something different, in your case. Code is not just an array of bytes. It has structure, so I'd make a record and give it fields for each of the instructions in the code.
type
TPatch = packed record
Nops: array [0..3] of Byte;
JmpInst: packed record
Opcode: Byte;
Offset: LongWord;
end;
Nop: Byte;
end;
const
Nop = $90;
Jmp = $e9;
var
Buffer: TPatch;
begin
// nop; nop; nop; nop;
FillChar(Buffer.Nops, SizeOf(Buffer.Nops), Nop);
// jmp xxxx
Buffer.JmpInst.Opcode := Jmp;
Buffer.JmpInst.Offset := LongWord(Destination) - SizeOf(Buffer.JmpInst)
- (CurrentLocation + SizeOf(Buffer.Nops));
// nop
Buffer.Nop := Nop;
WriteProcessmemory(Handle, Ptr(CurrentLocation), #Buffer, SizeOf(Buffer), nil);
end;
Even if you don't do all that, note that I've changed the third parameter of WriteProcessMemory. Your Buffer variable is not a pointer, so you really can't type-cast it to be one. You need to pass the address.
There's no way to assign the way you want to. Instead, use the Move() and FillMemory() procedures:
FillMemory(#Buffer[0], 4, $90);
Buffer[4] := $E9;
Move(Calc, Buffer[5], 4);
Buffer[9] := $90;
Note that I have removed the absolute variable, as it's no longer necessary.
You can't do that. Try something like:
var
Buffer:Array[0..9] of byte = ($90,$90,$90,$90,$E9,$CC,$CC,$CC,$CC,$90);
begin
PCardinal(#buffer[5])^ := (Cardinal(Destination) - $5)-(CurrentLocation + $4);

Resources