2
votes

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

2 Answers

2
votes

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;
0
votes

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