c++ pointer to struct new array to delphi to a DLL function - c++

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.

Related

Setting up Delphi to call a 2 dimensional array in C++ DLL

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;

Pass array of structs from C++ to GO

I'm trying to get an array of tensorflow box predictions from C++ to golang, but I'm not able to do it no matter what I do. I have a GO program that calls a function that does tensorflow detections in C++ using cgo. This all works and I'm able to get the predictions in C++. The problem is to transfer these predictions into GO as an array of 100 structs that each hold one prediction.
I'm able to set a pointer in GO and use this pointer address to set one struct in C++. The code for this is seen below.
I want to set an array of structs in C++ and retreive this array in GO. I thought it should be easy to just use the same pointer address as earlier and use this as the address for my C++ array. Then I could restore the struct from the pointer in GO. Does anyone have a solution for this?
GO
type PredictResult struct {
Loc [4]float32
Score int
Label int
}
var predictions PredictResult
predictions_ptr := unsafe.Pointer(&predictions)
C.LIB_predict(predictions_ptr)
fmt.Println("GO predictions; ", predictions)
bridge.hpp
struct PredictResult{
float Loc[4];
int64_t Score;
int64_t Label;
};
void LIB_predict(void* predictions);
bridge.cpp
void LIB_predict(void* predictions){
PredictResult *p = (PredictResult*)predictions;
p->Score = 6;
p->Label = 95;
}
Prints:
GO predictions; {[0 0 0 0] 6 95}
Assuming your C function returns the array as PredictResult* and assuming you know the length of the returned array (in the example below I assume 10, but you can replace it by whatever works), this approach should work:
// #include <stdio.h>
// #include <stdlib.h>
//
// typedef struct PredictResult {
// float Loc[4];
// int64_t Score;
// int64_t Label;
// } PredictResult;
//
// PredictResult* getOneResult() {
// PredictResult* p = (PredictResult*)calloc(1, sizeof(PredictResult));
// p->Score = 10;
// p->Label = 99;
// p->Loc[1] = 2.5;
// p->Loc[3] = 3.5;
// return p;
// }
//
// PredictResult* getTenResults() {
// PredictResult* parr = (PredictResult*)calloc(10, sizeof(PredictResult));
// parr[0].Score = 10;
// parr[0].Label = 99;
// parr[0].Loc[1] = 2.5;
// parr[0].Loc[3] = 3.5;
//
// parr[4].Score = 44;
// parr[4].Label = 123;
// parr[4].Loc[1] = 12.25;
// parr[4].Loc[3] = -40.5;
// return parr;
// }
//
//
import "C"
type PredictResult C.struct_PredictResult
func main() {
p := C.getOneResult()
if p == nil {
log.Fatal("got nil")
}
pp := (*PredictResult)(p)
fmt.Println(pp)
parr := C.getTenResults()
if parr == nil {
log.Fatal("got nil")
}
pslice := (*[1 << 28]PredictResult)(unsafe.Pointer(parr))[:10:10]
fmt.Println(pslice)
}
What you'll be most interested in is how the result of getTenResults is converted to a Go slice of the appropriate struct type. This is employing the technique recommended on the Go wiki.
Depending on the exact signature of your C function you may need to write a "bridge" function in the import "C" part to provide the data as convenient to Go, but this is the basic gist of it.
As an alternative, if you wish to allocate the slice on the Go side and pass in a pointer to C to populate, you can do this:
// void PopulateTenResults(void* arr) {
// PredictResult* parr = (PredictResult*)arr;
// parr[1].Score = 210;
// parr[1].Label = 299;
// parr[1].Loc[1] = 22.5;
// parr[1].Loc[3] = 23.5;
//
// parr[8].Score = 344;
// parr[8].Label = 3123;
// parr[8].Loc[1] = 312.25;
// parr[8].Loc[3] = -340.5;
// }
//
//
import "C"
And then in Go do:
prslice := make([]PredictResult, 10)
C.PopulateTenResults(unsafe.Pointer(&prslice[0]))
fmt.Println(prslice)
Of course the hard-coded 10 is just for simplicity here; you could pass the length of arr as a parameter to C.
You can pass a pointer to the first element in a slice and the length of the slice to C++ and treat it like a C-style array.

CPP to Delphi Convertion

