简介:金橙子激光打标软件二次开发所需的MarkEzd.dll动态链接库与配套头文件包,面向需要在C#环境下调用其接口的上位机开发、自动化设备集成及打标控制类工程师。压缩包内共2个文件,整体大小约29KB,其中dll封装了图形处理、参数配置与通信等底层功能,h文件则声明了函数原型、结构体与调用约定,方便开发者在C#项目中直接引用并编写交互代码。已有1401人学习下载,说明该资源在打标机控制、产线联动等场景中具有较高的参考价值。通过这套文件,读者可以快速完成DLL添加引用、命名空间导入、API调用、异常处理及基础功能测试等关键环节,有效缩短从接口文档理解到实际联调的上手周期,尤其适合正在从事金橙子控制模块C#二次开发、需要核对接口定义或排查调用异常的人员使用。
1. 用 C# 调金橙子 MarkEzd.dll 做激光打标上位机:这条路比想象的长一点
用 C# 调金橙子 MarkEzd.dll 做激光打标上位机,难点不在 DllImport 那几行声明,而在库跟「卡、驱动、EZD 文件、加密狗」绑在一起,任何一个环节不对,程序就跑不起来。这篇按一线集成顺序写:先拆 MarkEzd.rar 资源包,再给出可复用的 C# 封装,然后走通连接—加载—执行—确认的完整链路,最后是高频坑。适合刚拿到金橙子 USB 打标卡、打算用 C# 写上位机集成的工程师;后面要接 PLC 联动、视觉定位、多工位轮流打标的,也同样适用。
2. 先拆开 MarkEzd.rar:DLL、驱动、EzCad2 与 EZD 文件的职责分工
拿到压缩包先别急着把 MarkEzd.dll 拖进工程。这个资源包表面上是「一个 DLL」,实际是一整套打标工具链,DLL 只是 C# 程序要面对的接口。先把包里的东西分类,搞清楚谁负责什么,后面报错时才知道去哪找原因。
2.1 解压后常见文件与各自职责
不同来源的 MarkEzd.rar 内容略有差别,但核心成员一般是下面这几类,按职责分:
| 文件/目录 | 职责 | 什么时候用 |
|---|---|---|
| MarkEzd.dll | 打标控制卡的动态库,导出 EZ_ 系列接口 | C# 里 DllImport 引用它 |
| EzCad2.exe | 配套打标编辑软件,用来画 EZD 文件 | 定义内容、调参数、现场换版 |
| 驱动安装包(.exe / .inf) | 让操作系统识别 USB 打标卡 | 新工控机首次部署 |
| Sample / Demo 目录 | C++、C#、MFC 示例工程与已编译 Demo | 验证卡是否正常、抄函数调用方式 |
| SDK 手册(.chm / .pdf) | 函数原型、结构体定义、参数名表 | 写封装前逐条核对 |
| 示例 .ezd 文件 | 可以直接加载的打标文件 | 联调链路时的测试输入 |
资源包命名里如果出现 dongle、加密狗、usb 之类的后缀,说明它对应的是不同授权形态的卡,别只看文件名带 MarkEzd 就拿来用。装驱动前先确认手里的卡型号和 SDK 版本对应,金橙子旧卡与新驱动不匹配时,会出现「设备管理器里能看到卡,但 SDK 连不上」的怪现象。
判断资源包是否完整有个笨但稳的办法:先把 Sample 目录里编译好的可执行文件跑起来。如果它能在你的工控机上连接设备、加载示例 EZD、打出内容,说明包里的 DLL、驱动、加密狗是配套的;如果 Demo 都报错,问题在环境,不在你接下来要写的 C#。很多朋友跳过这一步直接开工,最后反过头来查代码,绕了远路。Sample 里如果同时有 C++ 和 C# 两份工程,优先看 C# 那份的调用方式——厂商示例通常会把调用约定、字符集这些踩过坑的选择直接示范出来,照着写比看手册快。
2.2 EZD 文件:打标内容在 EzCad2 里画,不在 C# 里拼
很多程序员第一次接触会问:能不能直接在 C# 里生成打标内容,比如画个二维码、写行字?可以,但没人这么干。因为打标内容不只是「图案」,还包含图层顺序、激光参数、打标次数、坐标基准,这些在 EzCad2 里所见即所得,在代码里拼就是给自己挖坑。EZD 文件相当于打标任务的模板:焦点位置、振镜速度、出光延时这类偏机器侧的东西,在软件里调好之后固化在文件里;上位机只负责三件事——加载哪个文件、改成什么内容、什么时候执行。
这个分工在现场非常有价值。今天打一号产品,明天换二号产品,产线技术员在 EzCad2 里调完另存一个 EZD,上位机代码一行都不用改。如果把这些内容硬编码在 C# 里,每次换版都要编译发布,还要处理字体、条码库、坐标系一堆问题,维护成本高一个量级。
关于 EZD 内部,有一个概念值得先建立:EZD 不是图片,它保存的是对象和参数层级。一个文件里可能有多层,每层有自己的打标内容;对象是文本、条码、矢量图的抽象,属性里有坐标、字号、条码类型;文件尾部是激光参数。C# 的视角只需要理解成:LoadFile 把整个任务装进卡的内存,DoFile 按文件内部的图层顺序执行。所以「改一下打标内容」在上位机层面对应的是 EZ_SetVtValue 改变量,而不是重新生成整个文件。
2.3 三个基础属性:位数、依赖库、加密狗
在封装 C# 之前,先确认 MarkEzd.dll 的三个基础属性,它们直接决定 DllImport 怎么写。
第一是位数。SDK 包里通常会提供 32 位和 64 位两个版本,C# 进程的位数必须和 DLL 位数一致。如果项目编译成 AnyCPU,在 64 位系统上会以 x64 进程运行,而 DLL 是 32 位的,运行到调用那一行直接抛 BadImageFormatException。经验做法是把主程序明确编成 x86 或 x64,别依赖 AnyCPU 碰运气;工控机里常常还装着一堆 32 位的老驱动组件,很多现场最终统一用 x86。
第二是依赖。MarkEzd.dll 可能依赖 VC 运行库,或者同目录下的其它支持文件。直接把 DLL 拷到 exe 目录、其它文件不带的做法,会出现 DllNotFoundException,而且报错是通用的「找不到模块」,根本不会告诉缺的是哪个。查 DLL 位数和依赖,Win10 以后建议用开源工具 Dependencies,它能显示 DLL 的导入表,并且标出哪些依赖缺失。用法:打开 MarkEzd.dll,看 PE 头信息里的架构,再看依赖树里有没有红色缺项。如果这台工控机上没有 VC 运行库,通常补装对应年份的 Visual C++ Redistributable 就能解决。还有一个更快的验证技巧:把 DLL 放到 system32 之外的自定义目录,避免系统目录里存在旧版本文件干扰判断,这样查到的情况才是你工程实际引用的那份。
第三是授权。金橙子通常要插加密狗,或者做机器码绑定授权。环境里没有狗,连接函数会失败,这不是代码问题。验证办法很简单:先运行 SDK 自带 Demo,Demo 能连上、能打标,再动自己的代码;Demo 都不行,排查环境而不是排查代码。
3. C# 封装:从函数原型到可复用的 MarkEzd 封装类
摸清资源包结构后,进入正题:把 C 接口翻译成 C# 能用的样子。金橙子的接口是典型的 C 风格导出函数,C# 侧用 P/Invoke 对接。直接在每个窗体里散写 DllImport 会非常乱,我一般是先做一个静态封装类,把会用到的函数集中声明,再包一层带日志的调用方法。
3.1 最小函数集:打通流程只需要这几个
完整 SDK 的函数有几十个,但第一次联调不需要全封装。先挑能跑通「连接—加载—执行—查询—断开」的最小集合,后面再按需补。
| 函数 | 作用 | 备注 |
|---|---|---|
| EZ_Connect(nIndex) | 连接第 nIndex 个设备 | 多卡时从 0 开始编号 |
| EZ_Disconnect() | 断开设备 | 程序退出前务必调用 |
| EZ_LoadFile(nIndex, fileName) | 加载 EZD 文件 | 传完整路径,中文路径有讲究 |
| EZ_DoFile(nIndex, fileIndex) | 执行打标 | fileIndex 一般传 0 |
| EZ_GetMarkState(out state) | 查询是否正在打标 | 用于完成后联动后续工位 |
| EZ_StopMark() | 急停当前打标 | 出错时安全兜底 |
| EZ_GetErrorString(err, buf, len) | 把错误码转成可读字符串 | 排查问题的出口 |
| EZ_SetVtValue(nIndex, objName, value) | 给 EZD 里的变量对象赋值 | 动态改序列号、日期就用它 |
这些函数的具体签名以你手上的 SDK 手册为准,版本不同参数可能微调,尤其是返回值的含义。封装类做好后,先用一个控制台工程验证这几个函数能通,再进 WinForms。
3.2 完整封装示例:一个可以抄作业的 MarkEzd 类
下面这个封装我一般会直接放进上位机工程里,再往上加业务逻辑。注意调用约定部分:如果运行时报 PInvokeStackImbalance,改成 Cdecl 后整体重试。
using System; using System.Runtime.InteropServices; using System.Text; namespace MarkEzdDemo { public static class MarkEzd { private const string DllName = "MarkEzd.dll"; // 金橙子 C 接口大多按 StdCall 导出; // 如果抛 PInvokeStackImbalance,把下面的 StdCall 全局换成 Cdecl。 [DllImport(DllName, EntryPoint = "EZ_Connect", CallingConvention = CallingConvention.StdCall)] public static extern int EZ_Connect(int nIndex); [DllImport(DllName, EntryPoint = "EZ_Disconnect")] public static extern void EZ_Disconnect(); // 文件名用 Ansi 字符集,中文路径按 GBK 编码传递 [DllImport(DllName, EntryPoint = "EZ_LoadFile", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern int EZ_LoadFile(int nIndex, string fileName); [DllImport(DllName, EntryPoint = "EZ_DoFile", CallingConvention = CallingConvention.StdCall)] public static extern int EZ_DoFile(int nIndex, int nFileIndex); [DllImport(DllName, EntryPoint = "EZ_GetMarkState", CallingConvention = CallingConvention.StdCall)] public static extern int EZ_GetMarkState(out int state); [DllImport(DllName, EntryPoint = "EZ_StopMark", CallingConvention = CallingConvention.StdCall)] public static extern int EZ_StopMark(); // 错误字符串输出用 StringBuilder,长度给足 [DllImport(DllName, EntryPoint = "EZ_GetErrorString", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern int EZ_GetErrorString(int nErr, StringBuilder pStr, int nMaxLen); [DllImport(DllName, EntryPoint = "EZ_SetVtValue", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern int EZ_SetVtValue(int nIndex, string objName, string value); } }代码里有三个关键点。第一,CharSet 统一用 Ansi,因为 C 接口的 char* 在中文 Windows 下按 GBK 多字节编码解释,C# 的 string 默认是 Unicode,漏掉 CharSet 会出现中文路径加载失败或变量内容乱码。第二,返回 int 的函数拿到结果后不要直接忽略,先打日志;我会在外面再包一层 CheckResult 统一把返回值打出来,配合 EZ_GetErrorString 看出错原因。第三,输出参数用 out int 而不是 ref int,是让运行时明确知道这个参数只做输出,避免多余的封送开销。
封装类做好后,我习惯再包一层带日志的业务方法,而不是直接在窗体里调 extern 函数。原因很实在:extern 函数没有返回值检查,返回值丢了你根本不知道调用失败;加了日志后,每次调用都留有痕迹。打标失败时用户只会说「没打上去」,有日志就能判断是加载失败、执行失败,还是压根没触发。
3.3 三个容易改错的声明细节:调用约定、返回码、结构体与回调
调用约定是新手最容易翻车的点。C# 里 DllImport 默认是 Winapi 平台相关约定,很多教程会直接在函数后面加 Cdecl 或 StdCall,但不说明怎么判断。判断依据只有一条:让程序跑起来,如果在调用 EZ_Connect 时直接抛 PInvokeStackImbalance,说明声明和实际导出约定不一致,换成另一种再试。这个异常是机制的友好错误,比内存错乱好排查得多。
返回码也要留个心眼。金橙子不同版本的 SDK 习惯不完全一样,有的成功返回 1,失败返回 0;有的版本失败返回负数错误码。最稳妥的写法不是判断「返回值等于某个值」,而是把返回值记录下来,再用 EZ_GetErrorString 翻译成可读文本。封装类里我都会放一个 LogReturn 方法,把「函数名 + 返回值 + 错误文本」一起写进日志,现场出问题能直接看日志定位。
结构体的情况更微妙。SDK 里不少接口要传入 C 结构体,比如打标参数结构体,里面是一串 int、float、定长 char 数组。C# 声明时要用 StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi),字段顺序、类型必须和 C 头文件一模一样,字段少一个都不行,因为接口拿到的是结构体指针,按地址读内存,少声明字段等于把内存读错位。我自己吃过这个亏,少声明末尾一个字段,读出来的参数全是乱的,而且不报错。
另外有一类高阶 API 需要事件回调,C 接口里是函数指针,C# 对应写成 delegate,并用 Marshal.GetFunctionPointerForDelegate 转换成函数指针传进去。有个细节:回调 delegate 必须保持引用,否则会被 GC 回收,回收后原生代码回调到非法地址,程序直接崩,而且崩得毫无规律。
4. 走通打标链路:连接、加载 EZD、参数注入与异步执行
封装类就位后,下一步是走一条真正能跑起来的最小链路。这条链路的顺序建议固定:连接设备、加载文件、注入变量、执行打标、轮询完成、断开设备。顺序不对会有各种隐性失败,比如没连接就加载,有的版本会静默失败返回 0。
4.1 最小可运行链路:一段可以直接试的 C# 代码
建议先建一个控制台项目,把下面这段跑通,再往 WinForms 里搬。控制台的好处是环境简单,日志直接打印,不会混入界面线程的问题。
static int RunOnce(int deviceIndex, string ezdPath, int timeoutMs) { if (!File.Exists(ezdPath)) { Console.WriteLine($"[错误] EZD 文件不存在: {ezdPath}"); return -1; } int ret = MarkEzd.EZ_Connect(deviceIndex); Console.WriteLine($"[连接] ret = {ret}"); if (ret == 0) { Console.WriteLine("[错误] 连接失败,检查驱动/加密狗/设备索引"); return -1; } ret = MarkEzd.EZ_LoadFile(deviceIndex, ezdPath); Console.WriteLine($"[加载] ret = {ret}"); if (ret == 0) { Console.WriteLine("[错误] 加载失败,检查路径和文件完整性"); MarkEzd.EZ_Disconnect(); return -1; } ret = MarkEzd.EZ_DoFile(deviceIndex, 0); Console.WriteLine($"[执行] ret = {ret}"); // 轮询打标状态:EZD 文件里打标次数越多,耗时越长,必须等完成再退出 int state = 0; int elapsed = 0; do { Thread.Sleep(20); MarkEzd.EZ_GetMarkState(out state); elapsed += 20; } while (state == 1 && elapsed < timeoutMs); MarkEzd.EZ_Disconnect(); Console.WriteLine($"[完成] 耗时 {elapsed}ms"); return 0; }这段代码的每一步都有对应的日志输出。连接失败先别怀疑代码,按顺序检查:驱动装没装、加密狗在不在、设备索引对不对。加载失败基本是路径问题,尤其是中文路径,先把 EZD 放到纯英文目录验证再讨论编码。轮询的 20 毫秒间隔是经验值,太快会增加无效调用,太慢会让产线等待;如果打的是小字符,十几毫秒就能结束,间隔可以再缩小到 10。
4.2 动态改参数与变量文本:序列号、日期怎么打上去
静态加载一个 EZD 文件只是入门,产线上高频需求是「文件不变,内容变」。金橙子的做法是在 EzCad2 里把某个文本对象定义为变量,比如命名 SN,上位机在打标前用 EZ_SetVtValue 赋值,打出来的就是最新的序列号。
string sn = DateTime.Now.ToString("yyyyMMddHHmmss"); int ret = MarkEzd.EZ_SetVtValue(0, "SN", sn);赋值要在 EZ_DoFile 之前调用,顺序反了这次打标就是旧值。变量名必须和 EzCad2 里定义的一模一样,大小写、空格都算,现场最常翻车的就是变量名对不上,而 SDK 对这种情况往往不报错,只是打出来是默认值。所以调试时要先在 EzCad2 里把变量默认值改成明显的测试值,比如 ABC123,打出来不对就知道是赋值没生效。
激光本身的参数,比如功率、速度、打标次数,一般不建议上位机频繁改,这些应该在 EZD 文件里按工艺固好。确实要改时,SDK 提供按名读写的参数接口,先读后写、写完回读确认,避免参数名拼错导致静默采用默认参数。不同 SDK 版本的参数名表有差异,用之前一定翻一遍手册,不要靠记忆盲写。上位机里的 JSON 配置清单也可以归到这个思路:每个产品型号对应一个 EZD 路径加一组合法参数名,配置校验放在加载阶段做,而不是等到打标才发现工艺不对。
4.3 别把打标放在 UI 线程:WinForms 卡死的根源
把 EZ_DoFile 直接放在按钮的 Click 事件里,点一下界面就死掉,打标几秒界面就死几秒,这是新手最常见的问题。原因在于 EZ_DoFile 是阻塞调用,要等打标结束才返回,而 UI 线程一旦被阻塞就无法处理消息循环,窗体自然卡死。另外打标过程中产线操作员可能想按急停,UI 死了急停按钮也点不动,这是安全隐患。
正确做法是丢到后台线程执行,打标期间 UI 线程只做状态显示和急停。
private async void btnMark_Click(object sender, EventArgs e) { btnMark.Enabled = false; try { await Task.Run(() => { MarkEzd.EZ_LoadFile(0, _currentEzdPath); MarkEzd.EZ_SetVtValue(0, "SN", _nextSerial); MarkEzd.EZ_DoFile(0, 0); WaitMarkDone(_timeoutMs); }); UpdateStatusBar("打标完成"); } catch (Exception ex) { Log.Error(ex); UpdateStatusBar($"异常: {ex.Message}"); } finally { btnMark.Enabled = true; } }注意 async void 只允许用于事件处理器,普通方法不要这么写。UI 更新通过 UpdateStatusBar 方法回到 UI 线程,不要在 Task 里直接碰控件,这在 C# 里是跨线程访问,WinForms 会抛异常。急停按钮单独走 EZ_StopMark,不需要等打标线程结束,这个函数设计上就是随时可以调用的。
5. 避坑排查:加载失败、连不上卡、乱码与合并 DLL 的高频问题
封装和链路都通了一遍,接下来是真正花时间的部分。下面是帮朋友排查时最常遇到的几类问题,按「现象 → 原因 → 解决」拆开,基本覆盖了从拿到资源包到上线的大部分障碍。
5.1 加载阶段:DllNotFoundException、依赖缺失与调用约定栈不平衡
现象:程序启动时直接报 DllNotFoundException,或者一调用 EZ_Connect 就抛 PInvokeStackImbalance,甚至直接进程崩溃退出。
原因分三种。一种是位数不匹配,32 位 DLL 被 64 位进程加载,表现是 BadImageFormatException 而不是 DllNotFound;一种是 DLL 本身找到了,但它依赖的 VC 运行库或同目录支持文件缺失;还有一种是调用约定声明错误,导致栈不平衡。DllNotFoundException 只报「找不到模块」,不说是缺哪个模块,所以第一反应往往是去检查项目引用,方向就错了。
解决:先用 Dependencies 打开 MarkEzd.dll 看完整依赖树,把缺的运行库装上;确认进程位数与 DLL 位数一致,把项目平台改为固定 x86 或 x64,关掉 AnyCPU;最后用最小控制台工程验证调用约定,报 PInvokeStackImbalance 就把 StdCall 换成 Cdecl 整体重试。这三个检查按顺序做完,加载阶段的报错基本清零。
这里还有一条经验:不要同时在系统目录和程序目录各放一份 MarkEzd.dll。Windows 的 DLL 搜索顺序是先应用程序目录,再系统目录,两份文件版本不一致时,你今天调通的是程序目录这份,明天环境变量一变可能加载到另一份,行为完全两样。统一只在程序目录放一份,省得给自己埋雷。
5.2 连接与执行阶段:连不上卡、返回 0 与中文乱码
现象:EZ_Connect 返回 0,或 EZ_LoadFile 加载中文路径文件失败,打标内容里的中文变成乱码或者打出默认值。
原因:连接失败绝大多数不是代码问题,是环境问题——驱动没装、加密狗没插、设备索引不对。加载失败和乱码则与字符编码有关,MarkEzd.dll 的 char* 参数在中文 Windows 下按 GBK 解释,C# 声明少了 CharSet.Ansi 时字符串按 Unicode 封送,编码对不上,中文就花。
解决:连接失败时先跑 SDK 自带 Demo,它能连就说明环境没问题,回来查代码;Demo 也连不上就别调代码了,去设备管理器看卡是否被识别,重新装驱动,确认加密狗。编码问题统一在 DllImport 声明里补 CharSet.Ansi,同时把 EZD 路径和变量值都改成纯 ASCII 做对照试验,哪个恢复正常就是哪个环节的编码问题。
变量值打出来是默认值这一点要单独注意。EZ_SetVtValue 的对象名对不上时不报错,只是静默忽略。解决方法是先在 EzCad2 里把该变量的默认值改成醒目的测试值,然后在 C# 里故意写一个错误的对象名调用一次,如果打出来还是默认值,说明赋值链路没通;如果打出来变成了测试值,说明对象名是对的、赋值路径是通的。这个对照法能快速区分是编码问题还是变量名问题,省去大量猜测。
5.3 工程阶段:Costura.Fody 合并失败、UI 线程卡死与跨线程更新
现象:项目里用 Costura.Fody 把依赖打成单文件,结果照样报找不到 MarkEzd.dll;或者把 EZ_DoFile 放在按钮事件里,界面整个冻住;还有的在 Task 里直接改窗体控件文本,运行时抛跨线程异常。
原因:Costura.Fody 只能把托管的程序集嵌入主程序集。MarkEzd.dll 是原生 DLL,不在它的处理范围内,这是很多人合并失败时没想到的坎。UI 卡死我们前面说过是阻塞调用占用了 UI 线程,属于使用姿势问题,不是库的问题。这三个现象指向同一个误区:以为 DLL 丢进项目就万事大吉,没考虑原生 DLL 的加载时机和线程模型。
解决:原生 DLL 就不要想着合并进托管单文件了,保留在运行目录下发,或者自己写一个「按需解压」逻辑,启动时把内嵌的原生 DLL 释放到临时目录再加载。UI 卡死的解法就是后台线程方案,打标放 Task,UI 只做状态驱动。跨线程更新控件就用 Invoke 或者把进度值放在一个 volatile 字段里,UI 定时器去读。
还有一条血泪经验:等待打标完成时,不要在主线程里用 Thread.Sleep(200) 这种固定延时。打标时间受内容复杂度和次数影响,短则几十毫秒长则几秒,固定延时要么等不够、要么白白浪费时间。一定要用 EZ_GetMarkState 轮询,配合超时保护;超时时间根据现场节拍设,一般 5 秒内足够,超时就触发 EZ_StopMark 并把异常记进日志,不要让产线卡在一个坏工件上。
6. 进阶技巧:用打标状态轮询做完成信号,把打标卡并进产线流程
单机打通只是第一步,产线上打标卡几乎都是被流程调度的:扫码枪扫到工件 → 视觉定位 → 触发打标 → 打完放行。这个流程里最关键的就是「打完了」这个信号,它决定工件能不能流入下一工位。
我一般会在封装类里加一个等待完成的方法,把轮询逻辑收拢起来,加上超时保护,避免打标卡异常时产线永远等下去:
public static bool WaitMarkDone(int timeoutMs) { int state = 0; int elapsed = 0; while (elapsed < timeoutMs) { MarkEzd.EZ_GetMarkState(out state); if (state == 0) { Log.Info($"打标完成,耗时 {elapsed}ms"); return true; } Thread.Sleep(10); elapsed += 10; } Log.Warn($"等待完成超时,{timeoutMs}ms 内未结束,触发急停"); MarkEzd.EZ_StopMark(); return false; }这个方法的价值在于统一了「完成判断」和「超时兜底」。产线集成时,PLC 读写占一个线程,视觉相机拍照占一个线程,打标卡本身又是一个后台任务,三者之间用队列或事件衔接,尽量别互相阻塞。我见过不少上位机把相机和打标串在同一个线程里,拍照慢导致打标延期,产线节拍被拖垮;正确做法是先把相机拍完的坐标结果放到队列,打标线程只认队列里的最新值。
日志习惯也要养成。每次打标把序列号、文件路径、返回码、耗时写进结构化日志,出问题能回溯到具体工件。封装层我还会再做一遍混淆处理,防止自己的集成逻辑被直接反编译抄走。
从那以后,我每换一台工控机,都强制先跑一遍 SDK 自带 Demo,确认卡、驱动、加密狗三件套通了再动自己的工程,这套流程帮我把环境问题挡在联调之前,排查时间省了一大半。希望帮到你。
本文还有配套的精品资源,点击获取