I am trying to understand how I can typecast in Delphi, similar to C/C++.
The C++ code I'm trying to convert is this;
typedef struct tagEXT2_INODE
{
uint16_t i_mode; /* File mode */
uint16_t i_uid; /* Low 16 bits of Owner Uid */
uint32_t i_size; /* Size in bytes */
uint32_t i_atime; /* Access time */
uint32_t i_ctime; /* Creation time */
uint32_t i_mtime; /* Modification time */
uint32_t i_dtime; /* Deletion Time */
uint16_t i_gid; /* Low 16 bits of Group Id */
uint16_t i_links_count; /* Links count */
uint32_t i_blocks; /* Blocks count */
uint32_t i_flags; /* File flags */
union {
struct {
uint32_t l_i_reserved1;
} linux1;
struct {
uint32_t h_i_translator;
} hurd1;
struct {
uint32_t m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
uint32_t i_generation; /* File version (for NFS) */
uint32_t i_file_acl; /* File ACL */
// uint32_t i_dir_acl; /* Directory ACL */
uint32_t i_size_high; /* This is used store the high 32 bit of file size in large files */
uint32_t i_faddr; /* Fragment address */
union {
struct {
uint8_t l_i_frag; /* Fragment number */
uint8_t l_i_fsize; /* Fragment size */
uint16_t i_pad1;
uint16_t l_i_uid_high; /* these 2 fields */
uint16_t l_i_gid_high; /* were reserved2[0] */
uint32_t l_i_reserved2;
} linux2;
struct {
uint8_t h_i_frag; /* Fragment number */
uint8_t h_i_fsize; /* Fragment size */
uint16_t h_i_mode_high;
uint16_t h_i_uid_high;
uint16_t h_i_gid_high;
uint16_t h_i_author;
} hurd2;
struct {
uint8_t m_i_frag; /* Fragment number */
uint8_t m_i_fsize; /* Fragment size */
uint16_t m_pad1;
uint32_t m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
} __attribute__ ((__packed__)) EXT2_INODE;
EXT2_INODE *src;
char *inode_buffer;
int inode_index, ret = 0;
inode_buffer = (char *)malloc(4096);
src = (EXT2_INODE *)(inode_buffer + inode_index);
Could someone explain how this src can be typecasted like this? And how would I do the same operation in Delphi?
Thanks
Edit:
Here is my Ext2 iNode Record;
Type
TExt2iNode = Record
i_mode : Word;
i_uid : Word;
i_size : Cardinal;
i_atime : Cardinal;
i_ctime : Cardinal;
i_mtime : Cardinal;
i_dtimes : Cardinal;
i_gid : Word;
i_links_count : Word;
i_blocks: Cardinal;
i_flags : Cardinal;
osd1 : Record
linux1 : Record
l_i_reserved1 : Cardinal;
end;
hurd1 : Record
h_i_translator: Cardinal;
End;
masix1 : Record
m_i_reserved1 : Cardinal;
End;
End;
i_block: array [0..EXT2_N_BLOCKS-1] of Cardinal;
i_generation : Cardinal;
i_file_acl : Cardinal;
i_size_high : Cardinal;
i_faddr : Cardinal;
osd2 : Record
Linux2 : Record
l_i_frag : Byte;
l_i_fsize : Byte;
i_pad1 : Word;
l_i_uid_high : Word;
l_i_gid_high : Word;
l_i_reserved2 : Cardinal
end;
hurd2 : Record
h_i_frag : Byte;
h_i_fsize : Byte;
h_i_mode_high : Word;
h_i_uid_high : Word;
h_i_gid_high : Word;
h_i_author : Word;
end;
masix2 : Record
m_i_frag:Byte;
m_i_fsize : Byte;
m_pad1 : Word;
m_i_reserved : array[0..1] of Cardinal;
end;
end;
End;
Here is my Ex2 Partition Record;
type
Ext2Partition = Class
private
handle: THandle;
sect_size: Integer;
total_sectors: Int64;
relative_sect: Int64;
linux_name :AnsiString;
inodes_per_group: integer;
inode_size: integer;
block_size: integer;
totalGRoups: Integer;
desc : TExt2_Group_Desc;
last_block : Cardinal;
inode_buffer : array of AnsiChar;
root : Ext2File;
buffercache : TList;
lvol : LogicalVolume;
public
onview, is_valid: boolean;
published
Constructor Create(size, offset :int64; ssise: integer; PHandle: THandle);
function read_inode(inum: Cardinal):Ext2File;
function readblock(blocknum: cardinal; var buffer: array of AnsiChar):integer;
function mount():integer;
End;
Here is my read_inode function where the pointer calculation takes place;
function Ext2Partition.read_inode(inum: Cardinal):Ext2File;
var
group, index, blknum: cardinal;
inode_index : integer;
ret: integer;
fFile: EXt2File;
src: TExt2iNode;
begin
if inum = 0 then
Result := NIL;
SetLength(self.inode_buffer, self.block_size);
group := (inum -1) DIV self.inodes_per_group;
if group > self.totalGRoups then
begin
//ShowMessage('Error reading iNode');
Result := -1;
end;
index := ((inum-1) MOD self.inodes_per_group) * self.inode_size;
inode_index := (index MOD self.block_size);
blknum := self.desc.bg_inode_table + (index div self.block_size);
if blknum <> self.last_block then
ret := readblock(blknum, self.inode_buffer);
fFile := TExt2iNode.Create;
//What goes here?
end;
The code allocates a block of memory. Elsewhere, it has determined the offset of an ext2 inode structure somewhere inside that block, given by inode_index. It adds that offset to the start of the block, giving the address of the structure. The code then type-casts the result to tell the compiler that the computed char* address is really the address of that struct type.
Using a literal translation, we'd have the following Delphi declarations:
var
inode_buffer: PAnsiChar;
inode_index: Integer;
src: PExt2_Inode;
Therefore, to assign src, you'd type-cast and add exactly as in the C++ code:
src := PExt2_Inode(inode_buffer + inode_index);
Most Delphi pointer types don't support such pointer arithmetic by default, but PAnsiChar is special.
Using your translation so far, where inode_buffer is an array instead of a pointer to a memory block, you'd instead have this:
src := PExt2_Inode(#inode_buffer[inode_index]);
That indexes the array, and then uses the # operator to get the address of that array element. (In fact, you could use the same syntax if inode_buffer were my original PAnsiChar type, too.)
Related
UPDATED USING PREVIOUS ANSWERS
I have a DLL with the following header definitions:
typedef struct {
int32_t dimSizes[2];
double NumericControl[1];
} DoubleArrayBase;
typedef DoubleArrayBase **DoubleArray;
void __stdcall ReadTERFCorrectedData(char FilepathString[],
int32_t ArrayLengths[], DoubleArray *AmplitudeData,
DoubleArray *FrequencyData, int32_t len);
DoubleArray __cdecl AllocateDoubleArray (int32 *dimSizeArr);
MgErr __cdecl ResizeDoubleArray (DoubleArray *hdlPtr, int32 *dimSizeArr);
MgErr __cdecl DeAllocateDoubleArray (DoubleArray *hdlPtr);
My question is, how do I go about setting up a Delphi call to this function?
My Delphi definition of the call is as follows:
type
PDoubleArray = ^DoubleArray;
DoubleArray = ^PDoubleArrayBase;
PDoubleArrayBase = ^DoubleArrayBase;
DoubleArrayBase = packed record
dimSizes: array[0..1] of Int32;
NumericControl: array[0..0] of Double;
end;
procedure ReadTERFCorrectedData(FilepathString: PAnsiChar;
var ArrayLengths: Int32; AmplitudeData, FrequencyData: PDoubleArray;
len: Int32);stdcall; external DLLDirectory;
function AllocateDoubleArray (SizeArray: PInt): DoubleArray;stdcall; external DLLDirectory;
And then in the body of the code I allocate space for the arrays then get the data as follows:
implementation
{$R *.dfm}
procedure TForm7.btn1Click(Sender: TObject);
var
AmplitudeData, FrequencyData : DoubleArray;
arraylengths: array[0..1] of int32;
I: Integer;
TempSize : Int32;
begin
TempSize := 50;
AmplitudeData := AllocateDoubleArray(#Tempsize);
FrequencyData := AllocateDoubleArray(#Tempsize);
ReadTERFCorrectedData(fileloc, ArrayLengths[0], #AmplitudeData, #FrequencyData, 50);
ShowMessage(arraylengths[1].tostring);
end;
How do I go about accessing the data stored in the AmplitudeData and FrequencyData? They are both DoubleArray types, but how do i get to the DoubleArrayBase.dimSizes and DoubleArrayBase.NumericControl values buried by the pointer? I have tried AmplitudeData[0,0], AmplitudeData.NumericControl, etc but can not get to the values.
I am basically trying to create a Delphi version of what is being done in this post:
https://lavag.org/topic/20486-lv-dll-creates-mysterious-doublearray-class/
The DLL is using fixed-length arrays, but your Delphi code is using dynamic arrays instead. They are not the same thing.
Also, the DLL is expecting a pointer-to-pointer-to-struct, but you are using a pointer-to-record instead. You are missing a level of indirection.
Also, the DLL expects a pointer to a null-terminated AnsiChar C string, not an AnsiString.
Try this instead:
type
PDoubleArray = ^DoubleArray;
DoubleArray = ^PDoubleArrayBase;
PDoubleArrayBase = ^DoubleArrayBase;
DoubleArrayBase = packed record
dimSizes: array[0..1] of Int32;
NumericControl: array[0..0] of Double;
end;
procedure ReadTERFCorrectedData(FilepathString: PAnsiChar;
ArrayLengths: PInt32; AmplitudeData, FrequencyData: PDoubleArray;
len: Int32); stdcall; external '...';
// Alternatively:
//
// procedure ReadTERFCorrectedData(FilepathString: PAnsiChar;
// var ArrayLengths: Int32; var AmplitudeData, FrequencyData: DoubleArray;
// len: Int32); stdcall; external '...';
UPDATE: the function takes DoubleArray* parameters, ie a pointer to a DoubleArray. So you likely need to call it something like this:
procedure TForm7.btn1Click(Sender: TObject);
var
AmplitudeData, FrequencyData : DoubleArray;
ArrayLengths: array[0..1] of Int32;
I: Integer;
begin
ReadTERFCorrectedData(fileloc, #ArrayLengths[0], #AmplitudeData, #FrequencyData, 500);
...
end;
But, there is simply not enough information presented to know whether you need to pre-allocate the arrays before calling the function, or if the function will allocate the arrays for you. The use of pointer-to-pointer indirections strongly implies that the DLL will perform the allocations for you. But what the DoubleArray contents will look like upon exit is anyone's guess without seeing documentation or example C code.
UPDATE: based on the information presented on this page, try something like the following:
procedure DisplayDoubleArray(const Arr: DoubleArray);
var
P: PDouble;
I, Num: Integer;
begin
if (Arr = nil) or (Arr^ = nil) then Exit;
ShowMessage('Arr.dimSizes[0]: ' + Arr^^.dimSizes[0].ToString);
ShowMessage('Arr.dimSizes[1]: ' + Arr^^.dimSizes[1].ToString);
Num := Arr^^.dimSizes[0] * Arr^^.dimSizes[1];
P := #(Arr^^.NumericControl[0]);
for I := 0 to Num-1 do
begin
ShowMessage('Arr.NumericControl[' + I.ToString + ']: ' + P^.ToString);
Inc(P);
end;
end;
procedure TForm7.btn1Click(Sender: TObject);
var
AmplitudeData, FrequencyData : DoubleArray;
ArrayLengths: array[0..1] of Int32;
I: Integer;
begin
ReadTERFCorrectedData(fileloc, #ArrayLengths[0], #AmplitudeData, #FrequencyData, 500);
try
ShowMessage('ArrayLengths[0]: ' + ArrayLengths[0].ToString);
ShowMessage('ArrayLengths[1]: ' + ArrayLengths[1].ToString);
DisplayDoubleArray(AmplitudeData);
DisplayDoubleArray(FrequencyData);
finally
DeAllocateDoubleArray(#AmplitudeData);
DeAllocateDoubleArray(#FrequencyData);
end;
end;
Sorry about my Yoda English, i will try my best.
I'm trying to use a Dahua SDK .dlls in my Delphi App but i can´t undertand how to do some convertions from one dll function.
To give some context to my problem, i will try to explain what i´m trying to do.
I need to call a find function from a dll to list all cardusers from a terminal, so the SDK provide a DLL with C++ header and a sample app to explain how to use it on Visual c++;
My first problem is i´m using Delphi and i need to translate the header from DLL and convert the C++ codes to Delphi;
The Find function from DLL is described bellow:
//C++ FROM header
BOOL CLIENT_DoFindUserInfo(LLONG lFindHandle, NET_IN_USERINFO_DO_FIND* pstIn, NET_OUT_USERINFO_DO_FIND* pstOut, int nWaitTime);
//Delphi
CLIENT_DoFindUserInfo(Int64 lFindHandle; pstIn : PNET_IN_USERINFO_DO_FIND; pstOut : PNET_OUT_USERINFO_DO_FIND; nWaitTime: Integer);
it receives the Find handle (lfindHandle), a pointer to a Inner Structure (pstIn) and a pointer to a outter Structure (pstOut), the last parameter is a integer
the Inner structure gives some integer params like, index start e max numbers os searchs and its ok.
The Outter strucure is described bellow:
//c++ from header
// input of CLIENT_DoFindUserInfo
typedef struct tagNET_IN_USERINFO_DO_FIND
{
DWORD dwSize; // struct size
int nStartNo; // start no
int nCount; // query count
}NET_IN_USERINFO_DO_FIND;
// output of CLIENT_DoFindUserInfo
typedef struct tagNET_OUT_USERINFO_DO_FIND
{
DWORD dwSize; // struct size
int nRetNum; // return number
NET_ACCESS_USER_INFO* pstuInfo; // user info, larger than nCount*sizeof(NET_ACCESS_USER_INFO)
int nMaxNum; // max return number
BYTE byReserved[4]; // reserve
}NET_OUT_USERINFO_DO_FIND;
// user info
typedef struct tagNET_ACCESS_USER_INFO
{
char szUserID[DH_MAX_USERID_LEN]; // user ID
char szName[MAX_COMMON_STRING_32]; // user name
NET_ENUM_USER_TYPE emUserType; // user type
UINT nUserStatus; // user status, 0 normal, 1 freeze
int nUserTime; // user times of guest
char szCitizenIDNo[MAX_COMMON_STRING_32]; // CitizenID no
char szPsw[DH_MAX_CARDPWD_LEN]; // UserID+password
int nDoorNum; // door number;
int nDoors[DH_MAX_DOOR_NUM]; // Privileged Door Number,That is CFG_CMD_ACCESS_EVENT Configure Array Subscript
int nTimeSectionNum; // the Number of Effective Open Time
int nTimeSectionNo[DH_MAX_TIMESECTION_NUM]; // Open Time Segment Index,That is CFG_ACCESS_TIMESCHEDULE_INFO Array subscript
int nSpecialDaysScheduleNum; // the number of specialday
int nSpecialDaysSchedule[MAX_ACCESSDOOR_NUM]; // Open specialday index, That is NET_EM_CFG_ACCESSCTL_SPECIALDAYS_SCHEDULE Array subscript
NET_TIME stuValidBeginTime; // Valid Begin Time
NET_TIME stuValidEndTime; // Valid End Time
BOOL bFirstEnter; // has first card or not
int nFirstEnterDoorsNum; // has first card door number
int nFirstEnterDoors[DH_MAX_DOOR_NUM]; // has first card door No,FirstEnter-1 means all channels
NET_ATTENDANCE_AUTHORITY emAuthority; // user authority
int nRepeatEnterRouteTimeout; // repeatenter timeout time
int nFloorNum; // floor number
char szFloorNo[MAX_ACCESS_FLOOR_NUM][DH_COMMON_STRING_16]; // floor
int nRoom; // room number
char szRoomNo[MAX_ROOMNUM_COUNT][DH_COMMON_STRING_16]; // room
BOOL bFloorNoExValid; // if szFloorNoEx is valid, TRUE:valid, else invalid
int nFloorNumEx; // floor number extended
char szFloorNoEx[256][4]; // floor info
char szClassInfo[256]; // class info
BYTE byReserved[2808]; // reserved
}NET_ACCESS_USER_INFO;
//Delphi convertion
type
NET_IN_USERINFO_DO_FIND = record
dwSize: DWORD; // struct size
nStartNo: Integer; // start no
nCount: Integer; // query count
end;
NET_OUT_USERINFO_DO_FIND = record
dwSize: DWORD; // struct size
nRetNum: Integer; // return number
pstuInfo: PNET_ACCESS_USER_INFO; // user info <- one of my problems stay here, a pointer to another record.
nMaxNum: Integer; // max return number
byReserved: array[0..3] of Byte; // reserve
end;
PNET_OUT_USERINFO_DO_FIND = ^NET_OUT_USERINFO_DO_FIND;
NET_ACCESS_USER_INFO = record
szUserID: array[0..32 - 1] of AnsiChar; // user ID
szName: array[0..MAX_COMMON_STRING_32 - 1] of AnsiChar; // user name
emUserType: NET_ENUM_USER_TYPE; // user type
nUserStatus: UINT; // user status, 0 normal, 1 freeze
nUserTime: Integer; // user times of guest
szCitizenIDNo: array[0..MAX_COMMON_STRING_32 - 1] of AnsiChar; // CitizenID no
szPsw: array[0..DH_MAX_CARDPWD_LEN - 1] of AnsiChar; // UserID+password
nDoorNum: Integer; // door number;
nDoors: array[0..32 - 1] of Integer; // Privileged Door Number,That is CFG_CMD_ACCESS_EVENT Configure Array Subscript
nTimeSectionNum: Integer; // the Number of Effective Open Time
nTimeSectionNo: array[0..32 - 1] of Integer; // Open Time Segment Index,That is CFG_ACCESS_TIMESCHEDULE_INFO Array subscript
nSpecialDaysScheduleNum: Integer; // the number of specialday
nSpecialDaysSchedule: array[0..MAX_ACCESSDOOR_NUM - 1] of Integer; // Open specialday index, That is NET_EM_CFG_ACCESSCTL_SPECIALDAYS_SCHEDULE Array subscript
stuValidBeginTime: NET_TIME; // Valid Begin Time
stuValidEndTime: NET_TIME; // Valid End Time
bFirstEnter: BOOL; // has first card or not
nFirstEnterDoorsNum: Integer; // has first card door number
nFirstEnterDoors: array[0..32 - 1] of Integer; // has first card door No,FirstEnter-1 means all channels
emAuthority: NET_ATTENDANCE_AUTHORITY; // user authority
nRepeatEnterRouteTimeout: Integer; // repeatenter timeout time
nFloorNum: Integer; // floor number
szFloorNo: array[0..MAX_ACCESS_FLOOR_NUM - 1, 0..DH_COMMON_STRING_16 - 1] of AnsiChar; // floor
nRoom: Integer; // room number
szRoomNo: array[0..32 - 1, 0..DH_COMMON_STRING_16 - 1] of AnsiChar; // room
bFloorNoExValid: BOOL; // if szFloorNoEx is valid, TRUE:valid, else invalid
nFloorNumEx: Integer; // floor number extended
szFloorNoEx: array[0..255, 0..3] of AnsiChar; // floor info
szClassInfo: array[0..255] of AnsiChar; // class info
byReserved: array[0..2807] of Byte; // reserved
end;
PNET_ACCESS_USER_INFO = ^NET_ACCESS_USER_INFO
the sample code, create a new pointer to a struct array and pass to outter struct to be called on the DLL function
while (m_bIsDoFindNext) //<- bool to control the lood
{
//Here comes my big problem, i understood the line
// declaring "pUserInfo" as a pointer to a array 10 of NET_ACCESS_USER_INFO
NET_ACCESS_USER_INFO* pUserInfo = new NET_ACCESS_USER_INFO[10];
if (pUserInfo) //<- do not know what is tested here
{
int nRecordNum = 0;
//here call the dll function passing the pUserInfo to me used
m_bIsDoFindNext = Device::GetInstance().UserFindNext(nStartNo,10,pUserInfo,nRecordNum);
for (int i=0;i<nRecordNum;i++)
{
NET_ACCESS_USER_INFO stuUserInfo;
memset(&stuUserInfo,0,sizeof(NET_ACCESS_USER_INFO));
memcpy(&stuUserInfo,&pUserInfo[i],sizeof(NET_ACCESS_USER_INFO));
m_UserInfoVector.push_back(stuUserInfo);
}
nStartNo += nRecordNum;
delete []pUserInfo;
pUserInfo = NULL;
}
else
{
m_bIsDoFindNext = FALSE;
}
}
BOOL DeviceImpl::UserFindNext(int nStartNo, int nMaxNum, NET_ACCESS_USER_INFO* pstuAlarm, int& nRecordNum)
{
//pstuAlarm is the pUserInfo pointer create in another function
if (0 == m_lLoginID || nMaxNum <= 0 || m_UserFindId == NULL || NULL == pstuAlarm)
{
return FALSE;
}
//creating a new inner structure
NET_IN_USERINFO_DO_FIND stuFindIn = {sizeof(stuFindIn)}; //<- i dont know why and how to do in delphi
stuFindIn.nStartNo = nStartNo;
stuFindIn.nCount = nMaxNum;
NET_OUT_USERINFO_DO_FIND stuFindOut = {sizeof(stuFindOut)}; //<- i dont know why and how to do in delphi
stuFindOut.nMaxNum = nMaxNum;
stuFindOut.pstuInfo = pstuAlarm; //<- here comes
//in the NET_OUT_USERINFO_DO_FIND structure, pstuInfo as defined as a pointer to NET_ACCESS_USER_INFO,
//but is receiving a pointer to array of NET_ACCESS_USER_INFO
if (CLIENT_DoFindUserInfo(m_UserFindId, &stuFindIn, &stuFindOut, SDK_API_WAIT))
{
if (stuFindOut.nRetNum > 0)
{
nRecordNum = stuFindOut.nRetNum;
return TRUE;
}
}
return FALSE;
}
//My Delphi code
var
aUserInfo : array of NET_ACCESS_USER_INFO;
Finish: Boolean;
nRecNum: Integer;
nStartNum: Integer;
begin
nStart := 0;
Finish := True;
While not Finish do
begin
SetLength(aUserInfo,10);
nRecNum := 0;
Finish := UserFindNext(nStartNum, 10, #UserInfo[0], nRecNum);
For I := 0 to nRecNum - 1 do
begin
//do something with the outter information
With UserInfo[I] do
begin
Memo1.Lines.Add(szName);
end
end;
nStartNum := nStartNum + nRecNum;
SetLength(aUserInfo,0);
end;
end;
function UserFindNext(nStart: Integer; nMax: Integer; pStuAlarm: PNET_ACCESS_USER_INFO; nRecordNum: PInteger) : Boolean;
var
FindIn: NET_IN_USERINFO_DO_FIND;
FindOut: NET_OUT_USERINFO_DO_FIND ;
begin
FindIn.nStartNo := nStart;
FindIn.nCount := nMax;
FindOut.nMaxNum := nMax;
FindOut.pstuInfo := pstuAlarm;
if CLIENT_DoFindUserInfo(lFindID, #FindIn, #FindOut, 5000) then
begin
if FindOut.nRetNum > 0 then
begin
nRecordNum^ := FindOut.nRetNum;
end;
Result := True;
end
else
Result := False;
end;
if i undertand corretly, i create a array of NET_ACCESS_USER_INFO, then i set the size of the array and
give the pointer to the first item of the array as parameter to UserFindNext function and the pointer to nRecNum
in the UserFindNext i create the inner and outter records and give the data to call the DLL function
But i always got False;
What i´m doing wrong ?
can be some problem in the NET_ACCESS_USER_INFO record ?
i´m doing righ on pointers stuff ?
Your translation of the function's signature is missing a return type and a calling convention. It should look more like this instead:
function CLIENT_DoFindUserInfo(lFindHandle : Int64; pstIn : PNET_IN_USERINFO_DO_FIND; pstOut : PNET_OUT_USERINFO_DO_FIND; nWaitTime: Integer): BOOL; cdecl;
As for the records, your translation of them is fine individually (though I see you changed some of the named constants into integer literals). But regarding the NET_OUT_USERINFO_DO_FIND.pstuInfo field specifically, yes it is a pointer to another record type, so you need to declare that record type beforehand, eg:
type
NET_ACCESS_USER_INFO = record
...
end;
PNET_ACCESS_USER_INFO = ^NET_ACCESS_USER_INFO;
...
NET_OUT_USERINFO_DO_FIND = record
...
pstuInfo: PNET_ACCESS_USER_INFO; // user info
...
end;
PNET_OUT_USERINFO_DO_FIND = ^NET_OUT_USERINFO_DO_FIND;
Or, at least forward-declare the pointer type, if not the record itself, eg:
type
PNET_ACCESS_USER_INFO = ^NET_ACCESS_USER_INFO;
...
NET_OUT_USERINFO_DO_FIND = record
...
pstuInfo: PNET_ACCESS_USER_INFO; // user info
...
end;
PNET_OUT_USERINFO_DO_FIND = ^NET_OUT_USERINFO_DO_FIND;
...
NET_ACCESS_USER_INFO = record
...
end;
Regarding the example code, your translation has a few minor typos/mistakes, try this instead:
var
aUserInfo: array of NET_ACCESS_USER_INFO;
DoFindNext: Boolean;
nRecordNum, I: Integer;
begin
...
DoFindNext := True;
repeat
try
SetLength(aUserInfo, 10);
try
nRecordNum := 0;
DoFindNext := UserFindNext(nStartNo, 10, PNET_ACCESS_USER_INFO(aUserInfo), nRecordNum);
// or:
// DoFindNext := UserFindNext(nStartNo, 10, #aUserInfo[0], nRecordNum);
for I := 0 to nRecordNum - 1 do
begin
//do something with the information...
Memo1.Lines.Add(aUserInfo[I].szName);
end;
Inc(nStartNo, nRecordNum);
finally
SetLength(aUserInfo, 0);
end;
except
DoFindNext := False;
end;
until not DoFindNext;
...
end;
function UserFindNext(nStartNo: Integer; nMaxNum: Integer; pstuAlarm: PNET_ACCESS_USER_INFO; var nRecordNum: Integer): Boolean;
const
SDK_API_WAIT = ...;
var
FindIn: NET_IN_USERINFO_DO_FIND;
FindOut: NET_OUT_USERINFO_DO_FIND;
begin
ZeroMemory(#FindIn, SizeOf(FindIn));
FindIn.dwSize := SizeOf(FindIn);
FindIn.nStartNo := nStartNo;
indIn.nCount := nMaxNum;
ZeroMemory(#FindOut, SizeOf(FindOut));
FindOut.dwSize := SizeOf(FindOut);
FindOut.nMaxNum := nMaxNum;
FindOut.pstuInfo := pstuAlarm;
if CLIENT_DoFindUserInfo(lFindID, #FindIn, #FindOut, SDK_API_WAIT) then
begin
if FindOut.nRetNum > 0 then
begin
nRecordNum := FindOut.nRetNum;
Result := True;
Exit;
end;
end
Result := False;
end;
#Remy-Lebeau I found It
I translate LLong to int64, but i misundertand the header,
for windows LLONG is mapped to LONG.
In Delphi i found LONG is LongInt;
I switch Int64 to LongInt and done;
Showmessage(IntTohex(CLIENT_GetLastError()));
This function help me a lot, your code correction was perfect, the dwSize must be informed before call the find function otherwise i got 1a7 error it means "dwSize" is not initialized in input param
almost one week to find this, thanks a lot for your help.
I want to change size of buffer array in Delphi program if C++ DLL function copies longer array.
Delphi Code:
function sendUserAllocatedArray(AllocatedArrayPtr: PAnsiChar; Length: Integer): Integer; cdecl;
external 'DLLlibrary.dll';
var
myCharPtr : PAnsiChar;
size : integer;
UserAllocatedArray: array[0..10] of AnsiChar;
arrayPtr: PAnsiChar;
begin
UserAllocatedArray := 'test123';
arrayPtr := UserAllocatedArray;
size := sendUserAllocatedArray(arrayPtr, Length(UserAllocatedArray));
end
C++ DLL:
extern "C" __declspec(dllexport) int sendUserAllocatedArray(char* data, int length);
int sendUserAllocatedArray(char* data, int length)
{
char char_array[] = "this array length is more than 10";
datLength = sizeof(char_array);
if(datLength < length)
strcpy_s(data, length, char_array);
else
....;
return length;
}
So i need to allocate more space if bigger buffer needed before DLL function copy the data to array.
Do i need handler. How can it be done.
Given the code you have shown, the only way to do what you want (without changing the DLL function's signature) is if the DLL function returns the required buffer length if the input buffer is too small, eg:
extern "C" __declspec(dllexport) int sendUserAllocatedArray(char* data, int length);
int sendUserAllocatedArray(char* data, int length)
{
char char_array[] = "this array length is more than 10";
int datLength = sizeof(char_array);
if ((data) && (datLength <= length))
memcpy(data, char_array, length);
return datLength;
}
Then the Delphi code can do this:
function sendUserAllocatedArray(AllocatedArrayPtr: PAnsiChar; Length: Integer): Integer; cdecl; external 'DLLlibrary.dll';
var
myCharPtr : array of AnsiChar;
size : integer;
UserAllocatedArray: array[0..10] of AnsiChar;
arrayPtr: PAnsiChar;
begin
UserAllocatedArray := 'test123';
size := sendUserAllocatedArray(UserAllocatedArray, Length(UserAllocatedArray));
if size <= Length(UserAllocatedArray) then
begin
arrayPtr := UserAllocatedArray;
end else
begin
SetLength(myCharPtr, size);
arrayPtr := PAnsiChar(myCharPtr);
StrLCopy(arrayPtr, UserAllocatedArray, size-1);
size := sendUserAllocatedArray(arrayPtr, size);
end;
// use arrayPtr up to size chars as needed...
end;
Which can be simplified to this:
function sendUserAllocatedArray(AllocatedArrayPtr: PAnsiChar; Length: Integer): Integer; cdecl; external 'DLLlibrary.dll';
var
size : integer;
UserAllocatedArray: array of AnsiChar;
begin
SetLength(UserAllocatedArray, 10);
StrLCopy(PAnsiChar(UserAllocatedArray), 'test123', Length(UserAllocatedArray)-1);
repeat
size := sendUserAllocatedArray(PAnsiChar(UserAllocatedArray), Length(UserAllocatedArray));
if size <= Length(UserAllocatedArray) then Break;
SetLength(UserAllocatedArray, size);
until False;
// use UserAllocatedArray up to size chars as needed...
end;
Or this:
function sendUserAllocatedArray(AllocatedArrayPtr: PAnsiChar; Length: Integer): Integer; cdecl; external 'DLLlibrary.dll';
var
size : integer;
UserAllocatedArray: array of AnsiChar;
begin
size := sendUserAllocatedArray(nil, 0);
SetLength(UserAllocatedArray, size);
StrLCopy(PAnsiChar(UserAllocatedArray), 'test123', size-1);
size := sendUserAllocatedArray(PAnsiChar(UserAllocatedArray), size);
// use UserAllocatedArray up to size chars as needed...
end;
It is widely used solution in APIs (which fills passed memory array up with contents) to interpret the memory array input parameter NIL value as a mode switch to calculate the necessary memory needs.
This fuction should:
Pass the memory needs back as a result.
Check whether the memory pointer NIL or not.
Check the passed memory array size and throw an exception in the right cases. (Comment : C has NOT exception mechanism! In case of C you must use special return values to inform the caller about the invalid input parameter value / processing errors)
Of course, if you call C/C++ DLL function, the call convension must be set. Most of the cases cdecl/stdcall.
The example code for C DLL function:
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TDLLFunction = function ( buffer_ : pointer; memSize_ : integer ) : integer; cdecl;
TForm3 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
fDLLFunction : TDLLFunction;
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
function testDLLFunction( mem_ : pchar; memSize_ : integer ) : integer; cdecl;
const
CONST_text_Viered : array [0..3] of char = '1234';
begin
result := ( length( CONST_text_Viered ) + 1 )*sizeOf( char );
if ( mem_ <> NIL ) then
if ( memSize_ >= result ) then
strPCopy( mem_, CONST_text_Viered )
else
result := -1;
end;
procedure TForm3.Button1Click(Sender: TObject);
var
memSize : integer;
memArray : pchar;
begin
memSize := fDLLFunction( NIL, 0 );
getMem( memArray, memSize );
try
if ( fDLLFunction( memArray, memSize ) >= 0 ) then
begin
// Do some usefull think with memArray
end else
showMessage( 'Unsufficient memory error!' );
finally
freeMem( memArray );
end;
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
fDLLFunction := #testDLLFunction;
end;
end.
Example code for C++ DLL function:
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TDLLFunction = function ( buffer_ : pointer; memSize_ : cardinal ) : cardinal; stdcall;
TForm3 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
fDLLFunction : TDLLFunction;
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
type
EUnsufficientMemoryError = class ( Exception )
public
constructor Create( currentSize_, minimumSize_ : cardinal );
end;
constructor EUnsufficientMemoryError.Create( currentSize_, minimumSize_ : cardinal );
begin
inherited Create( 'Unsufficient memory! Current size: ' + intToStr( currentSize_ ) + ' Minimum size: ' + intToStr( minimumSize_ ) );
end;
function testDLLFunction( mem_ : pchar; memSize_ : cardinal ) : cardinal; stdcall;
const
CONST_text_Viered : array [0..3] of char = '1234';
begin
result := ( length( CONST_text_Viered ) + 1 )*sizeOf( char );
if ( mem_ <> NIL ) then
if ( memSize_ >= result ) then
strPCopy( mem_, CONST_text_Viered )
else
raise EUnsufficientMemoryError.Create( memSize_, result );
end;
procedure TForm3.Button1Click(Sender: TObject);
var
memSize: cardinal;
memArray : pchar;
begin
memSize := fDLLFunction( NIL, 0 );
getMem( memArray, memSize );
try
try
fDLLFunction( memArray, memSize );
// Do some useful think with memArray
except
on e : EUnsufficientMemoryError do
//...
;
end;
finally
freeMem( memArray );
end;
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
fDLLFunction := #testDLLFunction;
end;
end.
I am trying to update table
CREATE TABLE some_table
(
id integer NOT NULL,
client_fid bigint NOT NULL,
index bytea[],
update_time timestamp without time zone
)
WITH (
OIDS = FALSE
using modified code snipped from here How to insert text array in PostgreSQL table in binary format using libpq?
#define BYTEAARRAYOID 1001
#define BYTEAOID 17
Here is a pgvals_t structure definition
struct pgvals_t
{
/* number of array dimensions */
int32_t ndims;
/* flag describing if array has NULL values */
int32_t hasNull;
/* Oid of data stored in array. In our case is 25 for TEXT */
Oid oidType;
/* Number of elements in array */
int32_t totalLen;
/* Not sure for this one.
I think it describes dimensions of elements in case of arrays storing arrays */
int32_t subDims;
/* Here our data begins */
} __attribute__ ((__packed__));
I've removed dataBegins pointer from struct as it affects data layout in memo
std::size_t nElems = _data.size();
uint32_t valsDataSize = sizeof(prx::pgvals_t) + sizeof(int32_t) * nElems +
sizeof(uint8_t)*nElems;
void *pData = malloc(valsDataSize);
prx::pgvals_t* pvals = (prx::pgvals_t*)pData;
/* our array has one dimension */
pvals->ndims = ntohl(1);
/* our array has no NULL elements */
pvals->hasNull = ntohl(0);
/* type of our elements is bytea */
pvals->oidType = ntohl(BYTEAOID);
/* our array has nElems elements */
pvals->totalLen = ntohl(nElems);
pvals->subDims = ntohl(1);
int32_t elemLen = ntohl(sizeof(uint8_t));
std::size_t offset = sizeof(elemLen) + sizeof(_data[0]);
char * ptr = (char*)(pvals + sizeof(prx::pgvals_t));
for(auto byte : _data){
memcpy(ptr, &elemLen, sizeof(elemLen));
memcpy(ptr + sizeof(elemLen), &byte, sizeof(byte));
ptr += offset;
}
Oid paramTypes[] = { BYTEAARRAYOID };
char * paramValues[] = {(char* )pData};
int paramLengths[] = { (int)valsDataSize };
int paramFormats[] = {1};
PGresult *res = PQexecParams(m_conn, _statement.c_str(),
1,
paramTypes,
paramValues,
paramLengths,
paramFormats,
1
);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
std::string errMsg = PQresultErrorMessage(res);
PQclear(res);
throw std::runtime_error(errMsg);
}
free(pData);
The binary data is contained in std::vector variable and am using the following query in a _statement variable of type std::string
INSERT INTO some_table \
(id, client_id, \"index\", update_time) \
VALUES \
(1, 2, $1, NOW())
Now after call to PQExecParams I am get an exception with message
"incorrect binary data format in bind parameter 1"
What can be the problem here?
If you want to pass a bytea[] in binary format, you have to use the binary array format as read by array_recv and written by array_send.
You cannot just pass a C array.
I'm struggling with HikVision SDK, trying to get it work in Delphi. There's some samples in C++, but I've stuck with pointers issues while translating to Delphi. Till that moment code seems to work, as I get correct plate number.
Is this correct equivalent: BYTE *pBuffer -> PByte ?
How to extract picture from pBuffer?
C++ code:
typedef struct tagNET_ITS_PICTURE_INFO
{
DWORD dwDataLen; //Media data length
BYTE byType;
BYTE byAbsTime[32];
NET_VCA_RECT struPlateRect;
BYTE *pBuffer; //Data pointer
DWORD dwUTCTime;
//...
}NET_ITS_PICTURE_INFO, *LPNET_ITS_PICTURE_INFO;
typedef struct tagNET_ITS_PLATE_RESULT
{
//...
NET_DVR_PLATE_INFO struPlateInfo;
NET_ITS_PICTURE_INFO struPicInfo[6];
}NET_ITS_PLATE_RESULT, *LPNET_ITS_PLATE_RESULT;
void CALLBACK MSesGCallback(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser)
{
char filename[100];
FILE *fSnapPic = NULL;
switch (lCommand) {
case COMM_ITS_PLATE_RESULT: {
NET_ITS_PLATE_RESULT struITSPlateResult = { 0 };
memcpy(&struITSPlateResult, pAlarmInfo, sizeof(struITSPlateResult));
printf("Plate: %s\n", struITSPlateResult.struPlateInfo.sLicense);
if (struITSPlateResult.struPicInfo[0].dwDataLen != 0)
{
sprintf(filename, "./pic/%d.jpg", 0);
fSnapPic = fopen(filename, "wb");
fwrite(struITSPlateResult.struPicInfo[0].pBuffer, struITSPlateResult.struPicInfo[0].dwDataLen, 1, fSnapPic);
fclose(fSnapPic);
}
}
}
return;
}
Delphi code:
LPNET_ITS_PICTURE_INFO = ^NET_ITS_PICTURE_INFO;
NET_ITS_PICTURE_INFO = record
dwDataLen: DWORD;
byType: BYTE;
byAbsTime: array [0..31] of BYTE;
struPlateRect: NET_VCA_RECT;
pBuffer: PByte; // ????????
dwUTCTime: DWORD;
//(...)
end;
LPNET_ITS_PLATE_RESULT = ^NET_ITS_PLATE_RESULT;
NET_ITS_PLATE_RESULT = record
//(...)
struPlateInfo: NET_DVR_PLATE_INFO;
struPicInfo: Array [0..5] of NET_ITS_PICTURE_INFO;
end;
procedure MessageCallBack(lCommand:longint; pAlarmer:LPNET_DVR_ALARMER; pAlarmInfo:PAnsiChar; dwBufLen:LongInt; pUser:pointer); stdcall;
var
struAlarmInfo: LPNET_DVR_ALARMINFO_V30;
struPlateResult: LPNET_ITS_PLATE_RESULT;
begin
case lCommand of
COMM_ITS_PLATE_RESULT:
begin
New(struPlateResult);
FillChar(struPlateResult^, SizeOf(NET_ITS_PLATE_RESULT), 0);
Move(pAlarmInfo^, struPlateResult^, Sizeof(NET_ITS_PLATE_RESULT));
Form1.memoOut.Lines.Add('sLicense: ' + struPlateResult.struPlateInfo.sLicense);
if (struPlateResult.struPicInfo[0].dwDataLen >0) then
??? how to get the picture from struPlateResult.struPicInfo[0].pBuffer ?
end;
end;
end;
Regards
Yes, PByte or PByteArray is correct translation.
If buffer contains valid file contents, you can save it into file:
FS := TFileStream.Create('test.jpg', fmCreate);
try
FS.Write(struITSPlateResult.struPicInfo[0].pBuffer^,
struITSPlateResult.struPicInfo[0].dwDataLen);
finally
FS.Free;
end;