c ++指向结构新数组的指针到delphi到DLL函数

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

对不起我的尤达英语,我会尽力的。

我正在尝试在我的 Delphi 应用程序中使用大华 SDK .dll,但我无法理解如何从一个 dll 函数进行一些转换。

为了给我的问题提供一些背景信息,我将尝试解释我正在尝试做的事情。

我需要从 dll 调用查找函数以从终端列出所有持卡人,因此 SDK 提供了一个带有 C++ 的 DLL header 和一个示例应用程序来解释如何在 Visual c++ 上使用它;

我的第一个问题是我正在使用 Delphi,我需要从 DLL 翻译 header 并将 C++ 代码转换为 Delphi;

DLL中的Find函数说明如下:



//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);



它接收查找句柄(lfindHandle),一个指向内部结构的指针(pstIn)和一个指向外部结构的指针(pstOut),最后一个参数是一个整数

内部结构给出了一些整数参数,例如,索引开始 e 最大数字 os 搜索和它的确定。

外部结构描述如下:

//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 

示例代码,创建一个指向结构数组的新指针并传递给要在 DLL 函数上调用的外部结构

        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; 


如果我理解正确,我会创建一个 NET_ACCESS_USER_INFO 的数组,然后我设置数组的大小和 将指向数组第一项的指针作为参数传递给 UserFindNext 函数和指向 nRecNum

的指针

在 UserFindNext 中,我创建内部和外部记录并提供数据以调用 DLL 函数

但我总是得到假;

我做错了什么?

可能是 NET_ACCESS_USER_INFO 记录有问题? 我在指导方面做得很好吗?

您对函数签名的翻译缺少 return 类型和调用约定。它应该看起来更像这样:

function CLIENT_DoFindUserInfo(lFindHandle : Int64; pstIn : PNET_IN_USERINFO_DO_FIND; pstOut : PNET_OUT_USERINFO_DO_FIND; nWaitTime: Integer): BOOL; cdecl;

至于记录,您对它们的单独翻译很好(尽管我看到您将一些命名常量更改为整数文字)。但是具体来说 NET_OUT_USERINFO_DO_FIND.pstuInfo 字段,是的,它是指向另一种记录类型的指针,因此您需要事先声明该记录类型,例如:

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;

或者,至少前向声明指针类型,如果不是 record 本身,例如:

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;

关于示例代码,您的翻译有一些小问题typos/mistakes,试试这个:

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我找到了

我将 LLong 翻译成 int64,但我误解了 header, 因为 windows LLONG 映射到 LONG。 在 Delphi 中我发现 LONG 是 LongInt; 我将 Int64 切换为 LongInt 并完成;

Showmessage(IntTohex(CLIENT_GetLastError()));

这个函数对我帮助很大,你的代码更正很完美,在调用 find 函数之前必须通知 dwSize 否则我得到 1a7 错误这意味着“dwSize”没有在输入参数中初始化

快一个星期才找到这个,非常感谢您的帮助。