大恒Galaxy SDK MFC开发(1):枚举、打开、开始_停止采集基础流程(无画面版)

大恒Galaxy SDK MFC开发(1):枚举、打开、开始/停止采集基础流程(无画面版)

一、 前言

本文基于大恒Galaxy SDK,以MFC对话框工程(CPP_MFC_Daheng_01_Base)为载体,实现相机最基础的操作流程,核心聚焦「流程正确性」,不含图像取图、显示逻辑,代码极简、注释清晰,适合刚接触大恒相机开发的新手,可直接作为项目基础框架复用。

核心功能:SDK初始化/释放 + 相机枚举(下拉选择) + 相机打开/关闭 + 采集启停 + 日志打印,全程无图像处理,专注跑通基础流程、避免资源泄露。

补充说明:本文工程仅实现"流程跑通",不涉及图像数据获取与显示,重点解决新手"相机打不开、采集启动失败、资源泄露"三大核心问题,是工业视觉开发的入门基础。

二、开发环境

  • 开发工具:Visual Studio(2010/2026均可)

  • 项目类型:MFC 对话框应用程序(使用多字节字符集)

  • 相机SDK:大恒Galaxy SDK(安装GalaxySDK,建议安装最新版本,兼容更多相机型号)

  • 核心依赖:GXDevice.lib(SDK核心库,根据编译平台选择x64/x86版本)

  • 运行环境:Windows 10/11(32位/64位均可,与编译版本对应)

    补充:GalaxySDK下载地址( 大恒官网):(进入官网下载中心→软件下载→windows第一个就行),安装时一路下一步,默认安装路径为:C:\Program Files\Daheng Imaging\GalaxySDK(64位系统)。

三、工程配置步骤(新手必看,补充完善)

新建MFC对话框工程后,需完成SDK配置才能正常编译运行,步骤如下:

  1. 打开工程属性(右键工程 → 属性);

  2. 配置包含目录:在「配置属性 → C/C++ → 常规 → 附加包含目录」中,添加大恒Galaxy SDK的「Include」文件夹路径(如:C:\Program Files\Daheng Imaging\GalaxySDK\Development\C++ SDK\inc

    );

  3. 配置库目录:在「配置属性 → 链接器 → 常规 → 附加库目录」中,添加SDK的「Lib」文件夹路径(如:C:\Program Files\Daheng Imaging\GalaxySDK\Development\C++ SDK\lib\x64,根据编译平台选择x64或x86);

  4. 配置附加依赖项:在「配置属性 → 链接器 → 输入 → 附加依赖项」中,添加「GxIAPICPPEx.lib」(Debug版本、Release版本同样适用);

  5. 确认字符集:在「配置属性 → 高级 → 字符集」中,选择【使用多字节字符集】(重点!如果选择"使用 Unicode 字符集",会导致SDK相关函数报错)

四、 核心流程(重中之重)

工业相机基础开发的核心链路,也是本文全部内容,无需多余操作:

  1. 初始化 SDK
cpp 复制代码
IGXFactory::GetInstance().Init();

必须第一个调用,用于初始化相机底层库。

  1. 枚举设备
cpp 复制代码
gxdeviceinfo_vector vectorDeviceInfo;
IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo);

扫描所有连接的相机,存入列表。

  1. 打开相机(通过 SN 打开)
cpp 复制代码
CGXDevicePointer ObjDevicePtr = IGXFactory::GetInstance().OpenDeviceBySN(...)

稳定、安全、不会打开错设备。

  1. 开启采集(最重要)
cpp 复制代码
ObjStreamPtr->StartGrab();
ObjFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute();

这两句必须一起调用:

StartGrab ():开启流通道

AcquisitionStart:让相机开始发图

  1. 停止采集
cpp 复制代码
ObjFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();
ObjStreamPtr->StopGrab();

顺序不能反,否则会报错。

  1. 释放资源
cpp 复制代码
//释放资源
//关闭流
ObjStreamPtr->Close();
// 关闭设备
ObjDevicePtr->Close();
 //反初始化库
 IGXFactory::GetInstance().Uninit();

补充:流程顺序不可颠倒,尤其是"停止采集→关闭相机→释放SDK",颠倒会导致资源泄露、程序崩溃,日志会打印对应错误信息,便于排查。

五、 核心代码(精简版,只贴关键片段)

1. 相机枚举(填充下拉框)

cpp 复制代码
if (!vectorDeviceInfo.empty())
	vectorDeviceInfo.clear();
//枚举相机设备

IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo);

CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST_CAMERA);
pList->ResetContent();

