C# 海康威视摄像头二次开发入门:HCNetSDK 登录、实时预览、云台控制、录像与抓图(WinForms 实战)
想在自己的程序里接入海康摄像头(实时画面、控制云台转向、录像、拍照),就绕不开海康官方的 HCNetSDK 。它本质是一套 C/C++ 编写的非托管 DLL,C# 通过 P/Invoke(
DllImport)来调用。本文用一个完整的 WinForms 练习程序,带你走完海康摄像头二开的标准流程:SDK 初始化 → 设备登录 → 实时预览 → 云台 PTZ 控制 → 录像 → JPEG 抓图,每一步都给出代码、关键参数和错误排查。适合第一次接触海康 SDK、有 C# 基础的初学者。
一、海康 SDK 二次开发准备
1.1 SDK 是什么、从哪获取
海康为各类网络摄像机、NVR、球机提供了统一的 设备网络 SDK(HCNetSDK)。我们常用的功能(登录、预览、回放、云台、布防、抓图、参数配置)都封装在里面。SDK 可以在海康威视开放平台下载,注意选择与自己程序匹配的版本(本文使用 64 位库)。
解压后的 SDK 里,开发主要用到两部分:
- 库文件(根目录的 dll) :如
HCNetSDK.dll、HCCore.dll、PlayCtrl.dll(播放库)、hlog.dll、hpr.dll、MP_Render.dll、SuperRender.dll以及libcrypto/libssl等依赖库; - HCNetSDKCom 文件夹 :按功能拆分的组件库,如
HCPreview.dll(预览)、HCPlayBack.dll(回放)、HCAlarm.dll(报警布防)、HCCoreDevCfg.dll(配置)等。
海康 SDK 还提供了各语言的调用示例,其中 C# 示例里的
CHCNetSDK.cs已经用DllImport把成百上千个函数、结构体、常量全部声明好了。我们直接把这个文件引入工程即可,不需要自己从头声明。
1.2 工程搭建(初学者最容易踩的环境坑)
- 新建 WinForms 项目(本文为 .NET Framework 4.7.2),把官方示例中的
CHCNetSDK.cs加入项目; - 平台必须和 DLL 一致 :64 位 DLL 就把项目目标平台设为
x64(项目属性 → 生成 → 目标平台),否则会报"无法加载 DLL"; - 把 SDK 的库文件 dll 和整个
HCNetSDKCom文件夹拷贝到程序运行目录(bin\Debug); - C# 调用非托管 DLL 的原理是 P/Invoke,形如:
csharp
// CHCNetSDK.cs 内部就是这样声明 SDK 函数的,DllImport 指定非托管 dll
[DllImport("HCNetSDK.dll")]
public static extern bool NET_DVR_Init();
只要理解"CHCNetSDK 这个静态类里封装了所有 SDK 函数,我们像调用普通 C# 静态方法一样调用它们"即可。
二、程序界面与运行效果
界面分为几个区域:
- 左侧一个大的
PictureBox:用来显示摄像头实时画面; - 登录授权、画面预览 按钮;
- 云台控制区 :上、下、左、右方向键,一个
Auto自动巡航按钮,以及一个云台速度下拉框(1~7 档); - Start Record / Stop Record:开始/停止录像;
- 拍照:抓取一张 JPEG。
【插图:程序运行效果图】
三、整体调用流程与两个关键"句柄"
海康 SDK 的调用有严格顺序,后面的功能依赖前面拿到的句柄:

