恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
海康工业相机采集显示到Halcon窗口示例
首页
资讯中心
/
海康工业相机采集显示到Halcon窗口示例
海康工业相机采集显示到Halcon窗口示例
发布时间:2026/10/14 19:44:27
下面是一个完整的 WinForms 示例涵盖从设备枚举、下拉列表绑定、打开设备、连续采集显示到关闭设备的完整流程。一、环境准备在 Visual Studio 项目中添加以下引用DLL路径用途MvCameraControl.Net.dllMVS\MVS\Development\DotNet\x64海康相机 SDK 托管封装halcondotnet.dllHalcon\bin\dotnet35或dotnet64Halcon .NET 接口项目平台目标必须设为x64。相机设备为MVS虚拟设备。usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.InteropServices;usingSystem.Threading;usingSystem.Windows.Forms;usingMvCamCtrl.NET;usingHalconDotNet;二、界面控件在 Form 上放置ComboBox— 命名为cmbDevices用于显示设备列表Button— 命名为btnEnum文本“枚举设备”Button— 命名为btnOpen文本“打开设备”Button— 命名为btnStart文本“开始采集”Button— 命名为btnStop文本“停止采集”Button— 命名为btnClose文本“关闭设备”HSmartWindowControl— 命名为hSmartWindowControl1Dock 设为 Fill将halcondotnet.dll添加到工具箱后HSmartWindowControl控件会出现在工具箱中拖入窗体即可。三、完整代码publicpartialclassForm1:Form{privateMyCamera_cameranull;privateMyCamera.MV_CC_DEVICE_INFO_LIST_deviceList;privateListMyCamera.MV_CC_DEVICE_INFO_deviceInfosnewListMyCamera.MV_CC_DEVICE_INFO();privateMyCamera.MV_CC_DEVICE_INFO_selectedDeviceInfo;// 采集控制privatebool_isGrabbingfalse;privateThread_grabThread;privatereadonlyobject_displayLocknewobject();// 回调委托必须声明为成员变量防止被 GC 回收privateMyCamera.cbOutputExdelegate_imgCallback;privateGCHandle_gch;publicForm1(){InitializeComponent();}// // 1. 枚举设备 → 绑定到下拉列表// privatevoidbtnEnum_Click(objectsender,EventArgse){cmbDevices.Items.Clear();_deviceInfos.Clear();_deviceListnewMyCamera.MV_CC_DEVICE_INFO_LIST();intnRetMyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE|MyCamera.MV_USB_DEVICE,ref_deviceList);if(nRet!MyCamera.MV_OK||_deviceList.nDeviceNum0){MessageBox.Show(未发现设备错误码: nRet.ToString(X8));return;}// 遍历非托管设备列表转换为托管结构体for(inti0;i_deviceList.nDeviceNum;i){vardevInfo(MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(_deviceList.pDeviceInfo[i],typeof(MyCamera.MV_CC_DEVICE_INFO));_deviceInfos.Add(devInfo);// 根据设备类型提取序列号/名称作为下拉列表显示文本stringdisplayName;if(devInfo.nTLayerTypeMyCamera.MV_GIGE_DEVICE){vargigeInfo(MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(devInfo.SpecialInfo.stGigEInfo,typeof(MyCamera.MV_GIGE_DEVICE_INFO));displayNamegigeInfo.chUserDefinedName!?gigeInfo.chUserDefinedName:gigeInfo.chSerialNumber;}elseif(devInfo.nTLayerTypeMyCamera.MV_USB_DEVICE){varusbInfo(MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(devInfo.SpecialInfo.stUsb3VInfo,typeof(MyCamera.MV_USB3_DEVICE_INFO));displayNameusbInfo.chUserDefinedName!?usbInfo.chUserDefinedName:usbInfo.chSerialNumber;}cmbDevices.Items.Add($[{i}]{displayName});}cmbDevices.SelectedIndex0;}// // 2. 打开设备// privatevoidbtnOpen_Click(objectsender,EventArgse){if(cmbDevices.SelectedIndex0){MessageBox.Show(请先枚举并选择设备);return;}if(_camera!null){MessageBox.Show(设备已打开请先关闭);return;}_selectedDeviceInfo_deviceInfos[cmbDevices.SelectedIndex];_cameranewMyCamera();intnRet_camera.MV_CC_CreateDevice_NET(ref_selectedDeviceInfo);if(nRet!MyCamera.MV_OK){MessageBox.Show(创建句柄失败: nRet.ToString(X8));_cameranull;return;}nRet_camera.MV_CC_OpenDevice_NET();if(nRet!MyCamera.MV_OK){MessageBox.Show(打开设备失败: nRet.ToString(X8));_camera.MV_CC_DestroyDevice_NET();_cameranull;return;}// 设置连续采集模式关闭触发_camera.MV_CC_SetEnumValue_NET(TriggerMode,(uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);MessageBox.Show(设备已打开);}// // 3. 连续采集到 HSmartWindowControl回调方式// privatevoidbtnStart_Click(objectsender,EventArgse){if(_cameranull){MessageBox.Show(请先打开设备);return;}// 注册回调必须声明为成员变量防止 GC_imgCallbacknewMyCamera.cbOutputExdelegate(OnImageGrabbed);_gchGCHandle.Alloc(_imgCallback);_camera.MV_CC_RegisterImageCallBackEx_NET(_imgCallback,IntPtr.Zero);intnRet_camera.MV_CC_StartGrabbing_NET();if(nRet!MyCamera.MV_OK){MessageBox.Show(开始采集失败: nRet.ToString(X8));return;}_isGrabbingtrue;MessageBox.Show(已开始连续采集);}/// summary/// 回调函数运行在 SDK 非托管线程上/// 原则只做深拷贝 入队不做耗时操作/// /summaryprivatevoidOnImageGrabbed(IntPtrpData,refMyCamera.MV_FRAME_OUT_INFO_EXpFrameInfo,IntPtrpUser){if(!_isGrabbing)return;if(pFrameInfo.nFrameLen0)return;// 过滤空帧intwidthpFrameInfo.nWidth;intheightpFrameInfo.nHeight;intframeLen(int)pFrameInfo.nFrameLen;varstFrameInfopFrameInfo;// ① 深拷贝到托管内存回调返回后 SDK 缓冲区可能被覆写byte[]buffernewbyte[frameLen];Marshal.Copy(pData,buffer,0,frameLen);// ② 交给独立线程处理不阻塞回调ThreadPool.QueueUserWorkItem(_{try{DisplayImage(buffer,width,height,stFrameInfo.enPixelType);}catch{/* 忽略单帧异常 */}});}/// summary/// 将原始图像数据转换为 HObject 并显示到 HSmartWindowControl/// /summaryprivatevoidDisplayImage(byte[]buffer,intwidth,intheight,uintpixelType){HObjectho_Imagenull;try{// 获取非托管指针GCHandlehandleGCHandle.Alloc(buffer,GCHandleType.Pinned);IntPtrptrhandle.AddrOfPinnedObject();if(pixelTypeMyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8){// 灰度图HOperatorSet.GenImage1(outho_Image,byte,newHTuple(width),newHTuple(height),newHTuple(ptr));}else{// 彩色图HOperatorSet.GenImageInterleaved(outho_Image,newHTuple(ptr),rgb,newHTuple(width),newHTuple(height),-1,byte,0,0,0,0,-1,0);}handle.Free();// 显示到 HSmartWindowControl必须在 UI 线程上调用hSmartWindowControl1.Invoke(newAction((){lock(_displayLock){HOperatorSet.DispObj(ho_Image,hSmartWindowControl1.HalconWindow);}}));}finally{if(ho_Image!null)ho_Image.Dispose();}}// // 4. 停止采集// privatevoidbtnStop_Click(objectsender,EventArgse){if(_cameranull)return;_isGrabbingfalse;intnRet_camera.MV_CC_StopGrabbing_NET();if(nRet!MyCamera.MV_OK){MessageBox.Show(停止采集失败: nRet.ToString(X8));}// 等待最后一次回调执行完毕Thread.Sleep(500);// 释放回调委托的 GCHandleif(_gch.IsAllocated)_gch.Free();_imgCallbacknull;MessageBox.Show(已停止采集);}// // 5. 关闭设备// privatevoidbtnClose_Click(objectsender,EventArgse){if(_cameranull)return;// 如果正在采集先停止if(_isGrabbing){_isGrabbingfalse;_camera.MV_CC_StopGrabbing_NET();Thread.Sleep(500);if(_gch.IsAllocated)_gch.Free();}_camera.MV_CC_CloseDevice_NET();_camera.MV_CC_DestroyDevice_NET();_cameranull;MessageBox.Show(设备已关闭);}// // 窗体关闭时统一释放// privatevoidForm1_FormClosing(objectsender,FormClosingEventArgse){if(_camera!null){if(_isGrabbing){_isGrabbingfalse;_camera.MV_CC_StopGrabbing_NET();Thread.Sleep(500);}if(_gch.IsAllocated)_gch.Free();_camera.MV_CC_CloseDevice_NET();_camera.MV_CC_DestroyDevice_NET();_cameranull;}}}四、调用顺序总结btnEnum_Click → MV_CC_EnumDevices_NET → 绑定 ComboBox btnOpen_Click → MV_CC_CreateDevice_NET → MV_CC_OpenDevice_NET btnStart_Click → MV_CC_RegisterImageCallBackEx_NET → MV_CC_StartGrabbing_NET [回调自动触发] → Marshal.Copy(深拷贝) → 后台线程 → GenImage1/GenImageInterleaved → HSmartWindowControl 显示 btnStop_Click → MV_CC_StopGrabbing_NET → Thread.Sleep(500) → 释放 GCHandle btnClose_Click → MV_CC_CloseDevice_NET → MV_CC_DestroyDevice_NET FormClosing → 统一释放相机 GCHandle HObject五、关键注意事项回调委托必须声明为成员变量_imgCallback如果是局部变量会被 GC 回收导致回调失效甚至程序崩溃。回调中必须深拷贝pData指向 SDK 环形缓冲区回调返回后可能被覆写。用Marshal.Copy拷贝到byte[]后再处理。UI 更新必须切回主线程回调运行在 SDK 线程不能直接操作HSmartWindowControl。用Invoke或BeginInvoke切换。停止采集后等待再释放MV_CC_StopGrabbing_NET后建议Thread.Sleep(500)确保 SDK 采集线程已完成最后一次回调否则可能在回调访问已释放对象时崩溃。HObject 必须 DisposeHalcon 使用非托管内存C# GC 不感知。每帧转换后的ho_Image必须在finally中释放。枚举后释放非托管内存如果使用Marshal.AllocHGlobal分配设备列表内存枚举完成后必须Marshal.FreeHGlobal释放。