**
SOLVED
**
Thanks to the Rob for bringing light to my problem.
Create a global var on component unit fDict
Implement 3 methods, one to add, one to remove and one to retrive;
making the main class create , clear and destroy the global var to avoid memory leaks
making the connectin function of TDispositivo to feed the fDict
making the callback to retrive and move on.
making the disconnect function of TDispositivo to remove the reference in fDict keeping it updated
**
EDIT
**
I found my crash error, i´m implementing the dll inside a component class and try to declare the callback procedures inside the class using
TSnapRev = procedure(XXXXX) of object stdcall;
...
private
procedure callbacksnapshot(XXXXX); stdcall;
...
CLIENT_SetSnapRevCallBack(callbackSnapshot,FSDKUSer);
the callbacksnapshot was triggered but raising a accesse violation after.
O realize the order of variables returned was always misplaced, then i change my approuch moving the callbacksnapshot procedure out of the class and changing type declaration like this:
Type
TSnapRev = procedure(XXXXX); stdcall;
TDispositivo = class
private
end;
procedure callbacksnapshot(XXXXX); stdcall;
...
CLIENT_SetSnapRevCallBack(#callbackSnapshot,FSDKUSer);
...
now the callback triggeing, the order of variables are right and no access violation after, BUT, i need to invoke some functions (events, etc) from my TDispositivo instance or from the owner of TDispositivo instance, in a outside procedure i can´t.
How can i do this into my class ?
i had tryed declare a variable FSDKSnap : TRevSap (when TRevSap is a type of object) but got the same erros or miplaced vars and access violations
**
ORIGINAL
**
Keep working on my Dahua dll convertion to Delphi. Now i'm stuck in a callback, actually in a definition of the type used.
There is the cpp code in header
#define BYTE unsigned char
#define UINT unsigned int
// Snapshot parameter structure
typedef struct _snap_param
{
unsigned int Channel; // Snapshot channel
unsigned int Quality; // Image quality:level 1 to level 6
unsigned int ImageSize; // Video size;0:QCIF,1:CIF,2:D1
unsigned int mode; // Snapshot mode;0:request one frame,1:send out requestion regularly,2: Request consecutively
unsigned int InterSnap; // Time unit is second.If mode=1, it means send out requestion regularly. The time is valid.
unsigned int CmdSerial; // Request serial number
unsigned int Reserved[4];
} SNAP_PARAMS, *LPSNAP_PARAMS;
// Snapshot callback function original shape
// Encode Type 10: jpeg 0: number i frame of mpeg4
typedef void (CALLBACK *fSnapRev)(LLONG lLoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, DWORD CmdSerial, LDWORD dwUser);
// Set snapshot callback function
CLIENT_NET_API void CALL_METHOD CLIENT_SetSnapRevCallBack(fSnapRev OnSnapRevMessage, LDWORD dwUser);
// Snapshot request
CLIENT_NET_API BOOL CALL_METHOD CLIENT_SnapPicture(LLONG lLoginID, SNAP_PARAMS par);
Looking to the cpp code, it defined BYTE as unsigned char, so mapping to Delphi (Byte), UINT mapping to Cardinal.
The structure was mapped to Cardinal as well.
---> First question HERE
The callback fSnapRev has (BYTE *pBuf) so i suppose is a pointer to byte (Delphi PByte) and i suspect here is the problem. A buffer with so little memory is not right, maybe a array of bytes or array of pansichar. How can i do know that ? like try and miss ? I accept suggestions.
The RevLen and Encode Type was mapped to Cardinal.
There is the cpp code from demo application.
//First part of the method to initialize the dll,
BOOL ret = CLIENT_Init(DisConnectFunc, (LDWORD)this);
if (ret)
{
CLIENT_SetSnapRevCallBack(SnapPicRet,(LDWORD)this);
//here defining the callback
CLIENT_SetAutoReconnect(ReConnectFunc, (LDWORD)this);
}
else
{
MessageBox(ConvertString("initialize SDK failed!"), ConvertString("prompt"));
}
//the callback
void CALLBACK SnapPicRet(LLONG ILoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, DWORD CmdSerial,
LDWORD dwUser)
{
CCapturePictureDlg *pThis = (CCapturePictureDlg*)dwUser;
pThis->OnOnePicture(ILoginID,pBuf,RevLen,EncodeType,CmdSerial);
}
void CCapturePictureDlg::OnOnePicture(LLONG ILoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, UINT
CmdSerial)
{
//Get file path
char path[1000];
int filelen = GetModuleFileName(NULL,path,1000);
int i = filelen;
while(path[i]!='\\')
{
i--;
}
path[i + 1] = '\0';
//Get file name
CString filepath(path);
CString filename = "mpeg4.JPG";
CString strfile = filepath + filename;
char *pFileName = strfile.GetBuffer(200);
/* Save image original file */
FILE *stream;
if( (stream = fopen((const char*) pFileName, "wb")) != NULL )
{
int numwritten = fwrite( pBuf, sizeof( char ), RevLen, stream );
fclose( stream );
}
/*Veirfy image encode type. If it is an I frame of mpeg4,then call I frame to decode to BMP to display.*/
if ( EncodeType == 0)
{
//int iRet = IFrameToBmp("tmp.bmp",pFileName);
//if (iRet == 1)
//{
// ShowBitmap("tmp.bmp");
//}
}
else if (EncodeType == 10)
{
//ShowBitmap(pFileName);
ShowSnapImage(pFileName);
}
}
In OnOnePicture the first code is to define the path to image file be stored, then write the buffer in a file stream. if the buffer and size comes from pBuf and RevLen, then i can write this on a TMemoryStream and assign to TJpegImage but still do not know what pBuf really is.
Another ocuard thing is the data, revLen returns value 10 and is too small to be a byte length of the image.
My Delphi Code
TSnapRev = procedure(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD) of object stdcall;
// Snapshot parameter structure
PSnapParams = ^TSnapParams;
TSnapParams = record
Channel: Cardinal; // Snapshot channel
Quality: Cardinal; // Image quality:level 1 to level 6
ImageSize: Cardinal; // Video size;0:QCIF,1:CIF,2:D1
mode: Cardinal; // Snapshot mode;0:request one frame,1:send out requestion regularly,2: Request consecutively
InterSnap: Cardinal; // Time unit is second.If mode=1, it means send out requestion regularly. The time is valid.
CmdSerial: Cardinal; // Request serial number
Reserved: array[0..3] of Cardinal;
end;
// Snapshot request
function CLIENT_SnapPicture(lLoginID: Integer; par: TSnapParams): Boolean; stdcall; external 'dhnetsdk.dll';
procedure callbackSnapshot(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD); stdcall;
//Part of my initDll function
FInicializado := CLIENT_Init(callbackDis,FSDKUser);
if FInicializado then
begin
CLIENT_SetSnapRevCallBack(callbackSnapshot,FSDKUSer);
end;
Result := FInicializado;
//Snapshot wrapper procedure
procedure TDispositivo.SDKSnapshot;
var
sIn: TSnapParams;
begin
ZeroMemory(#sIn, SizeOf(sIn));
With sIn do
begin
Channel := 1;
mode := 0;
CmdSerial := 0;
if not CLIENT_SnapPicture(FLoginHandler,sIn) then
begin
doErro(999,'Não foi possível tirar o snapshot');
end;
end;
end;
//the callback implementation
procedure TDispositivo.callbackSnapshot(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD);
var
F: TMemoryStream;
jpg : TJpegImage;
begin
Showmessage('Tamanho do buffer: ' + SizeOf(pBuf).ToString + #13 +
'RevLen: ' + RevLen.ToString + #13 +
'Tipo: ' + EncodeType.ToString + #13 +
'CmdSerial: ' + CmdSerial.ToString
);
F := TMemoryStream.Create;
jpg := TJPEGImage.Create;
Try
F.Write(pBuf,RevLen);
//F.Write(pBuf^,RevLen);
F.Position:= 0;
jpg.LoadFromStream(F);
fOwner.doSnapshot(self,jpg);
Finally
FreeAndNil(F);
FreeAndNil(jpg);
End;
end;
When i call SDKSnapshot, i got the "showmessage" placed in callbackfunction with the values, but im debug mode i got infinite access violation, on Release mode the application close after the showmessage.
I try to do nothing on callback but still getting access violation and termination.
I´m wondering if my problem is the declaration of the callback procedure, pBuf.
I am posting a different answer because this is a different question now.
Your callback is a standard procedure and does not accept an object reference (and can't be called as an object method which it wasn't when I checked your previous post as this would be one of the key reasons why variables don't match up).
You need to be able to look up the object from some information that you have that will be unique in the callback. Without knowing more details about the API you're calling it looks like that would either be LLoginId or dwUser in your case.
I would probably implement a TDictionary with a lookup to match the unique code to the object you're using. Remember to remove objects from the TDictionary when you're not using them!
Assuming you can use dwUser your code may be something like:
implementation
uses Systm.Generics.Collections;
var
gClassDict: TDictionary<DWORD, TDispositivo>;
procedure RegisterInDictionary(dwUser: DWORD, class: TDispitivo);
begin
if(gClassDict=nil) then
gClassDict:=TDictionary<DWORD, TDispositivo>.Create;
gClassDict.AddOrSetValue(DWORD, class);
end;
function GetClassFromDictionary(dwUser: DWORD): TDispotivo;
begin
if( (gClassDict=nil) Or
(not(gClassDict.TryGetValue(dwUser, Result))) ) then
Result:=nil;
end;
Then in your callback call GetClassFromDictionary to obtain your object.
I suspect your LLoginId shoud be an Int64 .. LLONG is going to be defined in the compiler dialect / system includes for your source platform. (See: long long in C/C++)
Your errors indicate that the parameters are holding garbage (see below) which would happen if they are out of sequence - which probably means that you have the wrong parameter sizes (hence LLoginId not reading all of the value from the stack, all variables that follow it will be corrupted).
You can try making LLogin Int64, or if you want to invesitigate further you can try the following:
It seems that you are successfully calling the callback routine, but that the variables you receive in the routine are incorrect. (You say that you have revLen only 10, and the access violation errors indicate that pBuf is probably pointing at garbage).
You have declared the routine as stdcall which should be correct, and parameters will be passed on the stack in the normal "C" convention.
If you break on the entry to the routine and look in CPU view you should see that the variables are read from the stack, and determine that each one is set correctly. You should be able to put the address of pBuf into the memory window and determine if it is pointing at JPEG data from the JPEG ssignature.
You can also look at the memory dump of the stack to check what has been passed to you and see if you can identify where the variables you need really are.

How to send new size of array from C++ DLL to Delphi before function return

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.

Problem with SDK pointers - c++ to Delphi translating

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;