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函数的主要内容,如果未能解决你的问题,请参考以下文章