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

Posted

技术标签:

【中文标题】c ++指针将结构新数组指向delphi到DLL函数【英文标题】:c++ pointer to struct new array to delphi to a DLL function 【发布时间】:2021-02-19 18:17:34 【问题描述】:

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

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

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

我需要从 dll 调用 find 函数来列出终端中的所有 carduser,因此 SDK 提供了一个带有 C++ 标头的 DLL 和一个示例应用程序来解释如何在 Visual c++ 上使用它;

我的第一个问题是我正在使用 Delphi,我需要从 DLL 转换头文件并将 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),最后一个参数是一个整数

Inner 结构给出了一些整数参数,例如 index start e max numbers os searchs and its ok.

外部结构描述如下:

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

但我总是得到 False;

我做错了什么?

NET_ACCESS_USER_INFO 记录中可能有问题吗? 我在指针方面做得对吗?

【问题讨论】:

【参考方案1】:

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

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;

关于示例代码,您的翻译有一些小错别字/错误,请尝试以下操作:

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;

【讨论】:

首先,感谢您的回复,我之前阅读了很多您的文字并帮助了我很多(idHttp)。我像你说的那样更改了我的代码,没有错误,但在 CLIENT_DoFindUserInfo 上仍然得到 False,可能是我的 NET_ACCESS_USER_INFO 记录声明中的一些错误? 声明对我来说看起来不错,因此可能是对齐问题,导致 Delphi 记录的字节大小与 C 结构不同。结构中的dwSize 字段通常用于版本控制和大小验证,因此CLIENT_DoFindUserInfo() 可能会失败,因为dwSize 的值是错误的。尝试在记录上使用packed$ALIGN SDK 是否没有提供一种方法来确定为什么 CLIENT_DoFindUserInfo() 返回 false?任何类型的错误代码? SDK 提供了一个函数来初始化,另一个函数用来连接并返回一个处理程序和一个错误代码,他们使用 3 个函数来查找记录,第一个开始查找给出开始 fin 参数并返回一个LLONG 作为查找的标识符,第二个是返回布尔值的方法,最后一个函数停止查找。我会尝试将记录更改为已打包。我试过这个''' DoFindNext := True;重复尝试 SetLength(aUserInfo, 10);对于 i := 0 到 9 做 ZeroMemory(@aUserInfo[i],SizeOf(aUserInfo[i]));尝试 nRecordNum := 0; '''但没有成功 连接函数,接收一个指向结构的指针并且工作正常,所以我关注并理解 find 函数,因为它使用不同的指针,指向一个记录数组。【参考方案2】:

@Remy-Lebeau 我找到了它

我将 Llong 翻译为 int64,但我误解了标题, 对于 windows LLONG 映射到 LONG。 在德尔福我发现 LONG 是 LongInt; 我将 Int64 切换到 LongInt 并完成;

Showmessage(IntTohex(CLIENT_GetLastError()));

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

差不多一周才能找到这个,非常感谢您的帮助。

【讨论】:

以上是关于c ++指针将结构新数组指向delphi到DLL函数的主要内容,如果未能解决你的问题,请参考以下文章

c语言如何给结构体指针赋值?

如何将 C 指针编组到 C# 结构数组

C语言 利用指向结构体数组的指针将3个学生的全部信息输出

C语言 利用指向结构体数组的指针将3个学生的全部信息输出

在C中读取和写入结构到管道

如何访问 C 中指向列表的指针结构数组?