iRecords variable no change after execute DbiWriteBlock function - delphi

The value of iRecords variable no change after execute DbiWriteBlock function. Please explain this to me. Thanks!
This is my code:
procedure TMainForm.btnBDICheckClick(Sender: TObject);
var
Table : TTable;
PTable : PByte;
RecordSize : Integer;
RecordCount : Integer;
iRecords : Integer;
begin
Table := TTable.Create(Self);
Table.DatabaseName := 'D:\Temp';
Table.TableName := 'SrcTable.db';
Table.Active := True;
RecordSize := Table.RecordSize;
RecordCount := Table.RecordCount;
PTable := nil;
iRecords := 0;
GetMem(PTable, RecordSize * RecordCount);
DbiWriteBlock(Table.Handle, iRecords, PTable);
// iRecords = 0 at here
Table.Close;
end;

Variable iRecords is a pointer to the number of records to be written. On output, iRecords will have the actual number of records written. Your code should look like this:
procedure TMainForm.btnBDICheckClick(Sender: TObject);
var
Table : TTable;
PTable : PByte;
RecordSize : Integer;
RecordCount : Integer;
iRecords : Integer;
begin
Table := TTable.Create(Self);
Table.DatabaseName := 'D:\Temp';
Table.TableName := 'SrcTable.db';
Table.Active := True;
RecordSize := Table.RecordSize;
RecordCount := Table.RecordCount;
//PTable := nil;
//iRecords := 0;
iRecords := RecordCount;
GetMem(PTable, RecordSize * RecordCount);
DbiWriteBlock(Table.Handle, iRecords, PTable);
Table.Close;
ShowMessage('Records: ' + IntToStr(iRecords));
end;
With this code you will add empty records. Use DbiInitRecord() and DbiPutField() to fill field values.
Below is the documentation about the DbiWriteBlock function (from BDE help file):
Function definition:
function DbiWriteBlock (hCursor: hDBICur; var iRecords: Longint; pBuf: Pointer): DBIResult stdcall;
Description:
DbiWriteBlock writes a block of records to the table associated with
hCursor.
Parameters:
hCursor Type: hDBICur (Input) Specifies the cursor handle to the table.
piRecords Type: pUINT32 (Input/Output) On input, piRecords is a pointer to the number of records to write. On output, pointer to the client variable that receives the actual number of records written. The number actually written may be less than requested if an integrity violation or other error occurred.
pBuf Type: pBYTE (Input) Pointer to the buffer containing the records to be written.
Usage:
This function is similar to calling DbiAppendRecord for the specified
number of piRecords. DbiWriteBlock can access data in blocks larger
than 64Kb, depending on the size you allocate for the buffer.
Note:
This function cannot be used if the records contain non-empty BLOBs.
Paradox:
This function verifies any referential integrity requirements or
validity checks that may be in place. If either fails, the write
operation is canceled.
Completion state:
The cursor is positioned at the last record that was inserted.
Result:
DbiResult Meaning
DBIERR_NONE The block of records contained in pBuf has been successfully written to the table specified by hCursor.
DBIERR_INVALIDHNDL The specified cursor handle is invalid or NULL, or piRecords is NULL, or pBuf is NULL.
DBIERR_TABLEREADONLY The table is opened read-only; cannot write to it.
DBIERR_NOTSUFFTABLERIGHTS Insufficient table rights to insert a record. (Paradox only.)
DBIERR_NODISKSPACE Insertion failed due to insufficient disk space.
Example from Delphi 7 help:
procedure fDbiWriteBlock(Customer: TTable; var RecordsToInsert: Longint);
var
pRecordsBuf, pTmpBuf: pBYTE;
Rec: Longint;
CustNo: Double;
begin
Randomize;
GetMem(pRecordsBuf, Customer.RecordSize * RecordsToInsert);
pTmpBuf := pRecordsBuf;
try
for Rec := 1 to RecordsToInsert do begin
CustNo := Random(1000000);
// Iterate through the entire record buffer filling each
// individual record with information
with Customer do begin
Check(DbiInitRecord(Handle, pTmpBuf));
Check(DbiPutField(Handle, FieldByName('CustNo').Index + 1, pTmpBuf,
pBYTE(#CustNo)));
Check(DbiPutField(Handle, FieldByName('Company').Index + 1, pTmpBuf,
PChar('INPRISE Corporation')));
Inc(pTmpBuf, RecordSize);
end;
end;
Check(DbiWriteBLock(Customer.Handle, RecordsToInsert, pRecordsBuf));
finally
FreeMem(pRecordsBuf, Customer.RecordSize * RecordsToInsert);
end;
end

Related

Delphi Using Locate function in data source in memory table

I need to check given value is already exists in the memory table, so i have tried following method.
function TForm4.findValueExists(id: Integer): Boolean;
var
state: Boolean;
begin
state := DBGrid1.DataSource.DataSet.Locate('code', id, []);
end;
here is the Search button procedure
procedure TForm4.butSearchClick(Sender: TObject);
var
id: Integer;
Name: String;
Sell: Double;
Qty: Integer;
Amount: Double;
OldQty: Integer;
RecordExist: Boolean;
begin
if txtprocode.Text <> '' then
begin
FDQuery1.Params.ParamByName('ID').Value := txtprocode.Text;
if FDQuery1.Active then
FDQuery1.Close;
FDQuery1.Open();
try
FDQuery1.First;
if not FDQuery1.Eof then
begin
id := FDQuery1.FieldByName('Id').AsInteger;
Name := FDQuery1.FieldByName('name').AsString;
Sell := FDQuery1.FieldByName('selling').AsFloat;
Qty := 1;
Amount := Sell * Qty;
if Form4.findValueExists(id) then
begin
FDMemTable1.Edit;
OldQty := DBGrid1.Fields[3].Value;
FDMemTable1.FieldByName('Qty').AsInteger := (OldQty + 1);
FDMemTable1.FieldByName('amount').AsFloat := (Sell * (OldQty + 1));
FDMemTable1.Post;
end
else
begin
FDMemTable1.InsertRecord([id, Name, Sell, Qty, Amount]);
end;
end;
finally
end;
end;
end;
unfortunately this method always gives me result as 'false'. but physically i can found matched result for given id.
here is UI
Guys i`m new to Delphi language.
You're not returning a value from your FindValueExists function, so you have absolutely no way of knowing if it locates the record or not. If you'd turn on compiler hints and warnings, the compiler would have pointed that fact out to you. (It would have informed you function findValueExists might not return a value, and also that Value assigned to 'state' is never used.)
Change your findValueExists so that it actually returns the result of the Locate call.
function TForm4.findValueExists(id: Integer): Boolean;
begin
Result := DBGrid1.DataSource.DataSet.Locate('code', id, []);
end;

What is the correct way to store record pointers in a Tqueue in Delphi

I'm trying to store a pointer to a record in a Tqueue, then dequeue the pointer later and extract the data but am getting in a muddle with the pointers and keep getting a 'Abstract Error'
Can anyone please see what I am doing wrong and advise me on the correct solution?
(BTW, Initially I had it all without the ^ but then realised my mistake but was surprised that it still gave an error)
The record holds email data that gets sent to a smtp server, It uses a TstringList to hold each line of the body and another one to hold each attachment filename
This is the record structure used to store the email data
TPtrEmailData = ^TEmailDataRec;
TEmailDataRec = record
ToAddr : string; //one email address
CcAddr : string; //addresses delimitated by semicolons
BccAddr : string; //addresses delimitated by semicolons
Subject : String;
Body : TStrings; //each string contains one line of the body
attachments: TStrings;//each string contains a filename
end;
To create the records I use
function TFrmSendEmail.CreateNewEmailRec: TPtrEmailData;
var
EmailRecPtr : TPtrEmailData;
begin
new(EmailRecPtr); //make a new record
EmailRecPtr^.Body := Tstrings.Create ;
EmailRecPtr^.attachments := Tstrings.create;
result := EmailRecPtr ;
end;
and to free them after dequeing I use
procedure TFrmSendSllSmtptEmail.DestroyEmailRec(EmailRecPtr : TPtrEmailData);
//frees memory for the Tstrings and then frees the record
begin
freeandnil(EmailRecPtr^.Body); //free one Tstringlist
FreeAndNil(EmailRecPtr^.attachments); //and the other
FreeAndNil(EmailRecPtr ); //now free the precord pointer
end;
CreateNewEmailRec is called when I enqueue a new record pointer in the queue using the following, passing in the memo and list box containig th ebody and attachments. This is where I get the error.
procedure TFrmSendEmail.AddToEmailQueue(ToAddr, CCAddr,
BccAddr,Subject:String;
Body: Tmemo; Attachments: TListBox);
var
i : integer;
s : string;
EmailRecPtr : TPtrEmailData;
begin
EmailRecPtr := CreateNewEmailRec; //allocate memory
//deallocated in RemoveFromEmailQueue
EmailRecPtr^.ToAddr := ToAddr;
EmailRecPtr^.CCAddr := CCAddr;
EmailRecPtr^.BccAddr := BccAddr;
for I := 0 to Attachments.Count - 1 do
begin
s := Attachments.Items[i];
EmailRecPtr^.attachments.add(s ); <---- !!! get abstract error here
end;
for I := 0 to Body.lines.Count - 1 do
begin
s := Body.lines[i];
EmailRecPtr^.Body.Add(s) ;
end;
EmailQueue.Enqueue(EmailRecPtr );
end;
and DestroyEmailRec is called when I dequeue a pointer to use the data in
procedure TFrmSendEmail.RemoveFromEmailQueue(var ToAddr,
CCAddr,
BccAddr,
Subject: String;
var Body,
Attachments: TStringlist);
var
EmailRecPtr :TPtrEmailData;
i : integer;
s : string;
begin
if EmailQueue.Count > 0 then
begin
Body.Clear;
Attachments.Clear;
EmailRecPtr := EmailQueue.Dequeue; //get pointer to next record
ToAddr := EmailRecPtr^.ToAddr; //populate procedure parameters
CCAddr := EmailRecPtr^.CCAddr;
BccAddr := EmailRecPtr^.BccAddr;
for EmailRecPtr^.attachments.Count - 1 do
begin
s := EmailRec^.attachments[i];
Attachments.Add(s) ;
end;
for I := 0 to EmailRecPtr ^.Body.Count - 1 do
begin
s := EmailRecPtr ^.Body[i];
Body.Add(s);
end;
DestroyEmailRec(EmailRecPtr); //release memory
end;
The call to RemoveFromEmailQueue passes in a couple of created TStringLists
TheBody := Tstringlist.Create ;
TheAttachments := Tstringlist.create;
try
RemoveFromEmailQueue(ToAddr, CCAddr, BccAddr, Subject,TheBody,TheAttachments);
// do stuff with the data;
finally
TheBody.Free;
TheAttachments.Free;
end;
Oh, and the queue is declared as
var
EmailQueue : Tqueue<TPtrEmailData>;
You get the "Abstract Error" because you use an astract object (TStrings)! In the TFrmSendEmail.CreateNewEmailRec method replace TStrings with TStringList:
function TFrmSendEmail.CreateNewEmailRec: TPtrEmailData;
begin
new(result); //make a new record
Result^.Body := TStringList.Create ;
Result^.attachments := TStringList.create;
end;
Also, you can't free records using FreeAndNil! So your method to free the record should be like
procedure TFrmSendSllSmtptEmail.DestroyEmailRec(EmailRecPtr : TPtrEmailData);
//frees memory for the Tstrings and then frees the record
begin
EmailRecPtr^.Body.Free; //free one Tstringlist
EmailRecPtr^.attachments.Free; //and the other
Dispose(EmailRecPtr); //now free the precord pointer
end;

How to make call to DLL function from Delphi?

// Get a list of accounts in a domain separated by \x00 and ended by \x00\x00
Function GetUserList(AName: PAnsiChar; Var List; Size: Longint): Longint; StdCall;
I need to call the above from XE6.
Would someone be kind enough to post an example of how I can
get this buffer, and put it to a stream or a string.
The variable "List" is supposed to fill up some buffer, which I can read
off the list of users.
After trying for a couple of options, I have tried all options such as:
thanks!
var
Buffer: array of Byte;
iCount : Integer;
sName : AnsiString;
begin
...
SetLength(Buffer, 4096);
iCount := GetUserListTest(PAnsiChar(sName)#Buffer[0], Length(Buffer)); // cannot
// iCount := GetUserList(PAnsiChar(sName), Buffer, Length(Buffer));
That is not a Win32 API function, so it must be a third-party function. Ask the vendor for an example.
A var parameter expects you to pass a variable to it. The var receives the address of the variable. #Buffer[0] does not satisfy that requirement, as # returns a Pointer, and then the var ends up with the address of the pointer itself, not the address of the variable being pointed at. The function is expecting a pointer to a buffer. By using a var to receive that pointer, you need to drop the # and pass the first array element, so that the address of that element (effectively the address of the buffer) will be passed to the function, eg:
iCount := GetUserList(PAnsiChar(sName), Buffer[0], iCount);
Alternatively, you can use this syntax instead, which will pass the same address of the first element:
iCount := GetUserList(PAnsiChar(sName), PByte(Buffer)^, iCount);
Now, with that said, chances are that the function may allow you to query it for the necessary array size so you can allocate only what is actually needed (but check the documentation to be sure, I'm making an assumption here since you have not said otherwise)), eg:
procedure GetDomainUsers(const Domain: AnsiString; Users: TStrings);
var
Buffer: array of AnsiChar;
iCount : Integer;
User: PAnsiChar;
begin
// this call ASSUMES the function returns the needed
// bytecount when given a NULL/empty array - check
// the documentation!!!
iCount := GetUserList(PAnsiChar(Domain), PAnsiChar(nil)^, 0);
if iCount > 0 then
begin
SetLength(Buffer, iCount);
iCount := GetUserList(PAnsiChar(Domain), Buffer[0]{or: PAnsiChar(Buffer)^}, iCount);
end;
if iCount > 0 then
begin
Users.BeginUpdate;
try
User := PAnsiChar(Buffer);
while User^ <> #0 do
begin
Users.Add(User);
Inc(User, StrLen(User)+1);
end;
finally
Users.EndUpdate;
end;
end;
end;
If that does not work, then you will have to pre-allocate a large array:
procedure GetDomainUsers(const Domain: AnsiString; Users: TStrings);
var
Buffer: array of AnsiChar;
User: PAnsiChar;
begin
SetLength(Buffer, 1024);
if GetUserList(PAnsiChar(Domain), Buffer[0]{or: PAnsiChar(Buffer)^}, Length(Buffer)) > 0 then
begin
Users.BeginUpdate;
try
User := PAnsiChar(Buffer);
while User^ <> #0 do
begin
Users.Add(User);
Inc(User, StrLen(User)+1);
end;
finally
Users.EndUpdate;
end;
end;
end;

Reading/writing dynamic arrays of objects to a file - Delphi

Im trying to write some code which will read/write a dynamic array of objects to a file. The objects represent the structure of the java source code. I need to be able to scan the whole source code and gather information on Fields, Methods and Classes. I have an algorithm which does this and the result is kept in a structure of TFieldStruc, TMethodStruc and TClassStruc, all descendants of the TCoreStruc (a descendant of TObject). The java source code takes a couple of minutes to be scanned and have the virtual structure generated. Because of this my application scans all the source code once and saves it into a much more accessible format which is loaded when ever the IDE launches.
Is there a way (other than exporting the objects 'to string' and then re-creating them again when they are loaded) to stream the entire three arrays of TFieldStruc, TMethodStruc and TClassStruc, to a file so they can be read later?
I have tried reading and writing to a 'File of TFieldStruc..' and the TFileStream to save the objects to a file and read them back, but in both cases I get 'inaccessible value' in the debugger followed by an 'Access Violation' error as soon as the object is accessed again. If anyone has ideas on how to solve this problem it would be appreciated. Below is the code to TCodeStruc if any of its fields/methods may be causing issues:
type
TCoreStruc = class(TObject)
public
LowerPointer : integer;
HigherPointer : integer;
Line : integer;
Word : integer;
Char : integer;
CoreType : ansistring;
IsPublic : boolean;
IsPrivate : boolean;
IsStatic : boolean;
IsFinal : boolean;
Name : ansistring;
NestedStruc : TCoreStruc;
constructor Create(Name, CoreType : ansistring; NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
procedure UpdateValues(NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
procedure SetPosition(Line, Word, Char : integer);
end;
Here is an example using your structure.
A few notes about this:
There are lots of different ways to go about this. Take David Heffernan's advice and do some searches for serialization. I use the approach include below in one of my applications but other include using RTTI/Persistent objects to iterate the published properties of an object. There are libraries that will iterate object for you and do all the work.
You need to actually write out to the string the sizes of dynamic objects. That includes things like arrays and strings.
In my example each object knows how to read and write itself to the stream.
I use a struct for the fixed length parts of the object. That saves me from needing to write each data element individually.
Strings need to be written on their own to include their size (you could used a fixed length buffer like Delphi short strings but it is not that much work to write out a regular string. You need to decide what type of format you want string data written in. I picked UTF8 for my application.
For your other arrays you can write their data (including length) after the first array is written out. Sometime there will be a header section that includes the lengths for all the dynamic sections at the top, others will write the length write before the structure starts. The key part is to always write things in the same order and included somewhere it can be re-read how many there are.
There is no error checking or verification in the file structure below. If anything is different between the read and write it will blow up - probably with a stream read error.
Any change to the structures will cause old files to not be read properly. There are a number of ways to version a file to ensure you can still read old formats. Not included here.
In your application you would pass a TFileStream to the read and write function. I like to write the actual read/write functions with just a TStream. Then the object does not care where the data is coming from. It could be file, or it could already be in memory.
If you drop the following unit into a console application you should be able to add a call to Main and step through the example.
unit CoreStruct;
interface
uses Classes, Types;
type
TCoreStructData = packed record
LowerPointer : integer;
HigherPointer : integer;
Line : integer;
Word : integer;
Char : integer;
IsPublic : boolean;
IsPrivate : boolean;
IsStatic : boolean;
IsFinal : boolean;
HasNested: boolean;
end;
TCoreStruc = class(TObject)
private
FCoreData: TCoreStructData;
FNestedStruc : TCoreStruc;
procedure SetNestedStruc(AValue: TCoreStruc);
public
CoreType : String;
Name : String;
constructor Create(); overload;
procedure WriteToStream(Stream: TStream);
procedure ReadFromStream(Stream: TStream);
//constructor Create(Name, CoreType : ansistring; NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean); overload;
//procedure UpdateValues(NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
//procedure SetPosition(Line, Word, Char : integer);
property LowerPointer: integer read FCoreData.LowerPointer write FCoreData.LowerPointer;
property HigherPointer: integer read FCoreData.HigherPointer write FCoreData.HigherPointer;
property Line: integer read FCoreData.Line write FCoreData.Line;
property Word: integer read FCoreData.Word write FCoreData.Word;
property Char: integer read FCoreData.Char write FCoreData.Char;
property NestedStruc: TCoreStruc read FNestedStruc write SetNestedStruc;
end;
procedure Main();
implementation
function ReadUTF8StringFromStream(const Stream: TStream): String;
var
n: Integer;
Buffer8: Utf8String;
begin
Result := '';
Stream.ReadBuffer(n, SizeOf(n));
if n = 0 then
Exit;
SetLength(Buffer8, n);
Stream.ReadBuffer(Pointer(Buffer8)^, n);
Result := String(Buffer8);
end;
procedure WriteUtf8StringToStream(const Data: String; Stream: TStream);
var
Buffer8: Utf8String;
n: Integer;
begin
// When writing strings we need to make sure the length of the
// string is written out to the stream. That goes first so the
// reader knows how long the buffer is.
//
// You could you different formats to write to the file depending on
// needs. I like using UTF8 when writing out to file, but it does
// require an extra buffer copy when turning it back into a native
// Delphi unicode string.
Buffer8 := Utf8String(Data);
n := Length(Buffer8);
Stream.WriteBuffer(n, SizeOf(n));
Stream.WriteBuffer(Pointer(Buffer8)^, n);
end;
procedure Main();
var
Structs: array of TCoreStruc;
ArraySize: integer;
DataStream: TMemoryStream;
ArraySize_A: integer;
Structs_A: array of TCoreStruc;
i: integer;
begin
// Create and write some data
SetLength(Structs, 3);
Structs[0] := TCoreStruc.Create();
Structs[0].HigherPointer := 1;
Structs[0].Name := 'Testing';
Structs[0].NestedStruc := TCoreStruc.Create();
Structs[0].NestedStruc.HigherPointer := 100;
Structs[1] := TCoreStruc.Create();
Structs[1].HigherPointer := 2;
Structs[2] := TCoreStruc.Create();
Structs[2].HigherPointer := 3;
DataStream := TMemoryStream.Create();
// We need to start with the count we are writing out so
// the reader knows how many times to loop.
ArraySize := Length(Structs);
DataStream.WriteBuffer(ArraySize, SizeOf(integer));
for i := 0 to ArraySize - 1 do
begin
Structs[i].WriteToStream(DataStream);
end;
// Read the data into a new set of objects
DataStream.Position := 0;
DataStream.ReadBuffer(ArraySize_A, SizeOf(integer));
SetLength(Structs_A, ArraySize_A);
for i := 0 to ArraySize_A - 1 do
begin
Structs_A[i] := TCoreStruc.Create();
Structs_A[i].ReadFromStream(DataStream);
end;
end;
{ TCoreStruc }
constructor TCoreStruc.Create;
begin
Self.LowerPointer := 0;
Self.HigherPointer := 0;
Self.Line := 0;
Self.Word := 0;
Self.Char := 0;
Self.NestedStruc := nil;
end;
procedure TCoreStruc.WriteToStream(Stream: TStream);
begin
Stream.WriteBuffer(FCoreData, SizeOf(TCoreStructData));
WriteUtf8StringToStream(Name, Stream);
WriteUtf8StringToStream(CoreType, Stream);
if FCoreData.HasNested = true then
begin
FNestedStruc.WriteToStream(Stream)
end;
end;
procedure TCoreStruc.ReadFromStream(Stream: TStream);
begin
Stream.ReadBuffer(FCoreData, SizeOf(TCoreStructData));
Name := ReadUtf8StringFromStream(Stream);
Name := ReadUtf8StringFromStream(Stream);
if FCoreData.HasNested = true then
begin
FNestedStruc := TCoreStruc.Create();
FNestedStruc.ReadFromStream(Stream);
end;
end;
procedure TCoreStruc.SetNestedStruc(AValue: TCoreStruc);
begin
FNestedStruc := AValue;
if AValue = nil then
FCoreData.HasNested := false
else
FCoreData.HasNested := true;
end;
end.

Upgrade Indy9 to Indy10

I want to upgrade my application from Indy 9 to 10 with Delphi 2007.
In this thread there is a call to Indy9 TIdUDPBase.SendBuffer but this won't compile in Indy10 as the method parameter don't exists. The third parameter aBuffer is a var parameter and I didn't find any such method signature in Indy10.
Any alternative method to call ?
procedure TSenderThread.Execute;
var
vTimeData: TTimeDataRecord;
I: Integer;
FElapsed: Int64;
FTimerElappsed,
vLastTimerElappsed: Int64;
begin
vTimeData.Size := SizeOf(TTimeDataRecord);
vTimeData.ClientCount := 1;
Priority := tpHighest;
FIdUDPClient := TIdUDPClient.Create(nil);
FIdUDPClient.BroadcastEnabled := True;
try
while not (Terminated or Application.Terminated) do
begin
Sleep(1000);
//Measure Time frame
vLastTimerElappsed := FTimerElappsed;
QueryPerformanceCounter(FTimerElappsed);
FElapsed := ((FTimerElappsed-vLastTimerElappsed)*1000000) div FFrequency;
vTimeData.TotalTimeFrame := FElapsed;
if FRunning then
begin
FElapsed := ((FTimerElappsed-FStart)*1000000) div FFrequency;
vTimeData.CurrentMessageTime := FElapsed;
end
else
vTimeData.CurrentMessageTime := 0;
//Copy Values
vTimeData.AccumulatedTime := InterlockedExchange(TimeData.AccumulatedTime,0);
vTimeData.MessageCount := InterlockedExchange(TimeData.MessageCount,0);
for I := 0 to TimeClassMax do
vTimeData.TimeClasses[I] := InterlockedExchange(TimeData.TimeClasses[I],0);
// Calls procedure TIdUDPBase.SendBuffer(AHost: string; const APort: Integer; var ABuffer; const AByteCount: integer);
// This is changed in Indy10, unable to compile
FIdUDPClient.SendBuffer('255.255.255.255', UIPerfPort, vTimeData, TimeData.Size);
end;
finally
FreeAndNil(FIdUDPClient);
end;
end;
EDIT:
vTimeData is basically an array of integers.
TTimeDataRecord = record
Size: Integer; //Size of record structure is transfered and compared for safty reasons.
ClientCount: Integer;
AccumulatedTime: Integer; //This is the accumulated time busy in microseconds
CurrentMessageTime: Integer; //This is the time the current message has been processed. If several computers report a high value at the same time it indicates a freeze!
TotalTimeFrame: Integer; //This is the total time measured in microseconds
MessageCount: Integer;
TimeClasses: array [0..TimeClassMax] of Integer;
end;
you have a method with same name
procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort;
const ABuffer: TIdBytes);
Instead of an untyped buffer it expects an array of bytes. What is your data like? You just need to write your data as an array of bytes. Something like:
var
Buffer: TIdBytes;
begin
SetLength(Buffer, YourSizeOfData);
Move(YourData, Buffer[0], YourSizeOfData);
FIdUDPClient.SendBuffer('255.255.255.255', UIPerfPort, Buffer);
end;
But as I said it depends on the type of the data. The approach is ok however.
EDIT:
Now that I can see that you have a record you have two options:
Just move the whole record to array of bytes.
Move(#aRecord, Buffer[0], (6 + TimeClassMax) * SizeOf(Integer));
Have a CopyToBytes method in your record that does the actual copy. More general I guess.
TTimeDataRecord = record
Size: Integer; //Size of record structure is transfered and compared for safty reasons.
ClientCount: Integer;
AccumulatedTime: Integer; //This is the accumulated time busy in microseconds
CurrentMessageTime: Integer; //This is the time the current message has been processed. If several computers report a high value at the same time it indicates a freeze!
TotalTimeFrame: Integer; //This is the total time measured in microseconds
MessageCount: Integer;
TimeClasses: array [0..TimeClassMax] of Integer;
procedure CopyToBytes(var Buffer: TIdBytes);
end
Implementation of the CopyToBytes
procedure TTimeDataRecord.CopyToBytes(var Buffer: TIdBytes);
begin
// copy the data however you see fit
end;

Resources