简介:这份资源是华大推出的多合一通用读写读卡器OCX控件,面向需要在Windows应用中集成读卡器读写功能的开发者,尤其适合门禁、会员管理、数据采集等场景的软件集成。控件以ActiveX技术封装,提供统一接口兼容多种读卡器设备,可简化调用逻辑、降低开发成本。压缩包共2个文件,包含1个html和1个rar,整体约460KB;其中html为测试页面,可在IE浏览器中打开验证控件功能,rar内为多合一通用读写控件的1.2版本,便于更新或按地区使用。目前已有3226人学习下载,说明该控件在实际项目中有一定参考价值。读者可借此快速了解控件的调用方式与集成思路,通过测试页面验证读写效果,并获取较新版本以完善功能,适合需要对接多种读卡器设备的开发人员参考使用。
1. 多合一读卡器 OCX 控件:从解压到跑通第一个读卡 Demo
手上有个老项目要对接门禁读卡器,翻出一份「华大多合一通用读写读卡器 ocx 控件.zip」,解压后只有两个东西:一个测试(IE浏览器打开).html,一个多合一通用读写控件(北京)1.2.rar。别小看这俩文件,它解决的是读卡器开发里最烦的一件事——不同厂商、不同型号的读卡器接口五花八门,串口协议、USB HID、键盘口模拟各玩各的,而这个 OCX 控件把读写逻辑封成一个 ActiveX 组件,前端或桌面程序只要调几个方法就能完成寻卡、读块、写块、改密钥。适合谁?做门禁、会员管理、考勤、一卡通这类需要跟读卡器打交道的 Windows 桌面或 IE 内嵌页面的开发者。前提是你得接受它的运行环境:ActiveX 只活在 Windows + IE 内核或支持 COM 的宿主里,现代浏览器直接歇菜。这篇就把我拆包、注册、调通、踩坑的全过程摊开讲。
2. 拆包与注册:OCX 控件在 Windows 上怎么落地
拿到压缩包先别急着双击 HTML,那个测试页只是壳,真正的核心在 RAR 里。这一章把文件结构、依赖判断、注册流程走一遍,跑通了再谈调用。
2.1 先看清压缩包里到底有什么
外层 zip 解压后是两个文件,内层 RAR 才是控件本体。常见做法是先把 RAR 解到独立目录,别跟测试页混在一起,否则后面注册路径容易搞乱。解压后一般能看到这几类文件:
| 文件类型 | 典型文件名 | 作用 |
|---|---|---|
| OCX 控件 | *.ocx | 核心 COM 组件,读写卡逻辑都在这 |
| 依赖 DLL | *.dll | 串口通信、加密算法等支撑库 |
| 注册脚本 | *.bat/*.reg | 一键注册或写注册表 |
| 说明文档 | *.txt/*.doc | 接口方法、参数、返回值说明 |
| 测试页 | 测试(IE浏览器打开).html | 调用示例,验证控件是否可用 |
如果解压后没看到.ocx,只有一堆 DLL,那说明控件可能被拆成多个模块,得按文档顺序注册。我一般先看有没有regsvr32相关的 bat,有就直接用,没有就手动来。
2.2 注册 OCX:32 位和 64 位别搞混
ActiveX 控件分 32 位和 64 位,注册命令也不一样。多数老读卡器控件是 32 位的,哪怕你系统是 64 位 Windows,也得用 32 位的regsvr32去注册,否则 IE 里死活加载不出来。
:: 以管理员身份打开 CMD,进入控件所在目录 cd /d D:\ReaderSDK\MultiCard :: 32 位控件用 SysWOW64 下的 regsvr32 注册(64 位系统) C:\Windows\SysWOW64\regsvr32.exe MultiCardReader.ocx :: 如果是纯 64 位控件,才用 System32 下的 C:\Windows\System32\regsvr32.exe MultiCardReader64.ocx注册成功会弹一个「DllRegisterServer 成功」的框。如果报「模块加载失败」,先检查是不是缺依赖 DLL,把同目录所有 DLL 一起放到System32或SysWOW64里再试。参数上注意:regsvr32后面跟的是控件完整路径,路径里有空格要加引号,否则会当成多个参数解析。
2.3 用测试页验证控件是否真的活了
注册完别急着写代码,先拿自带的测试(IE浏览器打开).html验一把。双击用 IE 打开,如果页面提示「控件未加载」或一片空白,八成是 IE 的安全设置拦了 ActiveX。
// 测试页里通常是这样创建控件的 var reader = new ActiveXObject("MultiCardReader.ReaderCtrl.1"); if (reader) { // 初始化端口,0 一般代表 USB 或第一个串口 var ret = reader.Init(0); console.log("初始化返回:" + ret); }这段代码的逻辑是:用ActiveXObject按 ProgID 创建控件实例,ProgID 具体叫什么得看文档或注册表里HKEY_CLASSES_ROOT下的项。Init的参数是端口号或设备索引,返回值一般 0 表示成功,非 0 是错误码。如果new ActiveXObject直接抛异常,说明控件没注册成功或者 ProgID 写错了。验证通过的标准是:能创建对象、能初始化、能寻到卡。
提示:IE 里要允许「运行 ActiveX 控件和插件」,把站点加到受信任站点,安全级别调低。这一步不做,后面全是白忙。
3. 接口调用实战:寻卡、读块、写块的完整链路
控件注册好了,接下来是真正干活的部分。这一章按「初始化 → 寻卡 → 读块 → 写块 → 释放」的顺序,把每个方法的调用方式、参数含义、返回值判断讲清楚,代码可以直接抄。
3.1 初始化与寻卡:先把卡找到
读卡器的基本流程是:打开设备 → 寻卡 → 选卡 → 操作。不同控件方法名可能略有差异,但套路一致。下面是一段典型的调用代码:
// 创建控件实例 var reader = new ActiveXObject("MultiCardReader.ReaderCtrl.1"); // 1. 初始化设备,参数 0 表示自动选择第一个可用读卡器 var initRet = reader.Init(0); if (initRet !== 0) { alert("初始化失败,错误码:" + initRet); } // 2. 寻卡,参数 1 表示寻全部卡类型,超时 3000ms var cardType = reader.SearchCard(1, 3000); if (cardType <= 0) { alert("未寻到卡,检查卡片是否放在感应区"); } // 3. 选卡,拿到卡片句柄或序列号 var cardNo = reader.SelectCard(); console.log("卡片序列号:" + cardNo);逻辑说明:Init负责跟读卡器建立通信,参数是设备索引,多台读卡器时用 0、1、2 区分。SearchCard的第一个参数是卡类型掩码,1 通常代表 Mifare One,具体值看文档;第二个参数是超时毫秒数,太短会误报无卡。SelectCard返回卡片序列号,后续读写都基于这个句柄。参数上最容易翻车的是超时设太短,尤其是卡片刚放上去还没稳定,建议不低于 2000ms。
3.2 读块与写块:扇区、块号、密钥一个都不能错
Mifare 卡按扇区(Sector)和块(Block)组织,一个扇区 4 个块,第 0 扇区第 0 块是厂商信息,只读。读写前要先验证密钥,否则直接返回权限错误。
// 4. 验证密钥,扇区 1,密钥类型 A,默认密钥 6 个 FF var key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; var authRet = reader.AuthKey(1, 0, key); // 扇区1,密钥类型A if (authRet !== 0) { alert("密钥验证失败,检查密钥类型和扇区号"); } // 5. 读块,扇区 1 的块 4,读 16 字节 var readData = reader.ReadBlock(4); console.log("读到的数据:" + readData); // 6. 写块,往块 5 写 16 字节数据 var writeData = "0102030405060708090A0B0C0D0E0F10"; var writeRet = reader.WriteBlock(5, writeData); if (writeRet !== 0) { alert("写块失败,错误码:" + writeRet); } // 7. 释放资源 reader.Release();逻辑说明:AuthKey三个参数分别是扇区号、密钥类型(0 为 A 密钥,1 为 B 密钥)、密钥字节数组。密钥验证是按扇区来的,验证一次该扇区所有块都能操作。ReadBlock参数是绝对块号,不是扇区内偏移,比如扇区 1 的块 4 到块 7,块号就是 4 到 7。WriteBlock的数据必须是 16 字节的十六进制字符串,少一位多一位都会失败。Release别忘了调,否则下次初始化可能报设备占用。
注意:写块前一定先读一遍确认数据,写错了没有后悔药,尤其是块 0 和密钥块,写坏卡片直接报废。
3.3 参数速查与返回值对照
把常用方法的参数和返回值整理成表,调的时候对着看,比翻文档快。
| 方法 | 参数 | 含义 | 返回值 |
|---|---|---|---|
| Init | 设备索引 | 0 为第一个读卡器 | 0 成功,非 0 错误码 |
| SearchCard | 卡类型, 超时 | 类型掩码, 毫秒 | >0 卡类型,<=0 无卡 |
| SelectCard | 无 | 选中当前卡 | 序列号字符串 |
| AuthKey | 扇区, 密钥类型, 密钥 | 类型 0=A, 1=B | 0 成功 |
| ReadBlock | 块号 | 绝对块号 | 16 字节十六进制串 |
| WriteBlock | 块号, 数据 | 数据须 16 字节 | 0 成功 |
| Release | 无 | 释放设备 | 无 |
4. 避坑与排查:那些让我加班到凌晨的坑
控件这东西,跑通了觉得简单,跑不通就是黑匣子。这一章列几个我实际踩过的坑,每条按现象、原因、解决写,遇到问题先对照排查。
4.1 现象:IE 里控件加载不出来,页面一片空白
原因:IE 的安全区域设置拦了 ActiveX,或者控件没注册成功。很多人以为注册了就行,其实 IE 对未签名控件默认禁止。
解决:把测试页所在目录加到「受信任的站点」,在「自定义级别」里把「运行 ActiveX 控件和插件」设为启用,「下载未签名的 ActiveX 控件」也设为启用。如果还不行,用regedit查HKEY_CLASSES_ROOT下有没有控件的 ProgID,没有就是注册失败,回到第 2 章重新注册。
4.2 现象:初始化返回非 0,读卡器没反应
原因:读卡器驱动没装、USB 没插好、或者被其他程序占用了端口。多合一控件虽然兼容多种读卡器,但底层还是走串口或 USB,驱动不对照样白搭。
解决:先看设备管理器里读卡器有没有黄色感叹号,有就装驱动。然后确认没有别的程序(比如厂商自带的测试工具)占着设备,关掉再试。如果用的是串口读卡器,检查波特率设置,常见是 9600 或 115200,控件文档里一般有说明。
4.3 现象:寻到卡了但读块返回权限错误
原因:密钥没验证,或者验证的扇区和读的块不在同一扇区。Mifare 卡的密钥是按扇区管理的,跨扇区读必须先验证目标扇区的密钥。
解决:确认AuthKey的扇区号跟ReadBlock的块号对应。比如读块 8,块 8 属于扇区 2(块 8-11),那就要先AuthKey(2, 0, key)。另外密钥类型别搞反,A 密钥和 B 密钥权限可能不同,有些卡只允许 A 密钥读。
4.4 现象:写块成功但读出来数据不对
原因:写的数据格式不对,或者写到了错误的块。最常见的是数据长度不是 16 字节,或者把十六进制字符串当成了 ASCII 字符串传进去。
解决:写之前先ReadBlock读一遍原数据,确认块号没错。数据必须是 32 个十六进制字符(16 字节),比如"0102030405060708090A0B0C0D0E0F10",不能是"1234567890"这种。写完立刻读回来比对,不一致就重写。
4.5 现象:程序退出后读卡器灯还亮着,下次初始化失败
原因:没调Release,设备句柄没释放,被上一个进程占着。
解决:在window.onunload或程序退出事件里强制调Release。如果已经卡住了,把读卡器拔了重插,或者重启相关服务。养成习惯:Init和Release成对出现,中间用try...finally包住,异常也能释放。
5. 进阶技巧:把 OCX 塞进现代桌面程序与批量读卡
IE 终究是要退场的,但 OCX 控件本身是 COM 组件,不一定非得在 IE 里跑。这一章讲两个进阶方向:一是用支持 COM 的宿主程序调用,二是批量读卡时的性能与稳定性处理。
5.1 在非 IE 宿主里调用 OCX
OCX 的本质是 COM 服务器,只要宿主支持 COM,就能创建实例。常见做法是用 C# 的Type.GetTypeFromProgID或者 Python 的win32com.client来调,这样就能把读卡逻辑嵌进 WinForm、WPF 甚至控制台程序里。
# Python 调用 OCX 控件示例,需安装 pywin32 import win32com.client # 按 ProgID 创建控件实例 reader = win32com.client.Dispatch("MultiCardReader.ReaderCtrl.1") # 初始化并寻卡 if reader.Init(0) == 0: card_type = reader.SearchCard(1, 3000) if card_type > 0: card_no = reader.SelectCard() print("卡号:", card_no) else: print("未寻到卡") reader.Release()逻辑说明:Dispatch按 ProgID 拿到 COM 对象,后面方法调用跟 IE 里一模一样。参数上注意 Python 里整数和字符串的传递,十六进制数据要转成字符串再传。这种方式的好处是摆脱了 IE 限制,坏处是调试不如浏览器直观,出错信息得靠异常捕获。
5.2 批量读卡的稳定性处理
门禁或考勤场景经常要连续读几十上百张卡,这时候稳定性比功能更重要。我一般会做三件事:加超时重试、加延时、加异常兜底。
import time def read_card_safe(reader, retry=3): for i in range(retry): try: if reader.SearchCard(1, 2000) > 0: return reader.SelectCard() except Exception as e: print("第 %d 次重试,异常:%s" % (i + 1, e)) time.sleep(0.3) # 每次重试间隔 300ms,给读卡器恢复时间 return None逻辑说明:retry控制重试次数,time.sleep给读卡器留恢复时间,避免连续寻卡把设备搞死。参数上 0.3 秒是个经验值,太快没用,太慢影响吞吐。异常兜底是为了防止某张卡异常导致整个循环崩掉。
5.3 验证控件是否真的在工作
最后说个验证技巧:别只看返回值,拿一张已知数据的卡,读出来跟预期比对。我习惯在初始化后先读块 0 的厂商信息,虽然不能写,但能读出来就说明通信链路是通的。然后再做密钥验证和读写测试。这样分层验证,出问题能快速定位是通信层还是权限层。
从那以后我每次拿到新的读卡器控件,都强制走一遍「注册 → 测试页验证 → 读厂商块 → 密钥验证 → 读写比对」的流程,少一步后面都可能翻车。希望帮到你。
本文还有配套的精品资源,点击获取