两个必须理解的概念:
- 登录句柄
m_userId:登录成功后返回,代表"和这台设备建立的会话",抓图等设备级操作用它; - 预览句柄
m_lRealHandle:开始预览后返回,代表"这一路实时画面",云台、录像等针对画面的操作用它。
句柄返回 -1(或 0、false)表示失败 ,此时立刻调用 NET_DVR_GetLastError() 拿错误码排查。
窗体中先声明这两个句柄并初始化:
csharp
private int m_userId = -1; // 设备登录句柄
public int m_lRealHandle = -1; // 预览播放句柄
四、第一步:SDK 初始化
在窗体加载事件里初始化 SDK,并设置连接超时时间:
csharp
private void Form1_Load(object sender, EventArgs e)
{
// 初始化 SDK,整个程序只需要调用一次;返回 false 表示失败
bool initSuccess = CHCNetSDK.NET_DVR_Init();
if (!initSuccess)
{
MessageBox.Show($"sdk初始化失败,错误码:{CHCNetSDK.NET_DVR_GetLastError()}");
}
else
{
// 设置连接设备的等待时间(毫秒)与重试次数,还可按需配置断线重连等
CHCNetSDK.NET_DVR_SetConnectTime(2000, 1);
}
}
五、第二步:设备登录
通过 IP、端口(海康默认 8000 )、用户名、密码登录设备。登录成功会返回 m_userId,设备信息(含序列号)写入 NET_DVR_DEVICEINFO_V30 结构体:
csharp
private void button1_Click(object sender, EventArgs e)
{
// 准备设备信息结构体,登录成功后会被填充
CHCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V30();
// 登录:IP、端口、用户名、密码、设备信息(引用传递)
// 返回 -1 失败,其它值即用户 ID(登录句柄)
m_userId = CHCNetSDK.NET_DVR_Login_V30("192.168.215.64", 8000,
"admin", "你的密码", ref deviceInfo);
if (m_userId < 0)
{
int errCode = (int)CHCNetSDK.NET_DVR_GetLastError();
MessageBox.Show($"相机登录失败,错误码: {errCode}");
}
else
{
// sSerialNumber 是字节数组,转成字符串得到设备序列号
MessageBox.Show($"相机登录成功,设备序列号: " +
Encoding.ASCII.GetString(deviceInfo.sSerialNumber));
}
}
练习时把 IP、账号、密码直接写在代码里没问题;正式项目建议放到配置文件,并保证电脑和摄像头在同一网段、能 ping 通。
六、第三步:实时预览
登录成功后,填充 NET_DVR_PREVIEWINFO 结构体指定播放窗口、通道、码流、连接方式,然后调用 NET_DVR_RealPlay_V40 开始预览:
csharp
private void button2_Click(object sender, EventArgs e)
{
if (m_userId < 0)
{
MessageBox.Show("请先登录设备");
return;
}
CHCNetSDK.NET_DVR_PREVIEWINFO previewInfo = new CHCNetSDK.NET_DVR_PREVIEWINFO();
previewInfo.hPlayWnd = this.pictureBox1.Handle; // 画面渲染到哪个控件:给 PictureBox 的句柄
previewInfo.lChannel = 1; // 通道号,网络摄像机默认 1 通道
previewInfo.dwStreamType = 0; // 码流类型:0 主码流,1 子码流
previewInfo.dwLinkMode = 0; // 连接模式:0 TCP,1 UDP,...
// 开始实时预览:传入登录句柄、预览参数;回调传 null(直接渲染到窗口)
// 返回预览句柄,<0 表示失败
m_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V40(m_userId, ref previewInfo,
null, IntPtr.Zero);
if (m_lRealHandle < 0)
{
int errCode = (int)CHCNetSDK.NET_DVR_GetLastError();
MessageBox.Show($"实时预览失败,错误码:{errCode}");
}
}
如果不想让 SDK 直接渲染画面,而是想自己拿到每一帧码流(例如做转码、AI 分析),可以把第三个参数传一个码流回调函数,再配合播放库
PlayCtrl处理。入门阶段直接给窗口句柄最简单。
七、第四步:云台 PTZ 控制(方向 / 速度 / 自动巡航)
带云台的球机可以控制镜头转动。这里用的是 NET_DVR_PTZControlWithSpeed,函数签名为:
csharp
bool NET_DVR_PTZControlWithSpeed(int lRealHandle, uint dwPTZCommand,
uint dwStop, uint dwSpeed);
dwPTZCommand:动作命令(上/下/左/右/自动等);dwStop:0 = 启动动作,1 = 停止动作;dwSpeed:速度,1~7。
关键交互技巧:在方向按钮的 MouseDown(按下)事件里启动转动(dwStop=0),MouseUp(松开)事件里停止(dwStop=1),这样按住就转、松手即停。
以"向左"为例:
csharp
// 按下"左":开始向左转,速度取下拉框选中值
private void button5_MouseDown(object sender, MouseEventArgs e)
{
CHCNetSDK.NET_DVR_PTZControlWithSpeed(m_lRealHandle, (uint)CHCNetSDK.PAN_LEFT,
0, Convert.ToUInt32(comboBoxSpeed.SelectedItem));
}
// 松开"左":停止转动(dwStop=1)
private void button5_MouseUp(object sender, MouseEventArgs e)
{
CHCNetSDK.NET_DVR_PTZControlWithSpeed(m_lRealHandle, CHCNetSDK.PAN_LEFT,
1, Convert.ToUInt32(comboBoxSpeed.SelectedItem));
}
其余三个方向完全一样,只是命令常量不同:
csharp
// 向上
NET_DVR_PTZControlWithSpeed(m_lRealHandle, (uint)CHCNetSDK.TILT_UP, 0, speed);
// 向下
NET_DVR_PTZControlWithSpeed(m_lRealHandle, (uint)CHCNetSDK.TILT_DOWN, 0, speed);
// 向右
NET_DVR_PTZControlWithSpeed(m_lRealHandle, (uint)CHCNetSDK.PAN_RIGHT, 0, speed);
// (对应的 MouseUp 把第三个参数改为 1 即可)
常用 PTZ 命令常量(定义在 CHCNetSDK.cs):
| 命令常量 | 值 | 含义 |
|---|---|---|
TILT_UP |
21 | 向上 |
TILT_DOWN |
22 | 向下 |
PAN_LEFT |
23 | 向左 |
PAN_RIGHT |
24 | 向右 |
PAN_AUTO |
29 | 水平自动扫描 |
自动巡航用一个按钮在"启动/停止"之间切换:
csharp
private void button5_Click(object sender, EventArgs e)
{
if (btnAuto.Text == "Auto")
{
// 开启水平自动旋转,按钮文字切换为 Stop
CHCNetSDK.NET_DVR_PTZControlWithSpeed(m_lRealHandle, CHCNetSDK.PAN_AUTO,
0, Convert.ToUInt32(comboBoxSpeed.SelectedItem));
btnAuto.Text = "Stop";
}
else
{
// 再次点击:停止自动旋转
CHCNetSDK.NET_DVR_PTZControlWithSpeed(m_lRealHandle, CHCNetSDK.PAN_AUTO,
1, Convert.ToUInt32(comboBoxSpeed.SelectedItem));
btnAuto.Text = "Auto";
}
}
注意:云台控制必须在预览成功之后(需要预览句柄),且摄像头本身要支持云台(固定枪机调用会报错)。
八、第五步:录像(保存实时码流)
录像用 NET_DVR_SaveRealData 开始把实时码流写入文件,用 NET_DVR_StopSaveRealData 停止。同样要先开预览:
csharp
private string m_recordDir = Directory.GetCurrentDirectory() + "\\录像文件\\";
private void btnRecord_Click(object sender, EventArgs e)
{
if (m_lRealHandle < 0)
{
MessageBox.Show("请先开启画面预览!");
return;
}
// 目录不存在则自动创建
if (!Directory.Exists(m_recordDir))
Directory.CreateDirectory(m_recordDir);
// 用时间戳生成文件名,避免覆盖
string fileName = m_recordDir + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".mp4";
// 开始保存实时数据(录像)
bool result = CHCNetSDK.NET_DVR_SaveRealData(m_lRealHandle, fileName);
if (!result)
MessageBox.Show($"开始录像失败,错误码:{CHCNetSDK.NET_DVR_GetLastError()}");
else
MessageBox.Show($"录像已开始\n保存路径:{fileName}");
}
// 停止录像
private void StopRecord_Click(object sender, EventArgs e)
{
if (m_lRealHandle < 0) return;
bool result = CHCNetSDK.NET_DVR_StopSaveRealData(m_lRealHandle);
if (!result)
MessageBox.Show($"结束录像失败,错误码:{CHCNetSDK.NET_DVR_GetLastError()}");
else
MessageBox.Show("录像已结束,文件已保存");
}
九、第六步:JPEG 抓图(拍照)
抓图用 NET_DVR_CaptureJPEGPicture,它使用登录句柄 和通道号,并通过 NET_DVR_JPEGPARA 指定图片质量与分辨率:
csharp
private string m_rJPEGDir = Directory.GetCurrentDirectory() + "\\图片文件\\";
private void btnJPEG_Click(object sender, EventArgs e)
{
if (!Directory.Exists(m_rJPEGDir))
Directory.CreateDirectory(m_rJPEGDir);
// 用时间戳命名 jpg
string sJpegPicFileName = m_rJPEGDir + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".jpg";
int lChannel = 1; // 通道号
CHCNetSDK.NET_DVR_JPEGPARA lpJpegPara = new CHCNetSDK.NET_DVR_JPEGPARA();
lpJpegPara.wPicQuality = 0; // 图像质量
lpJpegPara.wPicSize = 0xff; // 抓图分辨率:0xff = Auto,使用当前码流分辨率
// JPEG 抓图:登录句柄、通道、参数、保存路径;返回 false 失败
if (!CHCNetSDK.NET_DVR_CaptureJPEGPicture(m_userId, lChannel,
ref lpJpegPara, sJpegPicFileName))
{
uint err = CHCNetSDK.NET_DVR_GetLastError();
MessageBox.Show("抓图失败, error code= " + err);
}
else
{
MessageBox.Show("抓图成功,文件已保存:" + sJpegPicFileName);
}
}
运行后即可在程序目录的 图片文件 文件夹看到拍下的 jpg 照片。
抓图还有一种方式是在预览回调里对当前帧抓图(
NET_DVR_CaptureJPEGPicture_NEW等),入门用上面的设备抓图接口即可。
十、资源释放与可改进点
本程序以"练通二开流程"为目的,功能跑通即可。工程上还可以再完善(了解即可):窗体关闭时应依次调用 NET_DVR_StopRealPlay(停止预览)、NET_DVR_Logout(登出)、NET_DVR_Cleanup(释放 SDK),并可加入断线重连、把账号参数移到配置文件等。这些属于健壮性优化,不影响理解核心调用流程。
十一、常见错误码与排查
任何 SDK 函数失败,都先用 NET_DVR_GetLastError() 取错误码,再对照官方《错误码对照表》。初学者高频问题:
| 现象 / 错误码 | 可能原因与处理 |
|---|---|
| 报"无法加载 DLL HCNetSDK" | 平台不匹配(x86/x64)、dll 或 HCNetSDKCom 没拷到运行目录、缺依赖库 |
| 错误码 7(连接失败) | 电脑和摄像头网络不通、IP 不在同一网段、端口不是 8000,先 ping 测试 |
| 错误码 1(密码错误) | 用户名/密码错误;多次输错设备会临时锁定 IP,需等待或重启设备 |
| 预览黑屏 / 报错 | 没登录成功、通道号不对、hPlayWnd 句柄错误、设备不支持该码流 |
| 云台控制报错 | 没有先预览(句柄无效)、设备是不带云台的固定枪机 |
| 录像/抓图找不到文件 | 路径用的是相对当前目录,确认程序实际运行目录及文件夹权限 |
排查口诀:先通网络(ping)→ 再对平台和 DLL → 然后看登录是否成功 → 最后看错误码。
十二、小结
本文用一个 WinForms 练习程序,完整走了一遍海康 HCNetSDK 二次开发流程:
- 引入官方
CHCNetSDK.cs封装,把 dll 和HCNetSDKCom放到运行目录、选对 x64 平台; NET_DVR_Init初始化、NET_DVR_Login_V30登录拿到m_userId;NET_DVR_RealPlay_V40把画面渲染到 PictureBox,拿到m_lRealHandle;NET_DVR_PTZControlWithSpeed配合 MouseDown/MouseUp 实现方向控制与速度调节,PAN_AUTO实现自动巡航;NET_DVR_SaveRealData / StopSaveRealData录像,NET_DVR_CaptureJPEGPicture抓 JPEG;- 任何失败都用
NET_DVR_GetLastError取错误码定位。
掌握这套"初始化---登录---预览---业务操作---释放"的固定套路后,再去做回放、报警布防、参数配置等更高级功能就会轻松很多。建议对照本文代码,接一台真实摄像头(或用海康模拟器)亲手跑一遍。