for(int i=0; i<vectorDeviceInfo.size(); i++)
{
    CString str;
	CA2AEX<128> aBuf(vectorDeviceInfo[i].GetModelName());
	str.Format(_T("[%d] %s"), i,(LPCSTR)aBuf);
	//str.Format(_T("[%d] %s"), i, CA2T(vectorDeviceInfo[i].GetModelName()));
    pList->AddString(str);
}
if (vectorDeviceInfo.size() <= 0)
	{
		AddLog(_T("未发现设备!"));
		//MessageBox("未发现设备!");
		return;
	}
AddLog(_T("相机枚举完成"));

2. 打开/关闭相机

cpp 复制代码
//相机选中打开函数
void OpenCam()
{
	bool bIsDeviceOpen = false;         ///< 设备是否打开标志
	bool bIsStreamOpen = false;         ///< 设备流是否打开标志
	
	try
	{
		CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST_CAMERA);
		int nSel = pList->GetCurSel();

		if(nSel < 0)
		{
			 AddLog(_T("请选择相机"));
			return;
		 }
		//打开设备
		m_objDevicePtr = IGXFactory::GetInstance().OpenDeviceBySN(vectorDeviceInfo[nSel].GetSN(),GX_ACCESS_EXCLUSIVE);
		bIsDeviceOpen = true;
		m_objFeatureControlPtr = m_objDevicePtr->GetRemoteFeatureControl();

		//判断设备流是否大于零,如果大于零则打开流
		int nStreamCount = m_objDevicePtr->GetStreamCount();

		if (nStreamCount > 0)
		{
			m_objStreamPtr = m_objDevicePtr->OpenStream(0);
			m_objStreamFeatureControlPtr = m_objStreamPtr->GetFeatureControl();
			bIsStreamOpen = true;
		}
		else
		{
			MessageBox("未发现设备流!");
		}

        // 建议用户在打开网络相机之后,根据当前网络环境设置相机的流通道包长值,
        // 以提高网络相机的采集性能,设置方法参考以下代码。
        GX_DEVICE_CLASS_LIST objDeviceClass = m_objDevicePtr->GetDeviceInfo().GetDeviceClass();
        if(GX_DEVICE_CLASS_GEV == objDeviceClass)
        {
            // 判断设备是否支持流通道数据包功能
            if(true == m_objFeatureControlPtr->IsImplemented("GevSCPSPacketSize"))
            {
                // 获取当前网络环境的最优包长值
                int nPacketSize = m_objStreamPtr->GetOptimalPacketSize();
                // 将最优包长值设置为当前设备的流通道包长值
                m_objFeatureControlPtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize);
            } 
        }
		m_bIsOpen = true;
		// 打印日志
		CString log;
		log.Format(_T("第%d个相机打开%s成功  "), nSel, CString(vectorDeviceInfo[nSel].GetModelName()));
		AddLog(log);
	}
	catch (CGalaxyException& e)
	{
		//判断设备流是否已打开
		if (bIsStreamOpen)
		{
			m_objStreamPtr->Close();
		}

		//判断设备是否已打开
		if (bIsDeviceOpen)
		{
			m_objDevicePtr->Close();
		}

		AddLog(_T("相机打开失败"));
		AddLog(_T(e.what()));
		MessageBox(e.what());
		return;
	}
	catch (std::exception& e)
	{
		//判断设备流是否已打开
		if (bIsStreamOpen)
		{
			m_objStreamPtr->Close();
		}

		//判断设备是否已打开
		if (bIsDeviceOpen)
		{
			m_objDevicePtr->Close();
		}

		
		AddLog(_T("相机打开失败"));
		AddLog(_T(e.what()));
		MessageBox(e.what());
		return;
	}
}

//相机关闭采集
void CloseCam()
{
	//先调用停止采集
	StopSnap();	
	if(m_bIsOpen)
	{
		try
		{			
			//关闭流对象
			m_objStreamPtr->Close();

		}
		catch(CGalaxyException &e)
		{
			AddLog(_T("相机流关闭失败"));
			AddLog(_T(e.what()));
			MessageBox(e.what());
		}
		try
		{
			//关闭设备
			m_objDevicePtr->Close();
		}
		catch(CGalaxyException &e)
		{
			AddLog(_T("相机关闭失败"));
			AddLog(_T(e.what()));
			MessageBox(e.what());
		}
		m_bIsOpen = false;
		AddLog(_T("相机关闭完成"));
	}
	
}

3. 采集启停

