中控考勤机C#二次开发实战:连接、读记录与避坑指南

简介:面向中控考勤机二次开发者的完整资料包,涵盖SDK组件、API函数说明、设备通讯协议解析及数据管理思路,并附有C#与VB.NET的可编译示例工程,适合初、中级程序员学习考勤系统集成。压缩包共1264个文件,大小约11.08MB,以cs源码、dll动态库、exe演示程序、txt说明文档和pdb调试符号为主,同时包含sln/csproj工程文件与resources等辅助资源,便于直接加载项目进行调试。已有2619人学习浏览。借助配套文档与大量示例,开发者可快速掌握设备初始化、考勤记录读取、用户管理、异常处理等核心流程,并在此基础上搭建完整的自动化考勤管理系统。

1. 中控考勤机二次开发:一份C# SDK资源包能解决什么

中控考勤机在中小公司、工厂、项目部里出现频率极高,但多数人只用它的配套软件导出报表,真要接进自己的OA、薪资、门禁系统时,才发现手头连一份能跑的SDK示例都没有。这份资源包的价值在于:它把设备通信、数据读取、指纹登记、记录清空这些能力封装成了C#能直接调用的接口,附带完整文档和一份官方风格的Demo工程,让你不用从抓包、逆向协议开始,而是照着声明就能把设备数据拿回来。

它适合两类人:一类是要在短时间内交付考勤对接功能的开发者,另一类是想搞懂考勤机数据流、但不想啃C++底层库的C#工程师。我拆过几批这类设备,结论是:大部分坑不在SDK本身,而在调用约定、字符编码和连接管理上。这篇文章就把怎么搭环境、怎么写第一个能跑的调用、以及哪些地方最容易翻车一次讲透。

2. 认识SDK的接口模型:连接、调用与数据格式

2.1 设备型号与通信方式:USB、网络、串口怎么选

中控考勤机的SDK虽然是同一套接口,但通信层分三条路:USB、TCP/IP、RS232/485串口。资源包里的文档一般会分平台列出支持情况,C#开发者在Windows上最常用的是USB和TCP/IP,串口多用于老式设备或需要走远距离布线的场景。

接口函数通常长这样:

// 连接设备(USB方式) int gz_connect_usb(int machineNumber); // 连接设备(网络方式) int gz_connect_net(string ip, int port);

选择通信方式时,我一般遵循三个原则:单机调试用USB,因为免配IP;生产环境用网络,因为考勤机通常挂在办公室或门卫室,PC可能离得很远,USB线拉不过去;如果现场有串口服务器或老设备,才考虑RS232/485。资源包文档里通常会有个连接参数对照表,建议先按表里推荐的默认端口试,不要自己猜。

2.2 SDK接口的组织方式:从声明到第一次调用

这套SDK对C#的友好度很不错,因为接口的管理方式相对集中:大部分函数是“先连接,再操作,最后断开”的三段式结构。资源包里自带一份接口声明文件,命名类似于zkemkeeper.cs,它把动态库里的导出函数用 P/Invoke 声明成了C#方法,开发者不需要自己写DllImport,直接引用对应类就能用。

核心声明通常拆成两部分:设备操作类和事件回调类。前者负责连接、断开、读记录、清记录;后者用来接收实时打卡事件。新手最容易忽略的是事件类,因为考勤机并不是只能被动拉取数据,它还能主动推送打卡事件,只是需要先注册回调。

2.3 连接与初始化:参考实现的代码与参数

先看一段资源包里最常见的连接示例:

// 引入SDK命名空间 using zkemkeeper; // 创建设备对象 CZEMClass device = new CZEMClass(); // 连接USB设备 bool connected = device.Connect_USB(1); // 或者连接网络设备 bool connected = device.Connect_Net("192.168.1.200", 4370); if (connected) { Console.WriteLine("连接成功"); } else { Console.WriteLine("连接失败"); }

重点看Connect_Net的两个参数:IP地址好理解,端口默认是4370,这个值是中控设备通用的,资源包文档里会注明,如果改了设备端的端口配置,这里也要同步改。Connect_USB的参数是机器号,不是设备ID,多台设备通过USB连接时用1、2、3区分,单台设备写1就行。

连接成功后,一般还要做一次设备信息读取,确认通讯正常。常见做法是读取序列号或固件版本:

