SDK源文件直接取的 "Demo实例\3- C# 开发示例\3-回放下载\NVRCsharpDemo\CHCNetSDK.cs"。因为里面没有我做项目时需要的下载图片功能,所以对该文件进行了修改。参照SDK开发文档里的 “设备网络SDK使用手册.chm”,添加了
结构体:NET_DVR_ADDRESS、NET_DVR_FIND_PICTURE_V50、NET_DVR_FACE_EXTRA_INFO、NET_DVR_PIC_EXTRA_INFO_UNION、NET_DVR_PIC_PARAM;
函数:NET_DVR_FindPicture、NET_DVR_FindNextPicture_V50、NET_DVR_GetPicture_V50、NET_DVR_GetPicture、NET_DVR_CloseFindPicture,并完善了结构体 NET_DVR_FIND_PICTURE_PARAM (不补充完整的话貌似会出现莫名其妙的错误,例如null错误)。
在调用函数 NET_DVR_GetPicture_V50 时,遇到了内存溢出问题和 "在多字节的目标代码页中,没有此Unicode字符可以映射到的字符"。然后仔细审查了函数NET_DVR_GetPicture_V50:
其c++定义为:
NET_DVR_GetPicture_V50(LONG lUserID, LPNET_DVR_PIC_PARAM lpPicParam)
结构体NET_DVR_PIC_PARAM 的c++定义为:
typedef struct tagNET_DVR_PIC_PARAM //查找结果结构体
{
char *pDVRFileName; //图片名,包含\0,最大64字节
char *pSavedFileBuf; //保存图片的缓冲区,内存外部申请释放
DWORD dwBufLen; //缓冲区大小
DWORD *lpdwRetLen; //实际收到的数据长度指针,不能为NULL
NET_DVR_ADDRESS struAddr; //图片所在的地址信息,图片查找时会返回
BYTE byRes[256]; //保留字节
}NET_DVR_PIC_PARAM, *LPNET_DVR_PIC_PARAM;
利用工具转换c#代码为:
//函数
public static extern bool NET_DVR_GetPicture_V50(Int32 lUserID, ref NET_DVR_PIC_PARAM lpPicParam);
//结构体
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct NET_DVR_PIC_PARAM
{
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string pDVRFileName;
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string pSavedFileBuf;
public uint dwBufLen;
public IntPtr lpdwRetLen;
public NET_DVR_ADDRESS struAddr;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256, ArraySubType = UnmanagedType.I1)]
public byte[] byRes;
}
在函数NET_DVR_GetPicture_V50调用之前,要对其参数 lpPicParam做一个初始化,在c++中是这样的;(temp就是要传入的变量)
//初始化变量
temp.pDVRFileName = struSavePictrue.sFileName;
//申请内存
temp.pSavedFileBuf = (char *)malloc(struSavePictrue.dwFileSize);
temp.dwBufLen = struSavePictrue.dwFileSize;
temp.struAddr = struSavePictrue.struAddr;
很明显 pSavedFileBuf 是一个指向一段内存的指针,但是在转换c#代码的时候却将其转换为了 string 类型,然后导致调用出错。(不知道这样说对不对///)在经过无数次尝试后,最终通过更改结构体定义和插入unsafe代码解决了这个问题:
修改后的结构体定义:
[StructLayoutAttribute(LayoutKind.Sequential)]
unsafe public struct NET_DVR_PIC_PARAM
{
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string pDVRFileName;
public char* pSavedFileBuf;
public uint dwBufLen;
public IntPtr lpdwRetLen;
public NET_DVR_ADDRESS struAddr;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256, ArraySubType = UnmanagedType.I1)]
public byte[] byRes;
}
修改后在c#中的调用:
//初始化变量
temp.pDVRFileName = struSavePictrue.sFileName;
//申请内存
unsafe
{
//需要判断 (int)struSavePictrue.dwFileSize 是否大于零, 否则可能会触发内存泄漏
temp.pSavedFileBuf = (char *)Marshal.AllocHGlobal((int)struSavePictrue.dwFileSize);
}
temp.dwBufLen = struSavePictrue.dwFileSize;
temp.struAddr = struSavePictrue.struAddr;
最后还要对申请的内存做释放,防止内存泄漏:
//释放内存
unsafe
{
Marshal.FreeHGlobal((IntPtr)temp.pSavedFileBuf);
}
仅作为一次记录,有更好的方法欢迎一起交流。