cpp 复制代码
//相机开始采集函数
void StartSnap()
{
	try
	{	
		if(!m_bIsSnap)
		{
			//开启流层通道
			m_objStreamPtr->StartGrab();

			//发送开采命令
			m_objFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute();
			m_bIsSnap = true;
			AddLog(_T("相机开始采集..."));
			return;
		}
		
		AddLog(_T("相机已经开始采集..."));
		
	}
	catch (CGalaxyException& e)
	{
		AddLog(_T("相机开始采集失败..."));
		AddLog(_T(e.what()));
		MessageBox(e.what());
		return;	
	}
	catch (std::exception& e)
	{
		AddLog(_T("相机开始采集失败..."));
		AddLog(_T(e.what()));
		MessageBox(e.what());	
		return;	
	}
}
//相机停止采集
void StopSnap()
{
	try
	{
		if(m_bIsSnap)
		{
			//发送停采命令
			m_objFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();

			//关闭流层通道
			m_objStreamPtr->StopGrab();

		
			m_bIsSnap = false;
			AddLog(_T("相机停止采集..."));
		}		
	}
	catch (CGalaxyException& e)
	{
		AddLog(_T("相机停止采集失败..."));
		AddLog(_T(e.what()));
		MessageBox(e.what());
		return;	
	}
	catch (std::exception& e)
	{
		AddLog(_T("相机停止采集失败..."));
		AddLog(_T(e.what()));
		MessageBox(e.what());
		return;	
	}
}

六、功能说明

  • 界面控件:ListBox(相机选择)、5个按钮(刷新、打开、关闭、开始采集、停止采集)、Edit(日志显示)

  • 核心逻辑:仅实现相机基础操作流程,无任何图像取图、显示、处理代码

  • 日志功能:实时打印操作状态、错误信息,便于调试;写入日志文件,方便分析

  • 安全机制:避免重复打开相机、重复启停采集,程序退出自动释放所有资源

  • 兼容性:适配大恒主流相机型号,只要SDK安装正确,无需修改核心代码即可使用

七、关键注意事项(新手必看)

  1. 必须先停止采集,再关闭相机,否则会导致资源泄露、程序崩溃;

  2. SDK初始化(Init)和释放(Uninit)必须成对出现,放在程序启动和退出时;

  3. 工程配置时,需添加SDK的Include目录、Lib目录,以及附加依赖项GxIAPICPPEx.lib;

  4. 枚举相机时,超时时间建议设置为1000ms(1秒),过短可能导致枚举失败;

  5. 打开相机后,禁止点击"刷新"按钮,避免相机句柄冲突,导致程序崩溃;

  6. 日志编辑框需设置为"多行、允许滚动",否则日志会被遮挡,无法查看完整调试信息。

八、常见问题排查(补充,新手必备)

  1. 问题1:编译报错"无法打开包括文件: "GalaxyIncludes.h"" → 解决方案:检查工程属性中"附加包含目录"是否添加SDK的Include文件夹;

  2. 问题2:链接报错"无法解析的外部符号" → 解决方案:检查"附加库目录"是否正确,"附加依赖项"是否添加GxIAPICPPEx.lib,以及编译平台(x64/x86)与SDK版本一致;

  3. 问题4:枚举相机显示"未检测到任何相机设备" → 解决方案:检查相机供电、连接是否正常(相机灯是否为绿色),打开驱动自带的GalaxyView软件确认相机能被识别,重启相机后重新运行程序;

总结

本文工程(CPP_MFC_Daheng_01_Base)定位为「大恒相机MFC开发基础模板」,核心是跑通"枚举-打开-采集-关闭"的完整流程,不涉及复杂的图像处理,适合新手入门。

配套资源下载(VS工程直接用)

本文配套完整VS2010控制台工程,已配置好SDK,无需手动配置,下载后直接用VS2010打开(2026通过测试),编译运行即可。

下载地址: CPP-MFC-Daheng-01-Base 大恒相机MFC基础工程枚举+打开+采集启停+日志

相关推荐
初圣魔门首席弟子2 小时前
bug20260415
c++·bug
m0_716765232 小时前
数据结构--循环链表、双向链表的插入、删除、查找详解
开发语言·数据结构·c++·学习·链表·青少年编程·visual studio
不想写代码的星星3 小时前
类型萃取:重生之我在幼儿园修炼类型学
开发语言·c++
郝学胜-神的一滴3 小时前
中级OpenGL教程 001:从Main函数到相机操控的完整实现
c++·程序人生·unity·图形渲染·unreal engine·opengl
charlie1145141913 小时前
嵌入式现代C++教程实战篇第12篇:C宏时代的LED驱动 —— 能跑但不优雅
c语言·c++·stm32·单片机·嵌入式硬件·c
wunaiqiezixin3 小时前
链表多项式大整数-BigInt
数据结构·c++·链表
kyle~3 小时前
BFS(广度优先搜索)与 DFS (深度优先搜索)
c++·算法·深度优先·宽度优先
汉克老师3 小时前
GESP2024年3月认证C++三级( 第二部分判断题(1-10))
c++·位运算·string·gesp三级·gesp3级
kyle~4 小时前
FANUC机械臂---PR位置寄存器(Position Register)
c++·机器人·fanuc