**
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.
Related
I send the following c++ application request:
string data_to_send = "Hello World";
PCSTR lpszString = data_to_send.c_str();
COPYDATASTRUCT cds;
cds.dwData = 0; // can be anything
cds.cbData = sizeof(TCHAR) * (data_to_send.size());
cds.lpData = &lpszString;
cout << lpszString << endl;
SendMessage(Output, WM_COPYDATA, (WPARAM)Output, (LPARAM)(PVOID)&cds);
I get the structure using the following code in Delphi
var
p : PCopyDataStruct;
s : UTF8String;
begin
p := PCopyDataStruct(Message.lParam);
if (p <> nil) then
begin
SetString(s, PAnsiChar(p^.lpData), p^.cbData);
ShowMessage(s);
end else
inherited;
end;
The string looks wrong.
In the debugger, it is equal to the following
'l'#$FE#$F6#2'Hello World'#0#$10#3#$88'u'#$B#0
We see 22 bytes with an extra 4 bytes before the message.
If we use CHAR instead of TCHAR, then we see 11 bytes, but again with an offset of 4 bytes
#$18#$F9#$1B#3'Hello W'
Please, help!!!
UPDATE:
Thanks to Remy Lebeau for his help, his code does everything as it was originally intended and David Heffernan for the correct remark! They saved me. Here is the working code.
On the C++ side:
cds.dwData should not be 0. Use a more unique value, such as the result of calling RegisterWindowMessage(). Many apps, and even the VCL internally, use WM_COPYDATA for different purposes, so you don't want to get confused with someone else's message by mistake.
sizeof(TCHAR) should be sizeof(char) instead (or omitted entirely, since sizeof(char) is always 1).
cds.lpData = &lpszString; needs to be cds.lpData = lpszString; instead. You are sending the address of the lpszString variable itself, not the address of the character data it points at. That is why you are seeing garbage on the other end - you are seeing random bytes from the call stack where lpszString resides, which in your case includes the std::string object (whose internal members happen to include a Short-String-Optimization buffer, which is why you are also seeing your characters, too).
On the Delphi side:
you should validate p^.dwData is your unique number before processing the message any further. If the number does not match what you are expecting, pass the message to the inherited handler and move on.
UTF8String should be AnsiString, unless the sender's std::string is actually UTF-8 encoded.
Try this:
const UINT uMyDataID = RegisterWindowMessage(TEXT("MyDataID"));
...
if (uMyDataID != 0)
{
string data_to_send = u8"Hello World";
COPYDATASTRUCT cds;
cds.dwData = uMyDataID;
cds.cbData = sizeof(char) * data_to_send.size();
cds.lpData = const_cast<char*>(data_to_send.c_str());
// or: cds.lpData = data_to_send.data(); in C++17 and later
SendMessage(Output, WM_COPYDATA, reinterpret_cast<WPARAM>(Output), reinterpret_cast<LPARAM>(&cds));
}
var
uMyDataID: UINT = 0;
...
procedure TMyForm.WMCopyData(var Message: TMessage);
var
p : PCopyDataStruct;
s : UTF8String;
begin
p := PCopyDataStruct(Message.lParam);
if (uMyDataID <> 0) and (p <> nil) and (p^.dwData = uMyDataID) then
begin
SetString(s, PAnsiChar(p^.lpData), p^.cbData);
ShowMessage(s);
end else
inherited;
end;
...
initialization
uMyDataID := RegisterWindowMessage('MyDataID');
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 am trying to send CAN Frames with xlCanTransmit method. The problem is that it returns 0 (XL_SUCCESS), but the frame is empty. there are only zeroes in the Frame.
IVxlApi.s_xl_event eventMsg = new IVxlApi.s_xl_event();
...
...
xlStatus = dll.xlCanTransmit(GlobalConfig.g_xlPortHandle, GlobalConfig.g_xlChannelMask[channelIndex],
pEventCount, eventMsg.getPointer());
After Calling the function xlCanTransmit() the frame is sent, but there is no data in it. The function is called from dll file and you can't debug it.
My mapped Structure
public static class s_xl_event extends Structure {
public byte tag;
public byte chanIndex;
public byte[] transId = new byte[SHORT];
public byte[] portHandle = new byte[SHORT];
public byte flags;
public byte reserved;
public byte[] timeStamp = new byte[LONG];
public s_xl_tag_data tagData;
#Override
public void read() {
// read from native memory, populate tag
super.read();
// set union type based on tag
switch (tag) {
case XL_RECEIVE_MSG:
case XL_TRANSMIT_MSG:
tagData.setType(s_xl_can_msg.class);
break;
case XL_CHIP_STATE:
tagData.setType(s_xl_chip_state.class);
break;
case XL_LIN_MSG:
tagData.setType(s_xl_lin_msg_api.class);
break;
case XL_SYNC_PULSE:
tagData.setType(s_xl_sync_pulse.class);
break;
case XL_RECEIVE_DAIO_DATA:
tagData.setType(s_xl_daio_data.class);
break;
case XL_TRANSCEIVER:
tagData.setType(s_xl_transceiver.class);
break;
case XL_RECEIVE_DAIO_PIGGY:
tagData.setType(s_xl_daio_piggy_data.class);
break;
case XL_KLINE_MSG:
tagData.setType(s_xl_kline_data.class);
break;
default:
// add default type or throw exception etc.
tagData.setType(s_xl_can_msg.class);
break;
}
// now read tagData from native memory
tagData.read();
}
#Override
protected List<String> getFieldOrder() {
return Arrays.asList("tag", "chanIndex", "transId", "portHandle", "flags", "reserved", "timeStamp", "tagData");
}
}
CAN Frame in CANoe Trace
enter image description here
The function on the native side looks like this
DECL_STDXL_FUNC(xlCanTransmit, XLCANTRANSMIT, (
XLportHandle portHandle,
XLaccess accessMask,
unsigned int* pEventCount,
void* pEvents)
);
My mapping in java
short xlCanTransmit(long portHandle, long accessMask, IntByReference pEventCount, Pointer pEvents);
I wrote some random data manually and called the xlCanTransmit() function.
But in CANoe trace I see only empty frame with no data and id.
My send function in java
public short send(String txID, String dlc, String[] data, int channelIndex) {
short xlStatus = IVxlApi.XL_ERROR;
IntByReference pEventCount = new IntByReference(1);
IVxlApi.s_xl_event eventMsg = new IVxlApi.s_xl_event();
eventMsg.tag = IVxlApi.XL_TRANSMIT_MSG;
eventMsg.tagData.msg.id[0] = 2;// = Long.parseLong(txID);
eventMsg.tagData.msg.dlc[0] = 8;// = Short.parseShort(dlc);
eventMsg.tagData.msg.flags[0] = 0;
eventMsg.tagData.msg.data[0] = Byte.parseByte(data[0]);
eventMsg.tagData.msg.data[1] = Byte.parseByte(data[1]);
eventMsg.tagData.msg.data[2] = Byte.parseByte(data[2]);
eventMsg.tagData.msg.data[3] = Byte.parseByte(data[3]);
eventMsg.tagData.msg.data[4] = Byte.parseByte(data[4]);
eventMsg.tagData.msg.data[5] = Byte.parseByte(data[5]);
eventMsg.tagData.msg.data[6] = Byte.parseByte(data[6]);
eventMsg.tagData.msg.data[7] = Byte.parseByte(data[7]);
/*if(true){
eventMsg.tagData.msg.id |= IVxlApi.XL_CAN_EXT_MSG_ID;
}*/
eventMsg.write();
xlStatus = dll.xlCanTransmit(GlobalConfig.g_xlPortHandle, GlobalConfig.g_xlChannelMask[channelIndex],
pEventCount, eventMsg.getPointer());
eventMsg.read();
return xlStatus;
}
CAN Frame in CANoe Trace
enter image description here
After calling tagData.read() (line 475) the data initialized with zeroes
enter image description here
The return value of 0 indicates the function preformed properly on the native side, but you haven't copied the native memory back to Java.
This is normally done automatically when you pass a Structure in a function, but you have passed the Pointer to the structure instead. Your last argument is eventMsg.getPointer(). JNA doesn't know how the Pointer maps to your structure until you tell it to do so, and in the current way you're calling it requires using read() to copy the value back to JNA.
You could probably get the results you want by just executing eventMsg.read() after calling the function, but the better solition is to pass the Structure to the function in the first place and let JNA do the conversion to Pointer and auto-read() for you. Change your function call to pass eventMsg as the argument.
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;