const { parentPort } = require("worker_threads"); const koffi = require("koffi"); const path = require("path"); const iconv = require("iconv-lite"); // 定义结构体 const IDCardData = koffi.struct("IDCardData", { Name: koffi.array("char", 32), Sex: koffi.array("char", 6), Nation: koffi.array("char", 20), Born: koffi.array("char", 18), Address: koffi.array("char", 72), IDCardNo: koffi.array("char", 38), GrantDept: koffi.array("char", 32), UserLifeBegin: koffi.array("char", 18), UserLifeEnd: koffi.array("char", 18), reserved: koffi.array("char", 38), PhotoFileName: koffi.array("char", 255), }); let lib = null; let running = false; // 解码 GBK 字符串 function decodeGBK(buffer) { // 找到第一个 null 字节 (0x00) 的位置 let nullIndex = -1; for (let i = 0; i < buffer.length; i++) { if (buffer[i] === 0) { nullIndex = i; break; } } // 如果找到了 null,截取前面的部分;否则使用整个 buffer const validBuffer = nullIndex !== -1 ? buffer.slice(0, nullIndex) : buffer; return iconv.decode(validBuffer, "gbk").trim(); } // 加载 DLL function loadDll() { if (lib) return true; try { const fs = require("fs"); const dllNames = ["Syn_IDCardRead.dll", "SynIDCardRead.dll"]; // 确定 DLL 目录:优先检查生产环境 resources/xzx,否则使用开发环境 resources/xzx let dllDir = path.join(process.cwd(), "resources", "xzx"); if (process.resourcesPath) { const prodDir = path.join(process.resourcesPath, "xzx"); if (fs.existsSync(prodDir)) { dllDir = prodDir; } } let dllPath = null; for (const name of dllNames) { const p = path.join(dllDir, name); if (fs.existsSync(p)) { dllPath = p; break; } } if (!dllPath) { throw new Error(`DLL not found in ${dllDir}`); } parentPort.postMessage({ type: "log", payload: `Loading DLL from: ${dllPath}`, }); lib = koffi.load(dllPath); // 尝试使用 stdcall 约定,这对于 Windows 32位 DLL 很常见 // koffi 2.x 版本中,stdcall 约定需要在函数名前加 __stdcall // 如果字符串解析失败,可以尝试使用 lib.stdcall() 方法 // 辅助函数:尝试加载函数,支持原名和修饰名 const loadFunc = (name, alias, ret, args) => { try { return lib.stdcall(name, ret, args); } catch (e) { try { return lib.stdcall(alias, ret, args); } catch (e2) { throw new Error( `Cannot find function '${name}' or '${alias}' in DLL` ); } } }; try { return { Syn_OpenPort: loadFunc("Syn_OpenPort", "_Syn_OpenPort@4", "int", [ "int", ]), Syn_ClosePort: loadFunc("Syn_ClosePort", "_Syn_ClosePort@4", "int", [ "int", ]), Syn_StartFindIDCard: loadFunc( "Syn_StartFindIDCard", "_Syn_StartFindIDCard@12", "int", ["int", "uint8*", "int"] ), Syn_SelectIDCard: loadFunc( "Syn_SelectIDCard", "_Syn_SelectIDCard@12", "int", ["int", "uint8*", "int"] ), Syn_ReadMsg: loadFunc("Syn_ReadMsg", "_Syn_ReadMsg@12", "int", [ "int", "int", koffi.out(koffi.pointer(IDCardData)), ]), }; } catch (e) { // 如果 lib.stdcall 也不行,尝试回退到 func 但不带 __stdcall (可能不是 stdcall 或者 koffi 版本差异) // 但根据之前的错误,不带 __stdcall 找不到函数,带了又报类型错误,说明很可能是 stdcall 但字符串解析有问题 // 这里我们坚持用 lib.stdcall 这种显式 API 调用,它比字符串解析更稳定 throw e; } } catch (err) { parentPort.postMessage({ type: "error", payload: `Failed to load DLL: ${err.message}`, }); return null; } } async function startListen() { if (running) return; running = true; const api = loadDll(); if (!api) { running = false; return; } try { // 尝试打开端口,优先尝试 1001 (USB) let port = 1001; let openRes = api.Syn_OpenPort(port); if (openRes !== 0) { // 如果 1001 失败,尝试 1001-1016 for (let p = 1002; p <= 1016; p++) { if (api.Syn_OpenPort(p) === 0) { port = p; openRes = 0; break; } } } if (openRes !== 0) { parentPort.postMessage({ type: "error", payload: `Open port failed (tried 1001-1016)`, }); running = false; return; } parentPort.postMessage({ type: "log", payload: `Port ${port} opened successfully`, }); const iin = Buffer.alloc(4); const sn = Buffer.alloc(8); // IDCardData 结构体大小计算: 32+6+20+18+72+38+32+18+18+38+255 = 547 字节 // koffi 会自动处理结构体内存分配 while (running) { // 寻卡 api.Syn_StartFindIDCard(port, iin, 0); // 选卡 api.Syn_SelectIDCard(port, sn, 0); // 读卡 const data = {}; // koffi 输出对象 const ret = api.Syn_ReadMsg(port, 0, data); if (ret === 0 && data) { const payload = { name: decodeGBK(Buffer.from(data.Name)), sex: decodeGBK(Buffer.from(data.Sex)), nation: decodeGBK(Buffer.from(data.Nation)), born: decodeGBK(Buffer.from(data.Born)), address: decodeGBK(Buffer.from(data.Address)), id_card_no: decodeGBK(Buffer.from(data.IDCardNo)), grant_dept: decodeGBK(Buffer.from(data.GrantDept)), life_begin: decodeGBK(Buffer.from(data.UserLifeBegin)), life_end: decodeGBK(Buffer.from(data.UserLifeEnd)), photo_path: decodeGBK(Buffer.from(data.PhotoFileName)), }; parentPort.postMessage({ type: "log", payload: `Read IDCard success: ${payload.name} ${payload.id_card_no}`, }); parentPort.postMessage({ type: "data", payload }); // 读到卡后暂停一下,避免重复读取太快 await new Promise((r) => setTimeout(r, 500)); } else { // 没读到卡,稍微等待 await new Promise((r) => setTimeout(r, 150)); } } api.Syn_ClosePort(port); } catch (err) { parentPort.postMessage({ type: "error", payload: `Worker error: ${err.message}`, }); running = false; } } parentPort.on("message", (msg) => { if (msg === "start") { startListen(); } else if (msg === "stop") { running = false; } });