string serialNumber = ""; string firmwareVersion = ""; // 读取设备序列号 device.GetSerialNumber(ref serialNumber); // 读取固件版本 device.GetFirmwareVersion(ref firmwareVersion); Console.WriteLine($"序列号: {serialNumber}"); Console.WriteLine($"固件版本: {firmwareVersion}");

这里有一个容易踩的坑:GetSerialNumber和GetFirmwareVersion用的是ref string传参,声明里给的是stringBuilder,如果你把参数声明成普通string,调用时就会报“对象未引用”或直接崩溃。资源包Demo里用的是StringBuilder,直接抄就行。

2.4 常用接口的全景:哪些函数是日常开发的主力

读完整个SDK文档,你会发现函数数量不少,但日常开发真正高频用到的就那几个,而且它们通常成对出现。下面这张表是资源包文档里最核心的接口清单,我拆包后按使用频率排了个序:

函数名(C#声明形式)作用使用频率
Connect_USB(机器号)USB连接设备高
Connect_Net(IP, 端口)网络连接设备高
ReadAllGLogData(机器号)读取全部考勤记录高
GetGeneralLogData(机器号, ref 索引, ref 时间, ref 验证方式, ref 工号, ref 状态)逐条读取记录高
SetUserInfo(机器号, 工号, 姓名, 卡号, 密码, 等级)登记用户信息中
EnrollFinger(机器号, 工号, 指纹编号)登记指纹中
ClearGLog(机器号)清空考勤记录中
SetDeviceTime(机器号)校准设备时间中
Disconnect()断开连接高

这里要注意的是:ReadAllGLogData和GetGeneralLogData是配合使用的。前者把记录加载到SDK内部缓冲区,后者从缓冲区逐条“取”出来。如果你只调ReadAllGLogData而不调GetGeneralLogData,记录是不会自动变成数组给你的。

3. 两个高频场景的完整实现:读打卡记录与登记指纹

3.1 场景一:读取考勤记录并转成本地数据结构

考勤对接最核心的需求就是“把设备里的打卡记录同步到本地数据库”。设备端的记录是一条条流水,包含工号、打卡时间、验证方式(指纹、卡、密码)、状态(正常、迟到判断由业务系统做),SDK拿回来的原始数据是平铺的参数,需要手动封装成实体类。

// 考勤记录实体 public class AttendanceRecord { public string EmployeeId { get; set; } public DateTime PunchTime { get; set; } public int VerifyMode { get; set; } public int Status { get; set; } } List<AttendanceRecord> records = new List<AttendanceRecord>(); // 先加载全部记录到SDK缓冲区 bool loadResult = device.ReadAllGLogData(1); if (loadResult) { int index = 0; string employeeId = ""; string punchTime = ""; int verifyMode = 0; int status = 0; // 逐条取出 while (device.GetGeneralLogData(1, ref index, ref punchTime, ref verifyMode, ref employeeId, ref status)) { AttendanceRecord record = new AttendanceRecord { EmployeeId = employeeId, PunchTime = Convert.ToDateTime(punchTime), VerifyMode = verifyMode, Status = status }; records.Add(record); } } Console.WriteLine($"共读取 {records.Count} 条记录");

为什么这里不用ReadAllGLogData的返回值直接代表记录数?因为对于部分固件版本,ReadAllGLogData只负责把记录从设备存储拉到SDK内部缓冲区,真正的取数要通过GetGeneralLogData循环拿。循环的终止条件是函数返回false,也就是缓冲区取完了。每次调用GetGeneralLogData后,ref index会自动递增,不需要手动加。

3.2 场景二:登记一枚新指纹

指纹登记是另一种高频操作,比如新员工入职时,不通过设备上的菜单操作,而是由人事系统远程下发指纹。

// 登记指纹 bool enrollResult = false; // 参数:机器号、工号、指纹编号 enrollResult = device.EnrollFinger(1, "EMP001", 1); if (enrollResult) { Console.WriteLine("指纹登记成功"); } else { Console.WriteLine("指纹登记失败"); }

这段代码的坑在于EnrollFinger并不能“凭空”完成登记。实际流程是:调用该方法后,设备会进入等待录入状态,屏幕上提示员工按压指纹,压完一次后通常还要再压一次确认,两次匹配成功后SDK才返回true。所以这段代码不是“一把梭”的远程写入,而是一个需要人工配合的交互过程。

常见做法是把登记逻辑封装成一个带超时的异步任务,调用后轮询SDK的状态接口,等设备上报“指纹已录入”再结束流程。资源包的Demo里有一个简单的同步版本,但生产环境建议自己加超时控制,否则员工一直不按指纹,界面会卡死在等待状态。

3.3 关于清空记录的策略:什么时候该清,什么时候不该清

读记录和清记录往往是连在一起的两个操作,但这条“懒人逻辑”在考勤场景里很容易翻车。SDK里的清空记录接口调用起来很简单:

// 清空考勤记录 bool clearResult = device.ClearGLog(1); if (clearResult) { Console.WriteLine("考勤记录已清空"); }

问题是:设备存储空间有限,老式型号只有几万条容量,不清的话会存满,然后设备屏幕上弹“内存已满”,新打卡记录写不进去。但如果每次同步完就立刻清空,一旦下次同步代码出了问题,或者本地数据库误删了数据,设备上已经没有原始流水的“后悔药”了。

我一般推荐的做法是:同步到本地数据库并确认写入成功之后,再用一个独立标志位去执行清空操作。比如在数据库写入事务中加一个批次ID,只有当这一批次全部落库、且校验条数一致后,才调用ClearGLog。设备端清空是不可逆的,这一点务必在代码注释里写明,避免后续维护的人为了“省事”把清空逻辑提前。

4. 避坑记录:调用约定、编码、驱动与其他常见问题

4.1 现象:调用函数时程序直接崩溃或内存地址报错

第一次写调用时,最典型的翻车是:demo里同样的代码能跑,自己写就崩溃,而且崩溃点是随机的,有时在连接,有时在读取记录。原因通常是调用约定不一致。SDK底层是C++编译出来的动态库,导出函数的调用约定是Cdecl还是Stdcall,直接影响参数传递和栈平衡。

解决方法是查看资源包里的接口声明文件,确认每个函数的DllImport是否正确标注了调用约定:

[DllImport("zkemkeeper.dll", CallingConvention = CallingConvention.StdCall)] public static extern bool Connect_Net(string ip, int port);

如果声明的调用约定和实际不符,C#调用时会出现“尝试读取或写入受保护的内存”之类的异常,这类问题不是参数写错,是约定不匹配。对照Demo里的声明逐个检查即可。

4.2 现象:读取到的打卡时间或姓名变成乱码

连接正常、记录也能读出来,但中文名字是乱码,或者时间字符串多出一堆空格。这类问题在两处:第一,SDK内部用的字符编码与C#默认的Unicode不一致,常见情况是设备端用GBK/ANSI,而C#拿到的是UTF-16;第二,GetGeneralLogData返回的字符串参数需要提前分配足够的缓冲区。

解决方式是在声明代码里把字符串参数加MarshalAs特性,指定合适的编码:

[DllImport("zkemkeeper.dll", CallingConvention = CallingConvention.StdCall)] public static extern bool GetGeneralLogData( int machineNumber, ref int dwIndex, [MarshalAs(UnmanagedType.BStr)] ref string timeStr, ref int verifyMode, [MarshalAs(UnmanagedType.BStr)] ref string employeeId, ref int status);

注意[MarshalAs(UnmanagedType.BStr)]的位置,必须加在字符串参数上,而不是函数体里。加了之后,SDK返回的BSTR字符串才能被C#正确解析为string。

4.3 现象:USB设备连不上,但设备管理器里能看到

设备管理器里能看到“USB Mass Storage”或“HID设备”,但SDK调用Connect_USB一直返回失败。原因大概率不是USB线或端口问题,而是驱动被系统识别成了其他类型设备。某些型号的考勤机在Windows 10/11上默认走HID驱动,而SDK需要的是串口驱动。

解决方法是找到资源包文档里的驱动安装说明,用设备管理器“更新驱动程序”,手动指向SDK自带的驱动目录,而不是选择“自动搜索”。装好之后,设备管理器里应该能看到一个独立的COM口。遇到这个问题时,不要反复拔插USB线,玄学重启没有用,驱动类型必须改对。

4.4 现象:指纹登记报“内存已满”,但设备明明没存多少指纹

设备指纹总数可能没满,但SDK登记时报错。原因多数是指纹模板编号分配混乱。中控设备的指纹模板是按“指纹编号”区分的,每个工号可以登记多枚指纹,编号范围0到9。如果同一工号重复登记时,往同一个编号上反复写,或者从10开始递增,超过设备支持的模板上限,就会提示内存满。

登记策略应该是:查询该工号已登记的指纹编号集合,找一个空位再写,而不是每次都从0开始:

// 查询某工号已登记的指纹编号 List<int> enrolledFingers = new List<int>(); for (int fingerIndex = 0; fingerIndex < 10; fingerIndex++) { bool fingerExists = device.CheckFinger(1, "EMP001", fingerIndex); if (fingerExists) { enrolledFingers.Add(fingerIndex); } } // 找一个未使用的编号 int targetIndex = 0; for (int i = 0; i < 10; i++) { if (!enrolledFingers.Contains(i)) { targetIndex = i; break; } }

如果所有编号都用完了,要先删除旧指纹再登记,否则就会踩到“内存满”的坑。资源包Demo里往往只演示了登记,没演示查询和删除的组合使用,生产环境必须补全这两段。

4.5 现象:读取到的记录有重复,或者同一时间段出现两遍

原因有两个:一是设备断网期间的记录在联网后补传,和之前主动拉取的记录重叠;二是多线程场景下,多个后台任务同时调用了同一设备对象的读取接口,底层缓冲区被并发读写,返回了重复或不完整的记录。

解决方式很简单:本地库以“设备号 + 打卡时间 + 工号”作为唯一索引,写入时用INSERT OR IGNORE或先查重再插入。另外,如果一个程序里需要定时拉取多个设备,建议每个设备各创建独立的CZEMClass实例,不要共用同一个SDK对象。

5. 进阶技巧:用本地时间窗口精准锁定考勤记录

考勤场景里最常被追问的需求是“帮我拉上周一到周五的记录”。如果设备里总共有几万条流水,每次都全量拉回来再过滤,网络开销和内存开销都很浪费。更聪明的做法是:读取记录后,在C#内存里用时间窗口过滤,只保留目标日期范围内的数据。

public List<AttendanceRecord> GetRecordsInRange(DateTime start, DateTime end) { List<AttendanceRecord> allRecords = new List<AttendanceRecord>(); // 读取设备全部记录 if (device.ReadAllGLogData(1)) { int index = 0; string employeeId = ""; string punchTime = ""; int verifyMode = 0; int status = 0; while (device.GetGeneralLogData(1, ref index, ref punchTime, ref verifyMode, ref employeeId, ref status)) { DateTime currentTime = Convert.ToDateTime(punchTime); // 在本地内存中过滤时间窗口 if (currentTime >= start && currentTime <= end) { allRecords.Add(new AttendanceRecord { EmployeeId = employeeId, PunchTime = currentTime, VerifyMode = verifyMode, Status = status }); } } } return allRecords; }

这样做的主要原因是,SDK本身不提供按时间范围查询的接口,读取函数只有“全量加载”和“逐条取出”两层,没有直接的WHERE条件。在本地过滤看似多了一步,但实际比全量入库再查询要简单可靠得多。

边界处理上要注意三点:第一,时间窗口的结束时间建议加上“当天最后一秒”或直接用小于次日零点,避免正好卡在边界上的打卡记录被漏掉;第二,设备时间如果没有校准,读出的记录时间和服务器时间可能差几分钟,必要性允许时先调SetDeviceTime再拉取数据;第三,Convert.ToDateTime遇到空字符串会抛异常,逐条循环时要先判断非空,否则遇到设备里某些损坏记录,整个同步流程会直接中断。

最后一个细节是,调SetDeviceTime校准的是设备本身,不是本次读取的数据。如果你怀疑设备时间和标准时间差得远,一定要在读取前校准而不是读取后补差额。从那以后,我每次对接新设备都强制走一遍“先测连接、再校准时钟、再全量拉取并过滤、最后清空”的流程,这套顺序跑通之后再接业务系统就顺畅多了。希望这份笔记能帮你把中控考勤机这块硬骨头啃下来,少走几步冤枉路。

本文还有配套的精品资源,点击获取

相关新闻

Spring Cache五大注解详解:从入门到缓存实战避坑指南

Spring Cache五大注解详解:从入门到缓存实战避坑指南

做后端开发的&#xff0c;几乎都见过这种场景&#xff1a;QPS 稍微上来一点&#xff0c;数据库连接先报警&#xff0c;然后代码里开始到处塞缓存逻辑。读的时候先查 Redis 再查库&#xff0c;写的时候先更库再删缓存&#xff0c;每个方法前面都要手写一坨缓存模板代码&#xff…

2026/10/20 7:23:00 阅读更多 →
基于粒子群算法的电力系统最优潮流经济调度模型

基于粒子群算法的电力系统最优潮流经济调度模型

电力系统的经济调度&#xff0c;说白了就一个问题&#xff1a;负荷就在那儿摆着&#xff0c;各台发电机组出多少力&#xff0c;才能既满足安全约束又把总成本压到最低&#xff1f;这句话听着简单&#xff0c;真上手写模型的时候就会发现&#xff0c;负荷平衡、节点电压、线路容…

2026/10/20 7:23:00 阅读更多 →
把对话模型塞进机器猫:低配开发板部署Jev的实战记录

把对话模型塞进机器猫:低配开发板部署Jev的实战记录

最近的状态有点魔怔。白天写技术方案&#xff0c;晚上回家还在折腾一个听起来像玩物丧志的项目&#xff1a;我想把一个叫 Jev 的对话模型&#xff0c;塞进桌面上那只机器猫的脑壳里。准确说&#xff0c;这不是一只真猫&#xff0c;而是我攒的一套猫头造型桌面机器人套件&#x…

2026/10/20 7:23:00 阅读更多 →

最新新闻

RAG面试高频考点全解析:从检索到生成的优化链路

RAG面试高频考点全解析:从检索到生成的优化链路

1. 为什么面试官揪着RAG不放&#xff1a;先把这个动机吃透我最近在系统梳理大模型方向的面试题&#xff0c;RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;几乎是每家必问的模块。尤其是做应用层项目的岗位&#xff0c;RAG的提问密度比微…

2026/10/20 8:14:30 阅读更多 →
机票电商平台重构实战:领域拆分与异步订单链路设计

机票电商平台重构实战:领域拆分与异步订单链路设计

简介&#xff1a;eyoo旅游机票电商平台&#xff08;重构版&#xff09;是一份基于Ruby与Rails技术构建的在线机票销售系统项目资源&#xff0c;面向Ruby后端开发者、电商平台学习者以及关注系统重构优化的人群。项目覆盖业务逻辑处理、ActiveRecord数据库操作、RESTful API接口…

2026/10/20 8:14:30 阅读更多 →
Docker容器化实战指南:从DockerFile到Compose编排全解析

Docker容器化实战指南:从DockerFile到Compose编排全解析

1. 先从一张架构图说起&#xff1a;为什么我们绕不开Docker我的个人项目X&#xff0c;从零开始做全栈实践。最初只开了一个云服务器&#xff0c;上面跑着数据库、缓存、定时任务&#xff0c;后来要加前后端服务&#xff0c;每个环境都折腾一遍依赖。结果不到三个月&#xff0c;…

2026/10/20 8:14:30 阅读更多 →
PHP借贷源码拆解与本地部署:从业务闭环到避坑实践

PHP借贷源码拆解与本地部署:从业务闭环到避坑实践

简介&#xff1a;一套基于 PHP 实现的借贷公司/网贷平台整站源码&#xff0c;面向需要快速搭建在线借贷系统的技术团队、中小型网贷项目开发者和 PHP 学习者。源码在得得系统基础上完成改编&#xff0c;并经过实际测试可用&#xff0c;覆盖用户注册、信用评估、借款申请、还款计…

2026/10/20 8:14:30 阅读更多 →
实战推演:用 esp32-iphone-agent 的思路,给自己 iPhone 做个“自动化开关“,可行吗

实战推演:用 esp32-iphone-agent 的思路,给自己 iPhone 做个“自动化开关“,可行吗

实战推演&#xff1a;用 esp32-iphone-agent 的思路&#xff0c;给自己 iPhone 做个"自动化开关"&#xff0c;可行吗 【免费下载链接】esp32-iphone-agent Let an AI model use a real iPhone with a $7 ESP32: AirPlay eyes, USB HID hands, any OpenAI-compatible …

2026/10/20 8:14:30 阅读更多 →
Agent上线就崩?从状态管理到可观测性,拆解工程化落地的五大核心难题

Agent上线就崩?从状态管理到可观测性,拆解工程化落地的五大核心难题

1. 演示与上线之间的鸿沟到底在哪做 Agent 项目的人大概都经历过这种场面&#xff1a;内部演示的时候&#xff0c;Agent 流畅地理解需求、调用工具、多步推理、最后给出一个漂亮的答案&#xff0c;会议室里一片掌声。然后老板说“下周上线吧”&#xff0c;结果一上线&#xff0…

2026/10/20 8:13:30 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →