ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

医保卡Z90读卡测试全解析:驱动、PC/SC验证与避坑指南

医保卡Z90读卡测试全解析:驱动、PC/SC验证与避坑指南 简介面向医疗信息化与硬件集成开发者的Z90读卡器C#测试工程完整浓缩了医保卡读取所需的核心实现流程。压缩包共有50个文件、约1.11MB其中11个动态库为读卡器运行依赖9个C#源文件与XAML界面文件共同构成可直接编译的Visual Studio项目另含可执行程序以便直接验证并附带少量配置与说明文档便于对照源码理解调用链路。程序覆盖串口参数设置、卡片数据读取、二进制解析、结果保存以及常见异常处理等关键环节可在开发环境中打开调试或运行程序快速查看效果。整体体积小、目录简洁适合医院管理系统、自助终端等场景下的快速预研与二次开发。已有1232人学习下载对刚接触读卡器或C#串口通信的开发者具有较好的参考价值也能为后续功能扩展提供实践起点。1. 医保卡Z90读卡测试新到一台读卡器先过这一关再谈上线医保科新装的结算终端突然读不出卡运维把 Z90 拔下来在另一台机器上装好驱动、插上医保卡双击测试工具能读出卡号就认定设备没问题可一旦放回结算台问题照旧。拿到手的“医保卡Z90读卡测试.rar”往往能帮你过第一关却也是坑最多的一关。这篇文章从一个测试包出发把它拆成驱动、测试工具、动态库三类东西讲清怎么通过 PC/SC 做最小读卡验证、怎么用脚本做连续压力回归以及插卡读卡里最容易翻车的几个问题。适不适合上线、值不值得拿来当测试基线读完自己就能判断。2. 拆开压缩包Z90 读卡测试包里哪些文件影响成败2.1 先搞清楚压缩包里每一类文件的用途Z90 是医保结算场景里很常见的接触式 IC 卡读写器设备本身通常是一个小方盒正面有插卡槽侧边有串口或 USB 口部分型号还带一个 PSAM 卡座。运维拿到手的“医保卡Z90读卡测试.rar”通常不是单个 exe而是一个压缩包。常见做法是解压后看到这几类内容医保卡Z90读卡测试/ ├─ 驱动/ │ ├─ CP210x_VCP_Win_XP_S2K3_Vista_7.exe # USB转串口芯片驱动 │ └─ pcsc_driver_setup.exe # 智能卡读卡器过滤驱动可选 ├─ TestTool/ │ ├─ Z90Test.exe # 32位读卡测试程序 │ └─ Z90Test.ini # 串口号/波特率/测试次数配置 ├─ SDK/ │ ├─ z90.dll # 厂商提供的读卡器动态库 │ └─ sdtapi.dll # 通用读卡动态库封装部分版本 └─ 用户手册.pdf上面是典型目录结构实际包名和文件数因厂家打包习惯不同而有差异。关键要分清三类东西驱动负责让操作系统认到设备exe 只是调用动态库的测试界面动态库才是真正跟读卡器对话的实现。区分一个包能不能用于二次开发就看 SDK 目录里是否提供了带“读卡函数导出”的动态库而不是只看 exe。解压后建议先用系统命令确认文件位数和是否存在依赖缺失# Windows PowerShell 下执行 Get-Item .\SDK\z90.dll | Select-Object FullName, Length, LastWriteTime python -c from ctypes import windll; windll.LoadLibrary(r.\SDK\z90.dll); print(dll ok)这段代码是在解压后的目录里确认 z90.dll 能被当前进程加载。LoadLibrary 成功说明运行库完整如果报“找不到指定的模块”或者 0x7E 错误多数情况是 VC 运行库缺失或者你所在进程是 64 位而 dll 是 32 位两个位数对不上。后面章节会专门说这个坑。2.2 驱动部分串口枚举和 PC/SC 枚举是两条路线Z90 这类读卡器进入系统有两种常见方式一种是以“USB 转串口 私有协议”方式工作设备管理器里看到的是 COM 口另一种是装了智能卡过滤驱动后以“智能卡读卡器”方式枚举应用层走 PC/SC 接口。这决定了你后面是用厂商 dll 还是用 pyscard 来读卡。要确认当前是哪条路线最简单的方法# PowerShell 检查串口 Get-WmiObject Win32_SerialPort | Select-Object DeviceID, Caption, Description # Python 检查 PC/SC 读卡器是否在内核层被识别 python -c from smartcard.System import readers; print(readers())如果第一段命令能列出“COM3 - USB Serial Port”说明走的是串口路线如果第二段命令能打印出“Z90 Smart Card Reader”之类的名称说明走的是 PC/SC 路线。两者不是互斥的有些驱动装上后既保留了虚拟串口也注册了 CCID 设备。实际项目里医保结算软件普遍要求 PC/SC 设备枚举因为 PSAM 卡认证、密钥分散计算在业务代码里直接调系统读卡器接口。这一点在后面的避坑章会再展开。注意实际压缩包内文件命名和数量会因厂商不同而不同不要因为包内没有驱动目录就认为缺文件只需确认是否有 exe 和 dll 两类核心文件即可。2.3 测试工具里的两个“看不见”的关键文件除了 exe 和 dll回读 ini 和日志文件值得先看。Z90Test.ini 里的波特率参数直接决定测试工具能否和读卡器握手。医保场景常见做法是保持出厂默认 115200但如果你接的是老式串口延长线又没接地可能会因为信号质量导致握手失败这时候把波特率降到 9600 反而能通过测试。日志文件是另一个容易被忽略的信息源。有的测试包会在程序目录下生成 z90_log.txt记录每次读卡的 APDU 包和返回码。遇到“能读卡号但返回码异常”的情况只有看这层数据才能定位到是传输层出错还是应用层数据错误。如果包里没有日志也可以开一个串口抓包软件挂在 COM 口上观察读卡器发出的 ATR 和命令序列来反推问题。2.4 先定路线再选工具做任何 Z90 读卡测试之前我一般会先用 10 分钟走完上面两步解压看文件类型确认当前枚举路线。这两步没走通就去点按钮后面出了坑很难判断是硬件问题还是软件问题。这一步还决定了后面你是用“厂商提供的工具”还是“自写脚本 系统接口”来做测试——前者适合现场快速判断后者适合批量回归。路线定错了后面所有验证都是在错误基线上打转。3. 让 Z90 和医保卡跑通一次完整读卡两种可行做法3.1 做法一用 Z90Test 工具做现场快速验证拿到测试包后最快能证明 Z90 能工作的方法是把医保卡插进卡槽打开 Z90Test.exe选择对应串口点击“读卡”。正常的流程是程序向读卡器发送复位命令读卡器给卡上电卡回传 ATR程序再发送选择医保应用目录的命令最后读取卡内基本文件并解析出卡号、姓名、证件号等信息。整个过程用时在 200 毫秒到 1 秒不等。现场验证要注意三个参数串口号、波特率、卡片是否插到位。串口号可以在设备管理器里看波特率优先用 ini 文件的默认值卡片插到位的感觉是卡槽底部有“咔嗒”一下就位且芯片面朝上。很多新人第一次测试失败是卡没插到底读卡器上电后拿不到 ATR直接报“读卡失败”。如果 Z90Test 能读出卡号建议再做一次“重复读 10 次”的稳定性验证确认每次返回的卡号一致且连续 10 次没有出现一次超时。这一步能筛除极少数触点氧化、卡槽弹片压力不均的设备别急着把设备发给业务部门。3.2 做法二用脚本走 PC/SC 接口读卡适合回归验证工具能读不代表你后续程序能读。在开发读卡逻辑前我更推荐用脚本直接调用系统智能卡接口做最小验证。下面是一个用 Python 和 pyscard 的完整示例# -*- coding: utf-8 -*- # 依赖pip install pyscard from smartcard.System import readers from smartcard.util import toHexString, toBytes, toASCIIString # 1. 枚举系统读卡器 r readers() if not r: print(未发现PC/SC读卡器请检查驱动枚举模式) exit(1) reader r[0] print(使用读卡器:, reader) # 2. 连接并复位获取 ATR connection reader.createConnection() connection.connect() print(ATR:, toHexString(connection.getATR())) # 3. 选择医保应用目录以 0x00A4 选择文件为例 SELECT_DF [0x00, 0xA4, 0x04, 0x00] # 医保应用目录标识此处按测试卡说明填写实际值以卡内目录为准 APP_PATH [0x11, 0x00, 0x00] # 示意值 data, sw1, sw2 connection.transmit(SELECT_DF [len(APP_PATH)] APP_PATH) print(选择目录返回:, toHexString(data), fSW{sw1:02X}{sw2:02X}) # 4. 读取卡号文件示意短文件一次读取 READ_BINARY [0x00, 0xB0, 0x00, 0x00, 0x10] data, sw1, sw2 connection.transmit(READ_BINARY) print(卡号数据:, toASCIIString(data) if data else 无数据) connection.disconnect()这段脚本看起来短实际踩坑点都在细节上。第 2 步的 connect() 内部会做一次 T0 或 T1 协议协商不需要你自己写 PPS。第 3 步选择应用目录时医保卡的应用目录标识在不同发卡地区可能不同测试前先看测试工具读到的 ATR 和目录名别直接把 APP_PATH 写死。第 4 步里的 READ_BINARY 偏移地址 0x0000 和长度 0x10 只是示意真实文件的偏移和长度必须从卡片的文件控制信息FCIFile Control Information里解析不要读完 16 字节就当成完整卡号。把上面脚本跑通后才算真正证明“Z90 系统 PC/SC 栈 你的调用代码”三层链路都没有问题。以后做读卡功能只要这个脚本能过就不用再怀疑读卡器硬件。3.3 参数与选型理由为什么优先 PC/SC 而不是厂商 dll读卡器测试路径有两条厂商 dll 和 PC/SC。多数医保业务系统最终会选 PC/SC原因是它不依赖特定厂商的私有 API换读卡器型号时业务层不用改。厂商 dll 的优势是接口更贴近设备特性比如直接支持 PSAM 卡操作缺点是每换一个硬件厂商就要换一版 dll对长期维护不友好。建议上医疗业务前做一次“双通道对比”测试内容厂商 dll 结果PC/SC 结果结论能读到卡号是是双通道正常选医保目录是是应用层正常连续读卡 50 次稳定偶发一次超时排查触点/驱动版本表格里这种对比能快速定位到瓶颈在驱动还是在程序。如果 PC/SC 偶发超时而厂商 dll 稳定问题基本出在系统智能卡驱动栈和读卡器的兼容性上反过来如果两边都不稳定优先检查卡槽触点、线缆和供电这类硬件问题不是换个接口能解决的。3.4 读 ATR 是测试里必须会看的一步复位应答ATR是读卡器和卡片握手后的第一段数据它包含协议、历史字节等信息。对 Z90 测试来说ATR 有两大用途判断卡片是否正常响应判断当前通讯协议是 T0 还是 T1。比如 ATR 里出现 0x3B 开头通常代表正向约定、0x3F 开头代表反向约定这在后面决定 APDU 是否要按 T1 组包时会用到。大多数 Z90 测试工具会把 ATR 直接显示出来你只要把它记下来作为后续脚本验证的基准值。多记录几台同型号设备的 ATR时间长了就能看出哪些字节随固件版本漂移、哪些字节是卡片的固定特征。4. 避坑Z90 读卡测试最容易翻车的 5 个场景4.1 现象工具打不开串口报“设备不存在”原因USB 转串口芯片驱动没有正确安装或者串口号在设备管理器里被其他软件占用。某些医保终端软件会在后台锁定 COM 口测试工具再打开就会失败。解决先在设备管理器里看端口COM 和 LPT下有没有 Z90 对应的串口。如果只有“USB Composite Device”而没有 COM 口说明驱动没装好去驱动目录里重装对应芯片的驱动。如果串口号规律性地在设备重新插拔后漂移建议在设备管理器高级设置里把 COM 口固定为 COM3 或 COM4 这种常用口号避免每次插拔变号。在打开工具前先确认没有其他进程占用串口。python -c import serial; s serial.Serial(COM3); print(s.name)这行代码用 pyserial 测试能不能独占打开串口。如果这里就报 PermissionError说明有别的程序占着 COM3即使测试工具打不开也不意外。一段简单的探测代码能帮你区分是驱动问题还是占用问题。4.2 现象卡插进去却报“无卡”或“读卡超时”原因医保卡芯片触点氧化、卡没插到位或者读卡器卡槽的弹片老化导致接触电阻过大。很多测试失败都被误判成“读卡器坏了”其实是物理层的接触不良。解决用橡皮擦沿芯片触点轻轻擦拭不要用手直接摸芯片插卡后确认底部卡到位如果反复发生把卡换到同一台读卡器的另一个卡槽或换一台读卡器测试。还是不行就换一张测试卡。判断卡还是读卡器有问题最有效的办法是交叉测试同一张卡插到另一台机器以及同一台机器插另一张卡对口诀“卡换机不换、机换卡不换”五分钟就能定位。4.3 现象自写程序读不到内容但 Z90Test 工具能读原因最典型的场景是 Python 脚本用 ctypes 加载 z90.dll 直接调用结果 LoadLibrary 失败或传入参数类型不对。Z90 的动态库通常是 32 位你用的 Python 是 64 位而 64 位进程不能直接加载 32 位 dll于是所有调用都死在第一步。解决先确认 dll 位数再决定用哪种方式调用。如果业务系统是 C# 写的把 AnyCPU 改成 x86 编译如果是 Python可以换 32 位解释器或者干脆走 PC/SC 而不是厂商 dll。用 PC/SC 方式没有位数问题这也是很多项目最终选 PC/SC 的原因。位数不对是玄学吗不是是 Windows 子系统把进程隔离了踩过一次以后就记得先查位数再做调用方案。# Linux 或 Git Bash 下检查 dll 是 32 位还是 64 位 file /path/to/sdk/z90.dll如果有条件在 Windows 下用 dumpbin /headers 查看文件头也能判断。这个问题最容易在“开发机是 64 位、现场机器也是 64 位”的配置下被忽略等到发布阶段才炸出来。4.4 现象偶尔读卡通过但连续测试时出现一次“失败”原因触点磨损到临界状态读卡器上电瞬间接触不良或者 USB 供电不足导致读卡器电压短暂跌落。常见于机柜 USB 延长线超过 2 米且没有独立供电的情况。解决先用 USB 电压测量工具观察供电情况读卡器工作时 USB 电压要稳定在 4.75V 以上把延长线换成带屏蔽的短 USB 线。另一个办法是区分供电与触点拿一张新卡在同一台机器上连续读 50 次如果新卡全过而旧卡偶发失败问题基本在卡不在设备建议直接触发更换。如果是设备偶发失败但卡是新的优先检查读卡器卡槽弹片是否被异物撑变形。4.5 现象Z90 工具能读卡但 pyscard 枚举不到读卡器原因读卡器驱动只安装了 USB 转串口驱动没有装 PC/SC 智能卡读卡器过滤驱动。Windows 默认把它当一个 COM 设备PC/SC 栈自然看不到它。解决装回驱动目录里的 pcsc_driver_setup.exe 或从设备管理器强制更新为“智能卡读卡器”设备然后重插 USB。重装后运行python -c from smartcard.System import readers; print(readers())能枚举到 Z90 才算成功。这一步做完上一章的脚本才能跑通。很多现场工程师被这一步卡住其实不是 Z90 的问题是驱动装得不全。5. 把“能读一次卡”升级成“连续读 50 次全过”回归脚本与参数固化5.1 为什么需要可重复的回归测试而不是只点一次测试按钮医保结算台的读卡器不是一次性设备它每天会被插拔几十上百次。第一次能读卡只能说明设备纸面合格触点磨损、接线老化是慢变量需要持续监测。做过实际的医保终端维护的人都会同意集成测试时稳定上线后三个月开始偶发失败是最常见的隐性故障。把“能读一次”升级成“连续读 N 次且结果一致”才是可验证的准入标准。我建议每次新部署 Z90 前跑一轮“连续读卡 50 次”压力测试50 次内 0 失败、卡号一致性 100%才算通过。这轮测试的耗时不到 5 分钟却能挡掉不少返修。如果 50 次里出现 1 次失败别急着下结论先看失败发生在启动的前 5 次还是后面——前 5 次失败多为卡槽未热插拔后面失败多为接触压力问题。5.2 写一个可持续跑的最小回归脚本下面脚本基于上一章的 PC/SC 代码做了扩展加上了重复次数、失败记录和结果落盘# -*- coding: utf-8 -*- # 连续读卡压力测试默认50次间隔0.1s结果写入csv import time, csv, re from smartcard.System import readers READER_NAME None LOOP_COUNT 50 INTERVAL 0.1 OUTPUT z90_stress_test_result.csv # 卡号规则校验这里假设卡号为16位十进制 CARDNO_REGEX r^\d{16}$ def get_reader(): rlist readers() if not rlist: raise RuntimeError(无PC/SC读卡器) return rlist[0] def read_verify_once(reader): conn reader.createConnection() conn.connect() atr conn.getATR() # 读卡选择应用目录后读卡号文件示意 # 实际文件偏移以医保卡应用目录说明为准 data, sw1, sw2 conn.transmit([0x00, 0xB0, 0x00, 0x00, 0x20]) conn.disconnect() if len(data) 0: return None, atr cardno bytes(data).decode(gbk, errorsignore) return cardno, atr def main(): reader get_reader() with open(OUTPUT, w, newline, encodingutf-8) as f: w csv.writer(f) w.writerow([seq, time, cardno, atr, pass]) ok 0 for i in range(LOOP_COUNT): try: cardno, atr read_verify_once(reader) passed bool(cardno and re.match(CARDNO_REGEX, cardno.strip())) w.writerow([i, time.strftime(%Y-%m-%d %H:%M:%S), cardno, .join(f{b:02X} for b in atr), passed]) f.flush() if passed: ok 1 except Exception as e: w.writerow([i, time.strftime(%Y-%m-%d %H:%M:%S), str(e), , False]) time.sleep(INTERVAL) print(f通过率: {ok}/{LOOP_COUNT}) if __name__ __main__: main()这段代码把测试过程变成了可审计的记录文件。重点说明几个参数LOOP_COUNT 即压力循环次数50 次适合日常准入INTERVAL 是每次读卡之间的间隔设太短容易因待卡不稳定引入假失败设太长又会拖慢回归0.1 秒是我常用的平衡值CARDNO_REGEX 用于判断读出来的数据是否符合卡号格式防止 APDU 错误时把乱码当成功。CSV 会用 f.flush() 每次强制写盘这样在脚本中途断电或中断时已经跑过的记录不会丢。这个细节看似小实际做回归时很有用。脚本跑完看一眼 CSV 的 pass 列凡是 False 的行就是需要排查的样本。硬件是黑匣子脚本的作用就是把它从黑匣子变成可追踪的记录。5.3 把参数固化成“设备档案”而不是每次重测同一个型号的读卡器固件版本不同ATR 也可能有差异。所以我建议每次验证通过后把这个设备的 ATR 和串口号、驱动版本一起记录到设备档案里。常见做法是建一个简单的 JSON 或表格{ device_sn: Z90-2024-001, reader: Z90 USB Reader, serial_port: COM3, driver_version: 6.7.2, atr: 3B 8B 80 01 80 31 C0 00 00 00 00, stress_test: {loop: 50, pass: 50, fail_reason: } }这份档案的价值在于换驱动、更新固件之后可以做差分比照如果 ATR 变了而卡没有换说明读卡器固件或驱动影响了底层协议参数需要重新验证。如果不做记录几个月后出问题只能重跑测试浪费的是现场往返时间。5.4 把“单机测试”扩展成“批量部署基线”如果你负责的是几十上百个医保窗口的设备部署单台测试脚本还不够建议在回归脚本外套一层批量跑批把 COM 口号列表写到一个配置文件里脚本依次对每台读卡器做 30 次往返读卡自动输出一份“通过/失败/ATR 异常”的设备台账。这一步不改变上一章的任何读卡逻辑只是把第 5.2 的脚本用外层for port in config包起来。批量跑完后把失败设备剔出来做人工复测基本能挡掉绝大多数装机当天的隐性故障。6. 收到测试包后先做的“读卡结果三验证”再决定上线6.1 ATR 基线比对回到最开始那个问题一个 rar 包能带我们走多远答案是它能证明物理链路通但业务能用不能用的判断得靠自己再做三件小事。第一件是 ATR 基线比对。每次新设备到位后用包里自带的工具读一次 ATR 并保存。医保结算程序如果走 PC/SC底层会先做协议协商然后把 ATR 和卡片的历史字节交给上层解析。ATR 看起来是十六进制字符串其实是敏感的征兆——如果换一台读卡器后 ATR 变了先怀疑读卡器固件版本不同再怀疑驱动栈如果同一台设备反复上电 ATR 偶尔不一致说明接触问题比卡号读不出来更隐蔽。6.2 卡号与业务字段的核对第二件是卡号与业务字段的核对。测试工具读出来的“卡号”字段大概率是卡的内部编号而不是医保结算系统里展示的个人编号。真正的个人编号需要选择医保应用目录、读取对应 EF 文件并做编码转换之后得到。所以现场用测试包确认能读到卡后别急着给业务方回复“完成”拿一张已知个人编号的测试卡比对工具读到的卡号与实际个人编号的映射关系只有这个映射落在你的程序里准确无误才算是业务上可用的验证。6.3 新设备先做 50 次热插拔再入库第三件也是最值得养成的习惯新读卡器先做 50 次热插拔再入库。我个人的习惯是新到货的 Z90 不做一次性测试而是放在柜台上反复插拔 50 次模拟真实结账动作每次都看 ATR 是否稳定、读卡是否超时。这个习惯帮我挡掉了很多“装上去一周才暴露”的返修。插拔测试属于体力活但每个做过医保终端维护的人都会告诉你触点磨损是慢变量一天能暴露的问题不多一个月就能看出现差别。一套读卡测试 rar 包能给到你的是让新手快速通过“能不能读卡”这一关而能不能在结算高峰期稳定工作靠的是你把它当成一种需要持续验证的基础设施来对待。把 ATR 基线、卡号映射、50 次插拔这三项固化进部署流程后Z90 这类设备就真的成为“插上就能用坏了能定位”的可控组件而不是玄学问题。希望这份经验帮到你少走几趟现场。本文还有配套的精品资源点击获取
返回列表