2

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 ?

2 Answers 2

2

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;
Sign up to request clarification or add additional context in comments.

9 Comments

First of all, Thanks for reply, i read a lot of your texts befor and help me a lot (idHttp). I change my code like u say, no erros, but still getting False on CLIENT_DoFindUserInfo, can be some error on my NET_ACCESS_USER_INFO record declaration ?
The declarations look fine to me, so it is likely an alignment issue, causing the Delphi records to have different byte sizes than the C structs. A dwSize field in a struct is commonly used for versioning and size verification, so it is possible that CLIENT_DoFindUserInfo() is failing because the dwSize value is wrong. Try using packed or {$ALIGN} on the records.
Does the SDK not provide a way of determining WHY CLIENT_DoFindUserInfo() is returning false? Any kind of error code?
The SDK provides a function to init, another one to connect and return a handler and a error code, to find record they use 3 functions, first one start the find giving the start fin parameters and returns a LLONG as a identifier of the find, the second one is that one how returns a boolean and the last function stop the find. I will try change the records to packed. I tryed this ´´´ DoFindNext := True; repeat try SetLength(aUserInfo, 10); for i := 0 to 9 do ZeroMemory(@aUserInfo[i],SizeOf(aUserInfo[i])); try nRecordNum := 0; ´´´ but not sucess
The connection function, receives a pointer to a structure and works fine, so i focus and undertand the find function cause it uses a diferent pointer, to a array of records.
|
0

@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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.