From 1bfa9fefe02f1167e0b88bf9815febdf46bc1547 Mon Sep 17 00:00:00 2001 From: yuchenglong Date: Fri, 21 Nov 2025 17:59:46 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BA=AB=E4=BB=BD=E8=AF=86=E5=88=AB=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/idcard-worker.js | 102 +++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/electron/idcard-worker.js b/electron/idcard-worker.js index 5eea793..e91ed9f 100644 --- a/electron/idcard-worker.js +++ b/electron/idcard-worker.js @@ -3,56 +3,33 @@ const koffi = require("koffi"); const path = require("path"); const iconv = require("iconv-lite"); -// 定义结构体 +// 定义结构体 (使用原始字节数组,避免对齐问题,手动解析) const IDCardData = koffi.struct("IDCardData", { - Name: koffi.array("uint8", 32), - Sex: koffi.array("uint8", 6), - Nation: koffi.array("uint8", 20), - Born: koffi.array("uint8", 18), - Address: koffi.array("uint8", 72), - IDCardNo: koffi.array("uint8", 38), - GrantDept: koffi.array("uint8", 32), - UserLifeBegin: koffi.array("uint8", 18), - UserLifeEnd: koffi.array("uint8", 18), - reserved: koffi.array("uint8", 38), - PhotoFileName: koffi.array("uint8", 255), + raw: koffi.array("uint8", 1024), // 分配足够大的空间 }); let lib = null; +let apiCache = null; let running = false; -// 解码字符串 (尝试 UCS-2/UTF-16LE) +// 解码字符串 (GBK) function decodeString(buffer) { - // 这里的 buffer 是 uint8 数组,先转为 Buffer - const buf = Buffer.from(buffer); + // 这里的 buffer 是 uint8 数组 或 Buffer + const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer); - // 很多读卡器 DLL 返回的是 UCS-2 (UTF-16LE) - // 也有可能是 GBK。我们可以通过检查字节特征来判断,或者优先尝试 UCS-2 - // UCS-2 的特征是每两个字节一个字符,对于 ASCII 字符,高字节通常是 0x00 - - // 尝试作为 UTF-16LE 解码 - // 找到双 null 结尾 (0x00 0x00) + // 找到第一个 null 字节 (0x00) 的位置 let nullIndex = -1; - for (let i = 0; i < buf.length - 1; i += 2) { - if (buf[i] === 0 && buf[i + 1] === 0) { + for (let i = 0; i < buf.length; i++) { + if (buf[i] === 0) { nullIndex = i; break; } } - const validBuf16 = nullIndex !== -1 ? buf.slice(0, nullIndex) : buf; - const str16 = iconv.decode(validBuf16, "utf16le").trim(); - - // 如果解码出来的字符串看起来是乱码(包含很多 )或者为空但 buffer 不为空,可能需要尝试 GBK - // 但通常 Syn_IDCardRead.dll 返回的是 UCS-2 - // 让我们先打印一下原始 hex 以便调试 - // parentPort.postMessage({ type: 'log', payload: `Hex: ${buf.slice(0, 10).toString('hex')}` }); - - return str16; -} - -// 加载 DLL + const validBuf = nullIndex !== -1 ? buf.slice(0, nullIndex) : buf; + return iconv.decode(validBuf, "gbk").trim(); +} // 加载 DLL function loadDll() { - if (lib) return true; + if (apiCache) return apiCache; try { const fs = require("fs"); const dllNames = ["Syn_IDCardRead.dll", "SynIDCardRead.dll"]; @@ -105,7 +82,7 @@ function loadDll() { }; try { - return { + apiCache = { Syn_OpenPort: loadFunc("Syn_OpenPort", "_Syn_OpenPort@4", "int", [ "int", ]), @@ -130,10 +107,8 @@ function loadDll() { koffi.out(koffi.pointer(IDCardData)), ]), }; + return apiCache; } catch (e) { - // 如果 lib.stdcall 也不行,尝试回退到 func 但不带 __stdcall (可能不是 stdcall 或者 koffi 版本差异) - // 但根据之前的错误,不带 __stdcall 找不到函数,带了又报类型错误,说明很可能是 stdcall 但字符串解析有问题 - // 这里我们坚持用 lib.stdcall 这种显式 API 调用,它比字符串解析更稳定 throw e; } } catch (err) { @@ -200,24 +175,49 @@ async function startListen() { const data = {}; // koffi 输出对象 const ret = api.Syn_ReadMsg(port, 0, data); - if (ret === 0 && data) { + if (ret === 0 && data && data.raw) { + const rawBuf = Buffer.from(data.raw); + + // 调试:打印前 64 字节 Hex + // parentPort.postMessage({ + // type: "log", + // payload: `Raw Hex (First 64): ${rawBuf.slice(0, 64).toString("hex")}`, + // }); + + // 手动切片解析 (修正后的偏移) + // Name: 0-32 (32) + // Sex: 32-36 (4) + // Nation: 36-42 (6) + // Born: 42-60 (18) + // Address: 60-132 (72) + // IDCardNo: 132-170 (38) + // GrantDept: 170-202 (32) + // UserLifeBegin: 202-220 (18) + // UserLifeEnd: 220-238 (18) + // PhotoFileName: 276-531 (255) + const payload = { - name: decodeString(data.Name), - sex: decodeString(data.Sex), - nation: decodeString(data.Nation), - born: decodeString(data.Born), - address: decodeString(data.Address), - id_card_no: decodeString(data.IDCardNo), - grant_dept: decodeString(data.GrantDept), - life_begin: decodeString(data.UserLifeBegin), - life_end: decodeString(data.UserLifeEnd), - photo_path: decodeString(data.PhotoFileName), + name: decodeString(rawBuf.slice(0, 32)), + sex: decodeString(rawBuf.slice(32, 36)), + nation: decodeString(rawBuf.slice(36, 42)), + born: decodeString(rawBuf.slice(42, 60)), + address: decodeString(rawBuf.slice(60, 132)), + id_card_no: decodeString(rawBuf.slice(132, 170)), + grant_dept: decodeString(rawBuf.slice(170, 202)), + life_begin: decodeString(rawBuf.slice(202, 220)), + life_end: decodeString(rawBuf.slice(220, 238)), + photo_path: decodeString(rawBuf.slice(276, 531)), }; parentPort.postMessage({ type: "log", payload: `Read IDCard success: ${payload.name} ${payload.id_card_no}`, }); + // 打印完整 payload 以便调试 + parentPort.postMessage({ + type: "log", + payload: `Full Payload: ${JSON.stringify(payload)}`, + }); parentPort.postMessage({ type: "data", payload }); // 读到卡后暂停一下,避免重复读取太快