feat: 读卡器程序调整

This commit is contained in:
huxuejian 2026-10-22 15:33:18 +08:00
parent d36cd31f84
commit 4a3a75d417
6 changed files with 37 additions and 27 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.12) cmake_minimum_required(VERSION 3.12)
project(readcard_c LANGUAGES C CXX) project(haining_card_reader LANGUAGES C CXX)
# Build with: cmake -A Win32 -T v141 .. # Build with: cmake -A Win32 -T v141 ..
# This project targets 32-bit Windows 7 SP1 and later, subject to the vendor # This project targets 32-bit Windows 7 SP1 and later, subject to the vendor
@ -7,12 +7,12 @@ project(readcard_c LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(readcard-c WIN32 src/main.cpp src/qrcodegen.c) add_executable(HainingCardReader WIN32 src/main.cpp src/qrcodegen.c)
target_compile_definitions(readcard-c PRIVATE UNICODE _UNICODE WIN32_LEAN_AND_MEAN) target_compile_definitions(HainingCardReader PRIVATE UNICODE _UNICODE WIN32_LEAN_AND_MEAN)
target_link_libraries(readcard-c PRIVATE ws2_32 comctl32) target_link_libraries(HainingCardReader PRIVATE ws2_32 comctl32)
if(MSVC) if(MSVC)
target_compile_options(readcard-c PRIVATE /W4 /permissive- /utf-8 /MT) target_compile_options(HainingCardReader PRIVATE /W4 /permissive- /utf-8 /MT)
# target_link_options was added after the CMake bundled with VS2017. # target_link_options was added after the CMake bundled with VS2017.
set_property(TARGET readcard-c APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS,6.01") set_property(TARGET HainingCardReader APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS,6.01")
endif() endif()

View File

@ -4,10 +4,10 @@
## Windows 兼容性 ## Windows 兼容性
项目使用 Win32 API 和静态链接 MSVC 运行时。建议用 Visual Studio 2017 的 `v141_xp` 工具集构建,目标为 Windows XP SP3 及以上;实际下限还取决于读卡器、扫码墩驱动及医保厂商 DLL 的支持范围。 项目使用 Win32 API 和静态链接 MSVC 运行时。建议用 Visual Studio 2017 的 `v141` 工具集构建,目标为 Windows 7 SP1 及以上;实际下限还取决于读卡器、扫码墩驱动及医保厂商 DLL 的支持范围。
```powershell ```powershell
cmake -S . -B build -A Win32 -T v141_xp cmake -S . -B build -A Win32 -T v141
cmake --build build --config Release cmake --build build --config Release
``` ```
@ -16,8 +16,7 @@ cmake --build build --config Release
将以下内容一起发布: 将以下内容一起发布:
```text ```text
readcard-c.exe HainingCardReader.exe
config.json
package/DWCardReaderDLL/ package/DWCardReaderDLL/
package/EMICDLL/ package/EMICDLL/
``` ```
@ -30,4 +29,6 @@ package/EMICDLL/
## 逐步骤日志 ## 逐步骤日志
`operation_trace_enabled` 控制流程日志,开发期默认为 `true`。`operation_trace_include_sensitive` 控制是否记录姓名、身份证号、卡号、医保码和 `ecToken`,开发期默认为 `true`,正式环境应设为 `false`。日志写入 `logs/operation-YYYYMMDD.log`。 医保、读卡和日志默认配置已编译进 EXE,发布目录不需要 `config.json`。如需为个别部署环境覆盖默认值,可以在 EXE 同级目录手动放置 `config.json`,程序会优先读取它。
`operation_trace_enabled` 控制流程日志,默认 `true`。`operation_trace_include_sensitive` 控制是否记录姓名、身份证号、卡号、医保码和 `ecToken`,默认 `false`;关闭后日志保留流程步骤,但结果内容显示为 `[hidden]`。仅在临时排障并取得相应授权时,才可通过 EXE 同目录的外部 `config.json` 显式设为 `true`。日志写入 `logs/operation-YYYYMMDD.log`。

View File

@ -3,7 +3,9 @@
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$Root = $PSScriptRoot $Root = $PSScriptRoot
$Build = Join-Path -Path $Root -ChildPath "build" $Build = Join-Path -Path $Root -ChildPath "build"
$Dist = Join-Path -Path $Root -ChildPath "dist" $PackageName = "海宁读卡助手_1.0.0"
$DistRoot = Join-Path -Path $Root -ChildPath "dist"
$Dist = Join-Path -Path $DistRoot -ChildPath $PackageName
$MSBuild = "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild.exe" $MSBuild = "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild.exe"
$VCVars = "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" $VCVars = "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat"
@ -60,16 +62,21 @@ try {
New-Item -ItemType Directory -Force -Path $Dist | Out-Null New-Item -ItemType Directory -Force -Path $Dist | Out-Null
$ConfigurationDir = Join-Path -Path $Build -ChildPath $Configuration $ConfigurationDir = Join-Path -Path $Build -ChildPath $Configuration
$Binary = Join-Path -Path $ConfigurationDir -ChildPath "readcard-c.exe" $Binary = Join-Path -Path $ConfigurationDir -ChildPath "HainingCardReader.exe"
if (-not (Test-Path -LiteralPath $Binary)) { if (-not (Test-Path -LiteralPath $Binary)) {
throw "Build output was not found: $Binary" throw "Build output was not found: $Binary"
} }
$ExeDestination = Join-Path -Path $Dist -ChildPath "readcard-c.exe" $ExeDestination = Join-Path -Path $Dist -ChildPath "HainingCardReader.exe"
$ConfigSource = Join-Path -Path $Root -ChildPath "config.json"
$ConfigDestination = Join-Path -Path $Dist -ChildPath "config.json" $ConfigDestination = Join-Path -Path $Dist -ChildPath "config.json"
$PackageSource = Join-Path -Path $Root -ChildPath "package" $PackageSource = Join-Path -Path $Root -ChildPath "package"
$PackageDestination = Join-Path -Path $Dist -ChildPath "package" $PackageDestination = Join-Path -Path $Dist -ChildPath "package"
Copy-Item -LiteralPath $Binary -Destination $ExeDestination -Force try {
Copy-Item -LiteralPath $ConfigSource -Destination $ConfigDestination -Force Copy-Item -LiteralPath $Binary -Destination $ExeDestination -Force
} catch {
throw "Cannot replace $ExeDestination. Close the running HainingCardReader.exe, then run 一键打包.bat again."
}
# Runtime defaults are compiled into readcard-c.exe. Remove a stale config
# from older packages so users do not accidentally override the embedded values.
if (Test-Path -LiteralPath $ConfigDestination) { Remove-Item -LiteralPath $ConfigDestination -Force }
Copy-Item -LiteralPath $PackageSource -Destination $PackageDestination -Recurse -Force Copy-Item -LiteralPath $PackageSource -Destination $PackageDestination -Recurse -Force
Write-Host "Build completed: $Dist" Write-Host "Build completed: $Dist"

View File

@ -12,6 +12,6 @@
"emic_office_id": "D99", "emic_office_id": "D99",
"emic_office_name": "其他科室", "emic_office_name": "其他科室",
"operation_trace_enabled": true, "operation_trace_enabled": true,
"operation_trace_include_sensitive": true, "operation_trace_include_sensitive": false,
"log_dir": "logs" "log_dir": "logs"
} }

View File

@ -18,8 +18,8 @@
// with the v141_xp toolset. Both vendor DLLs are x86 stdcall libraries. // with the v141_xp toolset. Both vendor DLLs are x86 stdcall libraries.
struct Config { struct Config {
std::string host="127.0.0.1", cardDir="package/DWCardReaderDLL", readerType="ZJ_HZ_310000"; std::string host="127.0.0.1", cardDir="package/DWCardReaderDLL", readerType="ZJ_HZ_310000";
std::string emicDll="package/EMICDLL/NationECCode.dll", emicUrl, orgId, businessType="01101", operatorId, operatorName, officeId, officeName, logDir="logs"; std::string emicDll="package/EMICDLL/NationECCode.dll", emicUrl="http://10.249.81.107/localcfc/api/hsecfc/localQrCodeQuery", orgId="H33048100004", businessType="01101", operatorId="admin", operatorName="administrator", officeId="D99", officeName=u8"其他科室", logDir="logs";
int port=17880; bool trace=true, traceSensitive=true; int port=17880; bool trace=true, traceSensitive=false;
}; };
static std::wstring root; static Config cfg; static std::mutex vendorLock; static std::atomic<unsigned long> sequence(0); static std::wstring root; static Config cfg; static std::mutex vendorLock; static std::atomic<unsigned long> sequence(0);
@ -32,13 +32,13 @@ static std::string Get(const std::string& j,const char* key,const std::string& d
static bool GetBool(const std::string& j,const char* key,bool d) { std::smatch m; std::regex x(std::string("\\\"")+key+"\\\"\\s*:\\s*(true|false)"); return std::regex_search(j,m,x)?m[1]=="true":d; } static bool GetBool(const std::string& j,const char* key,bool d) { std::smatch m; std::regex x(std::string("\\\"")+key+"\\\"\\s*:\\s*(true|false)"); return std::regex_search(j,m,x)?m[1]=="true":d; }
static int GetInt(const std::string& j,const char* key,int d) { std::smatch m; std::regex x(std::string("\\\"")+key+"\\\"\\s*:\\s*(\\d+)"); return std::regex_search(j,m,x)?atoi(m[1].str().c_str()):d; } static int GetInt(const std::string& j,const char* key,int d) { std::smatch m; std::regex x(std::string("\\\"")+key+"\\\"\\s*:\\s*(\\d+)"); return std::regex_search(j,m,x)?atoi(m[1].str().c_str()):d; }
static void Trace(const char* step,const std::string& result="") { if(!cfg.trace)return; std::wstring dir=Join(root,cfg.logDir); CreateDirectoryW(dir.c_str(),0); SYSTEMTIME t;GetLocalTime(&t); wchar_t n[64];wsprintfW(n,L"\\operation-%04d%02d%02d.log",t.wYear,t.wMonth,t.wDay); std::ofstream f((dir+n).c_str(),std::ios::app|std::ios::binary); if(!f)return; f<<t.wYear<<"-"<<t.wMonth<<"-"<<t.wDay<<" "<<t.wHour<<":"<<t.wMinute<<":"<<t.wSecond<<" ["<<step<<"] "<<(cfg.traceSensitive?result:"[hidden]")<<"\r\n"; } static void Trace(const char* step,const std::string& result="") { if(!cfg.trace)return; std::wstring dir=Join(root,cfg.logDir); CreateDirectoryW(dir.c_str(),0); SYSTEMTIME t;GetLocalTime(&t); wchar_t n[64];wsprintfW(n,L"\\operation-%04d%02d%02d.log",t.wYear,t.wMonth,t.wDay); std::ofstream f((dir+n).c_str(),std::ios::app|std::ios::binary); if(!f)return; f<<t.wYear<<"-"<<t.wMonth<<"-"<<t.wDay<<" "<<t.wHour<<":"<<t.wMinute<<":"<<t.wSecond<<" ["<<step<<"] "<<(cfg.traceSensitive?result:"[hidden]")<<"\r\n"; }
static void LoadConfig() { std::string j=ReadFileUtf8(root+L"\\config.json"); cfg.host=Get(j,"host",cfg.host);cfg.port=GetInt(j,"port",cfg.port);cfg.cardDir=Get(j,"card_dll_dir",cfg.cardDir);cfg.readerType=Get(j,"gui_reader_type",cfg.readerType);cfg.emicDll=Get(j,"emic_dll",cfg.emicDll);cfg.emicUrl=Get(j,"emic_url");cfg.orgId=Get(j,"emic_org_id");cfg.businessType=Get(j,"emic_business_type",cfg.businessType);cfg.operatorId=Get(j,"emic_operator_id");cfg.operatorName=Get(j,"emic_operator_name");cfg.officeId=Get(j,"emic_office_id");cfg.officeName=Get(j,"emic_office_name");cfg.logDir=Get(j,"log_dir",cfg.logDir);cfg.trace=GetBool(j,"operation_trace_enabled",true);cfg.traceSensitive=GetBool(j,"operation_trace_include_sensitive",true); } static void LoadConfig() { std::string j=ReadFileUtf8(root+L"\\config.json"); cfg.host=Get(j,"host",cfg.host);cfg.port=GetInt(j,"port",cfg.port);cfg.cardDir=Get(j,"card_dll_dir",cfg.cardDir);cfg.readerType=Get(j,"gui_reader_type",cfg.readerType);cfg.emicDll=Get(j,"emic_dll",cfg.emicDll);cfg.emicUrl=Get(j,"emic_url",cfg.emicUrl);cfg.orgId=Get(j,"emic_org_id",cfg.orgId);cfg.businessType=Get(j,"emic_business_type",cfg.businessType);cfg.operatorId=Get(j,"emic_operator_id",cfg.operatorId);cfg.operatorName=Get(j,"emic_operator_name",cfg.operatorName);cfg.officeId=Get(j,"emic_office_id",cfg.officeId);cfg.officeName=Get(j,"emic_office_name",cfg.officeName);cfg.logDir=Get(j,"log_dir",cfg.logDir);cfg.trace=GetBool(j,"operation_trace_enabled",cfg.trace);cfg.traceSensitive=GetBool(j,"operation_trace_include_sensitive",cfg.traceSensitive);Trace("config.load",j.empty()?"using embedded configuration":"using external config.json override"); }
typedef LONG (__stdcall *CardReadFn)(char*,char*); typedef char* (__stdcall *HmacFn)(char*,char*,char*,char*,char*); typedef char* (__stdcall *EmicFn)(char*,char*,char*); typedef LONG (__stdcall *CardReadFn)(char*,char*); typedef char* (__stdcall *HmacFn)(char*,char*,char*,char*,char*); typedef char* (__stdcall *EmicFn)(char*,char*,char*);
static HMODULE cardDll=0, emicDll=0; static CardReadFn readPin=0,readNoPin=0; static HmacFn hmac=0; static EmicFn emic=0; static HMODULE cardDll=0, emicDll=0; static CardReadFn readPin=0,readNoPin=0; static HmacFn hmac=0; static EmicFn emic=0;
static bool LoadDlls(std::string& e) { Trace("dll.load.start"); cardDll=LoadLibraryW((Join(root,cfg.cardDir)+L"\\CardReaderDLL.dll").c_str()); if(!cardDll){e="CardReaderDLL.dll load failed";Trace("dll.card.failure",e);return false;} readPin=(CardReadFn)GetProcAddress(cardDll,"ZJ_ReadCardInfo");readNoPin=(CardReadFn)GetProcAddress(cardDll,"ZJ_ReadCardInfo_NoPin");hmac=(HmacFn)GetProcAddress(cardDll,"ZJ_Hmac_SM3"); emicDll=LoadLibraryW(Join(root,cfg.emicDll).c_str()); if(!emicDll){e="NationECCode.dll load failed";Trace("dll.emic.failure",e);return false;} emic=(EmicFn)GetProcAddress(emicDll,"NationEcTrans"); if(!readPin||!readNoPin||!hmac||!emic){e="vendor DLL export missing";Trace("dll.export.failure",e);return false;} Trace("dll.load.success");return true; } static bool LoadDlls(std::string& e) { Trace("dll.load.start"); cardDll=LoadLibraryW((Join(root,cfg.cardDir)+L"\\CardReaderDLL.dll").c_str()); if(!cardDll){e="CardReaderDLL.dll load failed";Trace("dll.card.failure",e);return false;} readPin=(CardReadFn)GetProcAddress(cardDll,"ZJ_ReadCardInfo");readNoPin=(CardReadFn)GetProcAddress(cardDll,"ZJ_ReadCardInfo_NoPin");hmac=(HmacFn)GetProcAddress(cardDll,"ZJ_Hmac_SM3"); emicDll=LoadLibraryW(Join(root,cfg.emicDll).c_str()); if(!emicDll){e="NationECCode.dll load failed";Trace("dll.emic.failure",e);return false;} emic=(EmicFn)GetProcAddress(emicDll,"NationEcTrans"); if(!readPin||!readNoPin||!hmac||!emic){e="vendor DLL export missing";Trace("dll.export.failure",e);return false;} Trace("dll.load.success");return true; }
static std::string Card(const std::string& type,bool noPin,std::string& e) { Trace("card.read.start",std::string("readerType=")+type+", method="+(noPin?"ZJ_ReadCardInfo_NoPin":"ZJ_ReadCardInfo")); std::lock_guard<std::mutex>l(vendorLock); char out[4096]={}; LONG r=(noPin?readNoPin:readPin)((char*)type.c_str(),out); std::string raw=A(W(std::string(out),936)); Trace("card.read.dll.return",std::string("returnCode=")+std::to_string((long long)r)+", output="+raw); if(r!=0){e="读社保卡失败(读卡 DLL 返回码 " + std::to_string((long long)r) + ")";if(!raw.empty()&&raw!=std::to_string((long long)r))e+=":"+raw;Trace("card.read.failure",e);return "";}Trace("card.read.success",raw);return raw; } static std::string Card(const std::string& type,bool noPin,std::string& e) { Trace("card.read.start",std::string("readerType=")+type+", method="+(noPin?"ZJ_ReadCardInfo_NoPin":"ZJ_ReadCardInfo")); std::lock_guard<std::mutex>l(vendorLock); char out[4096]={}; LONG r=(noPin?readNoPin:readPin)((char*)type.c_str(),out); std::string raw=A(W(std::string(out),936)); Trace("card.read.dll.return",std::string("returnCode=")+std::to_string((long long)r)+", output="+raw); if(r!=0){e="读社保卡失败(读卡 DLL 返回码 " + std::to_string((long long)r) + ")";if(!raw.empty()&&raw!=std::to_string((long long)r))e+=":"+raw;Trace("card.read.failure",e);return "";}Trace("card.read.success",raw);return raw; }
static std::string Emic(const std::string& scanned,std::string& e) { Trace("emic.start",scanned); if(cfg.emicUrl.empty()||cfg.orgId.empty()||cfg.operatorId.empty()||cfg.officeId.empty()){e="EMIC configuration incomplete";Trace("emic.config.failure",e);return "";} std::string trans=scanned.empty()?"cn.nhsa.qrcode.get":"ec.query"; char serial[32]; SYSTEMTIME t;GetLocalTime(&t); wsprintfA(serial,"%04d%02d%02d%02d%02d%02d%04lu",t.wYear,t.wMonth,t.wDay,t.wHour,t.wMinute,t.wSecond,sequence.fetch_add(1)%10000); std::ostringstream q;q<<"{\"orgId\":\""<<J(cfg.orgId)<<"\",\"transType\":\""<<trans<<"\",\"data\":{\"orgId\":\""<<J(cfg.orgId)<<"\",\"outBizNo\":\""<<serial<<"\",\"businessType\":\""<<J(cfg.businessType)<<"\",\"operatorId\":\""<<J(cfg.operatorId)<<"\",\"operatorName\":\""<<J(cfg.operatorName)<<"\",\"officeId\":\""<<J(cfg.officeId)<<"\",\"officeName\":\""<<J(cfg.officeName)<<"\"}"; if(!scanned.empty())q<<",\"extra\":{\"ecQrCode\":\""<<J(scanned)<<"\"}";q<<"}"; Trace("emic.request.ready",std::string("transType=")+trans+", outBizNo="+serial); std::string in=A(W(q.str()),54936);std::vector<char> ib(in.begin(),in.end());ib.push_back(0);char out[16384]={};std::lock_guard<std::mutex>l(vendorLock);Trace("emic.dll.call.start",cfg.emicUrl);std::shared_ptr<std::atomic<bool> > completed(new std::atomic<bool>(false));std::thread([completed](){Sleep(10000);if(!completed->load())Trace("emic.dll.call.waiting","NationEcTrans has not returned after 10 seconds; check intranet connectivity or vendor DLL dialog");}).detach();char* r=emic((char*)cfg.emicUrl.c_str(),ib.data(),out);completed->store(true);if(!r||std::string(r)!="0000"){e=r?r:"NationEcTrans failed";Trace("emic.dll.failure",e);return "";} std::string raw=A(W(std::string(out),54936)); if(GetInt(raw,"code",-1)!=0){e=Get(raw,"message","EMIC failed");Trace("emic.platform.failure",e);return "";}Trace("emic.success",raw);return raw; } static std::string Emic(const std::string& scanned,std::string& e) { Trace("emic.start",scanned); if(cfg.emicUrl.empty()||cfg.orgId.empty()||cfg.operatorId.empty()||cfg.officeId.empty()){e="EMIC configuration incomplete";Trace("emic.config.failure",e);return "";} const std::string trans="ec.query"; std::ostringstream q;q<<"{\"orgId\":\""<<J(cfg.orgId)<<"\",\"transType\":\""<<trans<<"\",\"data\":{\"orgId\":\""<<J(cfg.orgId)<<"\",\"businessType\":\""<<J(cfg.businessType)<<"\",\"operatorId\":\""<<J(cfg.operatorId)<<"\",\"operatorName\":\""<<J(cfg.operatorName)<<"\",\"officeId\":\""<<J(cfg.officeId)<<"\",\"officeName\":\""<<J(cfg.officeName)<<"\"}}"; Trace("emic.request.ready",std::string("transType=")+trans+", mode=DLL credential dialog"); std::string in=A(W(q.str()),54936);std::vector<char> ib(in.begin(),in.end());ib.push_back(0);char out[16384]={};std::lock_guard<std::mutex>l(vendorLock);Trace("emic.dll.call.start",cfg.emicUrl);std::shared_ptr<std::atomic<bool> > completed(new std::atomic<bool>(false));std::thread([completed](){Sleep(10000);if(!completed->load())Trace("emic.dll.call.waiting","NationEcTrans has not returned after 10 seconds; check intranet connectivity or vendor DLL dialog");}).detach();char* r=emic((char*)cfg.emicUrl.c_str(),ib.data(),out);completed->store(true);if(!r||std::string(r)!="0000"){e=r?r:"NationEcTrans failed";Trace("emic.dll.failure",e);return "";} std::string raw=A(W(std::string(out),54936)); if(GetInt(raw,"code",-1)!=0){e=Get(raw,"message","EMIC failed");Trace("emic.platform.failure",e);return "";}Trace("emic.success",raw);return raw; }
static HWND outBox,inputBox,scanButton,cardButton,qrView,titleLabel,subtitleLabel; static HWND outBox,inputBox,scanButton,cardButton,qrView,titleLabel,subtitleLabel;
static HFONT titleFont=0,bodyFont=0,buttonFont=0; static HFONT titleFont=0,bodyFont=0,buttonFont=0;
@ -46,12 +46,14 @@ static HBRUSH headerBrush=0,backgroundBrush=0;
static WNDPROC inputProc=0; static bool scannerArmed=false; static std::wstring scannerBuffer; static std::vector<uint8_t> qr; static WNDPROC inputProc=0; static bool scannerArmed=false; static std::wstring scannerBuffer; static std::vector<uint8_t> qr;
static std::string Trim(std::string s){while(!s.empty()&&isspace((unsigned char)s.front()))s.erase(s.begin());while(!s.empty()&&isspace((unsigned char)s.back()))s.pop_back();return s;} static std::string Trim(std::string s){while(!s.empty()&&isspace((unsigned char)s.front()))s.erase(s.begin());while(!s.empty()&&isspace((unsigned char)s.back()))s.pop_back();return s;}
static std::string EncodeURIComponent(const std::string& s){const char* hex="0123456789ABCDEF";std::string r;for(unsigned char c:s){if(isalnum(c)||strchr("-_.!~*'()",c))r+=c;else{r+='%';r+=hex[c>>4];r+=hex[c&15];}}return r;} static std::string EncodeURIComponent(const std::string& s){const char* hex="0123456789ABCDEF";std::string r;for(unsigned char c:s){if(isalnum(c)||strchr("-_.!~*'()",c))r+=c;else{r+='%';r+=hex[c>>4];r+=hex[c&15];}}return r;}
static bool ShowQr(const std::string& raw,std::string& e){std::string content=EncodeURIComponent(Get(raw,"Name"))+"|"+Get(raw,"IDNum")+"|"+Get(raw,"CardNum")+"|"+Get(raw,"CardIDCode");if(content=="|||"){e="读卡成功,但未获取到可生成二维码的卡片信息";Trace("card.qrcode.failure",e);return false;}uint8_t temp[qrcodegen_BUFFER_LEN_MAX],data[qrcodegen_BUFFER_LEN_MAX];if(!qrcodegen_encodeText(content.c_str(),temp,data,qrcodegen_Ecc_MEDIUM,qrcodegen_VERSION_MIN,qrcodegen_VERSION_MAX,qrcodegen_Mask_AUTO,true)){e="卡片信息过长,无法生成二维码";Trace("card.qrcode.failure",e);return false;}qr.assign(data,data+sizeof(data));InvalidateRect(qrView,0,TRUE);Trace("card.qrcode.success",content);return true;} static bool RenderQr(const std::string& content,const char* event,std::string& e){uint8_t temp[qrcodegen_BUFFER_LEN_MAX],data[qrcodegen_BUFFER_LEN_MAX];if(!qrcodegen_encodeText(content.c_str(),temp,data,qrcodegen_Ecc_MEDIUM,qrcodegen_VERSION_MIN,qrcodegen_VERSION_MAX,qrcodegen_Mask_AUTO,true)){e="二维码内容过长,无法生成二维码";Trace(event,e);return false;}qr.assign(data,data+sizeof(data));InvalidateRect(qrView,0,TRUE);Trace(event,content);return true;}
static bool ShowQr(const std::string& raw,std::string& e){std::string name=Get(raw,"Name"),id=Get(raw,"IDNum"),cardNo=Get(raw,"CardNum"),cardId=Get(raw,"CardIDCode");if(name.empty()||id.empty()||cardNo.empty()||cardId.empty()){e="读卡成功,但未获取到可生成二维码的卡片信息";Trace("card.qrcode.failure",e);return false;}return RenderQr("SC|"+EncodeURIComponent(name)+"|"+id+"|"+cardNo+"|"+cardId,"card.qrcode.success",e);}
static LRESULT CALLBACK QrProc(HWND h,UINT m,WPARAM w,LPARAM l){if(m==WM_PAINT){PAINTSTRUCT ps;HDC dc=BeginPaint(h,&ps);RECT r;GetClientRect(h,&r);FillRect(dc,&r,(HBRUSH)(COLOR_WINDOW+1));if(qr.empty()){DrawTextW(dc,L"请放置社保卡,然后点击读社保卡",-1,&r,DT_CENTER|DT_VCENTER|DT_SINGLELINE);}else{int s=qrcodegen_getSize(qr.data()),scale=(std::min)(r.right/(s+8),r.bottom/(s+8));int x=(r.right-s*scale)/2,y=(r.bottom-s*scale)/2;HBRUSH b=CreateSolidBrush(RGB(0,0,0));for(int yy=0;yy<s;yy++)for(int xx=0;xx<s;xx++)if(qrcodegen_getModule(qr.data(),xx,yy)){RECT cell={x+xx*scale,y+yy*scale,x+(xx+1)*scale,y+(yy+1)*scale};FillRect(dc,&cell,b);}DeleteObject(b);}EndPaint(h,&ps);return 0;}return DefWindowProcW(h,m,w,l);} static LRESULT CALLBACK QrProc(HWND h,UINT m,WPARAM w,LPARAM l){if(m==WM_PAINT){PAINTSTRUCT ps;HDC dc=BeginPaint(h,&ps);RECT r;GetClientRect(h,&r);FillRect(dc,&r,(HBRUSH)(COLOR_WINDOW+1));if(qr.empty()){DrawTextW(dc,L"请放置社保卡,然后点击读社保卡",-1,&r,DT_CENTER|DT_VCENTER|DT_SINGLELINE);}else{int s=qrcodegen_getSize(qr.data()),scale=(std::min)(r.right/(s+8),r.bottom/(s+8));int x=(r.right-s*scale)/2,y=(r.bottom-s*scale)/2;HBRUSH b=CreateSolidBrush(RGB(0,0,0));for(int yy=0;yy<s;yy++)for(int xx=0;xx<s;xx++)if(qrcodegen_getModule(qr.data(),xx,yy)){RECT cell={x+xx*scale,y+yy*scale,x+(xx+1)*scale,y+(yy+1)*scale};FillRect(dc,&cell,b);}DeleteObject(b);}EndPaint(h,&ps);return 0;}return DefWindowProcW(h,m,w,l);}
static void ShowEmic(const std::string& raw){std::string name=Get(raw,"userName"),id=Get(raw,"idNo"),token=Get(raw,"ecToken");std::string s="医保电子凭证回调结果:\r\n姓名:"+name+"\r\n证件号:"+id+"\r\necToken:"+token;SetWindowTextW(outBox,W(s).c_str());Trace("emic.gui.display",s);} static void ShowEmic(const std::string& raw){std::string name=Get(raw,"userName"),id=Get(raw,"idNo"),token=Get(raw,"ecToken"),e,s;if(name.empty()||id.empty()||token.empty()){s="医保电子凭证信息不完整,未生成二维码。";Trace("emic.qrcode.failure",s);}else if(RenderQr("EC|"+EncodeURIComponent(name)+"|"+id+"|"+token,"emic.qrcode.success",e)){s="医保电子凭证扫码成功,请扫描上方二维码";}else{s=e;}SetWindowTextW(outBox,W(s).c_str());Trace("emic.gui.display",s);}
static void ScanAsync(std::string code){scannerArmed=false;EnableWindow(scanButton,FALSE);Trace("scanner.input.complete",code);std::thread([code](){std::string e,raw=Emic(code,e);PostMessageW(GetParent(outBox),WM_APP+1,(WPARAM)new std::string(raw.empty()?"ERROR:"+e:raw),0);}).detach();} static void ScanAsync(std::string code){scannerArmed=false;EnableWindow(scanButton,FALSE);Trace("scanner.input.complete",code);std::thread([code](){std::string e,raw=Emic(code,e);PostMessageW(GetParent(outBox),WM_APP+1,(WPARAM)new std::string(raw.empty()?"ERROR:"+e:raw),0);}).detach();}
static void StartEmicDialogAsync(){scannerArmed=false;scannerBuffer.clear();EnableWindow(scanButton,FALSE);SetWindowTextW(scanButton,L"正在打开凭证窗口...");SetWindowTextW(outBox,L"请在医保电子凭证窗口中出示二维码。");Trace("emic.dialog.requested","cn.nhsa.qrcode.get");std::thread([](){std::string e,raw=Emic("",e);PostMessageW(GetParent(outBox),WM_APP+1,(WPARAM)new std::string(raw.empty()?"ERROR:"+e:raw),0);}).detach();}
static LRESULT CALLBACK InputProc(HWND h,UINT m,WPARAM w,LPARAM l){if(m==WM_KILLFOCUS&&scannerArmed){scannerArmed=false;scannerBuffer.clear();SetWindowTextW(scanButton,L"扫医保电子凭证");SetWindowTextW(outBox,L"已取消扫码,请重新点击“扫医保电子凭证”。");Trace("scanner.cancelled","scanner input lost focus");}if(m==WM_CHAR&&scannerArmed){if(w==L'\r'||w==L'\n'){std::string code=Trim(A(scannerBuffer));scannerBuffer.clear();if(code.empty())Trace("scanner.input.empty");else{Trace("scanner.input.received",code);ScanAsync(code);}return 0;}if(w>=32)scannerBuffer.push_back((wchar_t)w);return 0;}if(m==WM_KEYDOWN&&w==VK_RETURN&&scannerArmed)return 0;return CallWindowProcW(inputProc,h,m,w,l);} static LRESULT CALLBACK InputProc(HWND h,UINT m,WPARAM w,LPARAM l){if(m==WM_KILLFOCUS&&scannerArmed){scannerArmed=false;scannerBuffer.clear();SetWindowTextW(scanButton,L"扫医保电子凭证");SetWindowTextW(outBox,L"已取消扫码,请重新点击“扫医保电子凭证”。");Trace("scanner.cancelled","scanner input lost focus");}if(m==WM_CHAR&&scannerArmed){if(w==L'\r'||w==L'\n'){std::string code=Trim(A(scannerBuffer));scannerBuffer.clear();if(code.empty())Trace("scanner.input.empty");else{Trace("scanner.input.received",code);ScanAsync(code);}return 0;}if(w>=32)scannerBuffer.push_back((wchar_t)w);return 0;}if(m==WM_KEYDOWN&&w==VK_RETURN&&scannerArmed)return 0;return CallWindowProcW(inputProc,h,m,w,l);}
static void PaintButton(const DRAWITEMSTRUCT* d){HDC dc=d->hDC;RECT r=d->rcItem;bool disabled=(d->itemState&ODS_DISABLED)!=0,pressed=(d->itemState&ODS_SELECTED)!=0;COLORREF color=disabled?RGB(163,181,203):(d->CtlID==12?RGB(30,104,190):RGB(21,137,196));if(pressed)color=RGB(18,79,153);HBRUSH b=CreateSolidBrush(color);HPEN p=CreatePen(PS_SOLID,1,color);HGDIOBJ oldB=SelectObject(dc,b),oldP=SelectObject(dc,p);RoundRect(dc,r.left,r.top,r.right,r.bottom,10,10);SelectObject(dc,oldB);SelectObject(dc,oldP);DeleteObject(b);DeleteObject(p);wchar_t text[128];GetWindowTextW(d->hwndItem,text,128);SetBkMode(dc,TRANSPARENT);SetTextColor(dc,RGB(255,255,255));HFONT old=(HFONT)SelectObject(dc,buttonFont);DrawTextW(dc,text,-1,&r,DT_CENTER|DT_VCENTER|DT_SINGLELINE);SelectObject(dc,old);} static void PaintButton(const DRAWITEMSTRUCT* d){HDC dc=d->hDC;RECT r=d->rcItem;bool disabled=(d->itemState&ODS_DISABLED)!=0,pressed=(d->itemState&ODS_SELECTED)!=0;COLORREF color=disabled?RGB(163,181,203):(d->CtlID==12?RGB(30,104,190):RGB(21,137,196));if(pressed)color=RGB(18,79,153);HBRUSH b=CreateSolidBrush(color);HPEN p=CreatePen(PS_SOLID,1,color);HGDIOBJ oldB=SelectObject(dc,b),oldP=SelectObject(dc,p);RoundRect(dc,r.left,r.top,r.right,r.bottom,10,10);SelectObject(dc,oldB);SelectObject(dc,oldP);DeleteObject(b);DeleteObject(p);wchar_t text[128];GetWindowTextW(d->hwndItem,text,128);SetBkMode(dc,TRANSPARENT);SetTextColor(dc,RGB(255,255,255));HFONT old=(HFONT)SelectObject(dc,buttonFont);DrawTextW(dc,text,-1,&r,DT_CENTER|DT_VCENTER|DT_SINGLELINE);SelectObject(dc,old);}
static LRESULT CALLBACK Proc(HWND h,UINT m,WPARAM w,LPARAM l){if(m==WM_PAINT){PAINTSTRUCT ps;HDC dc=BeginPaint(h,&ps);RECT r;GetClientRect(h,&r);FillRect(dc,&r,backgroundBrush);RECT header={0,0,r.right,118};FillRect(dc,&header,headerBrush);EndPaint(h,&ps);return 0;}if(m==WM_CTLCOLORSTATIC){HDC dc=(HDC)w;HWND control=(HWND)l;if(control==titleLabel||control==subtitleLabel){SetBkMode(dc,TRANSPARENT);SetTextColor(dc,RGB(255,255,255));return (LRESULT)headerBrush;}SetBkColor(dc,RGB(255,255,255));SetTextColor(dc,RGB(36,54,75));return (LRESULT)GetStockObject(WHITE_BRUSH);}if(m==WM_DRAWITEM){PaintButton((const DRAWITEMSTRUCT*)l);return TRUE;}if(m==WM_COMMAND){if(LOWORD(w)==10){SetWindowTextW(inputBox,L"");scannerBuffer.clear();scannerArmed=true;SetFocus(inputBox);SetWindowTextW(scanButton,L"请使用扫码墩扫码...");SetWindowTextW(outBox,L"扫码墩已就绪,请扫描医保电子凭证。");Trace("scanner.armed","input focused");return 0;}if(LOWORD(w)==12){EnableWindow(cardButton,FALSE);SetWindowTextW(outBox,L"正在读取社保卡,请保持卡片位置不动...");Trace("card.gui.clicked");std::thread([](){std::string e,raw=Card(cfg.readerType,true,e);PostMessageW(GetParent(outBox),WM_APP+2,(WPARAM)new std::string(raw.empty()?"ERROR:"+e:raw),0);}).detach();return 0;}}if(m==WM_APP+1||m==WM_APP+2){std::string* r=(std::string*)w;if(m==WM_APP+1){EnableWindow(scanButton,TRUE);SetWindowTextW(scanButton,L"扫医保电子凭证");}else EnableWindow(cardButton,TRUE);if(r->compare(0,6,"ERROR:")==0){SetWindowTextW(outBox,W(r->substr(6)).c_str());Trace(m==WM_APP+1?"emic.gui.error":"card.gui.error",r->substr(6));}else if(m==WM_APP+1)ShowEmic(*r);else{std::string e;if(ShowQr(*r,e))SetWindowTextW(outBox,L"读卡成功,请扫描上方二维码");else SetWindowTextW(outBox,W(e).c_str());}delete r;return 0;}if(m==WM_DESTROY){DeleteObject(titleFont);DeleteObject(bodyFont);DeleteObject(buttonFont);DeleteObject(headerBrush);DeleteObject(backgroundBrush);PostQuitMessage(0);return 0;}return DefWindowProcW(h,m,w,l);} static LRESULT CALLBACK Proc(HWND h,UINT m,WPARAM w,LPARAM l){if(m==WM_PAINT){PAINTSTRUCT ps;HDC dc=BeginPaint(h,&ps);RECT r;GetClientRect(h,&r);FillRect(dc,&r,backgroundBrush);RECT header={0,0,r.right,118};FillRect(dc,&header,headerBrush);EndPaint(h,&ps);return 0;}if(m==WM_CTLCOLORSTATIC){HDC dc=(HDC)w;HWND control=(HWND)l;if(control==titleLabel||control==subtitleLabel){SetBkMode(dc,TRANSPARENT);SetTextColor(dc,RGB(255,255,255));return (LRESULT)headerBrush;}SetBkColor(dc,RGB(255,255,255));SetTextColor(dc,RGB(36,54,75));return (LRESULT)GetStockObject(WHITE_BRUSH);}if(m==WM_DRAWITEM){PaintButton((const DRAWITEMSTRUCT*)l);return TRUE;}if(m==WM_COMMAND){if(LOWORD(w)==10){StartEmicDialogAsync();return 0;}if(LOWORD(w)==12){EnableWindow(cardButton,FALSE);SetWindowTextW(outBox,L"正在读取社保卡,请保持卡片位置不动...");Trace("card.gui.clicked");std::thread([](){std::string e,raw=Card(cfg.readerType,true,e);PostMessageW(GetParent(outBox),WM_APP+2,(WPARAM)new std::string(raw.empty()?"ERROR:"+e:raw),0);}).detach();return 0;}}if(m==WM_APP+1||m==WM_APP+2){std::string* r=(std::string*)w;if(m==WM_APP+1){EnableWindow(scanButton,TRUE);SetWindowTextW(scanButton,L"扫医保电子凭证");}else EnableWindow(cardButton,TRUE);if(r->compare(0,6,"ERROR:")==0){SetWindowTextW(outBox,W(r->substr(6)).c_str());Trace(m==WM_APP+1?"emic.gui.error":"card.gui.error",r->substr(6));}else if(m==WM_APP+1)ShowEmic(*r);else{std::string e;if(ShowQr(*r,e))SetWindowTextW(outBox,L"读卡成功,请扫描上方二维码");else SetWindowTextW(outBox,W(e).c_str());}delete r;return 0;}if(m==WM_DESTROY){DeleteObject(titleFont);DeleteObject(bodyFont);DeleteObject(buttonFont);DeleteObject(headerBrush);DeleteObject(backgroundBrush);PostQuitMessage(0);return 0;}return DefWindowProcW(h,m,w,l);}
static void RunGui(HINSTANCE i){headerBrush=CreateSolidBrush(RGB(25,72,132));backgroundBrush=CreateSolidBrush(RGB(242,246,251));titleFont=CreateFontW(-26,0,0,0,FW_BOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH,L"Microsoft YaHei UI");bodyFont=CreateFontW(-15,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH,L"Microsoft YaHei UI");buttonFont=CreateFontW(-16,0,0,0,FW_BOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH,L"Microsoft YaHei UI");WNDCLASSW c={};c.lpfnWndProc=Proc;c.hInstance=i;c.lpszClassName=L"readcardc";c.hCursor=LoadCursor(0,IDC_ARROW);RegisterClassW(&c);WNDCLASSW q={};q.lpfnWndProc=QrProc;q.hInstance=i;q.lpszClassName=L"readcardc_qr";q.hCursor=LoadCursor(0,IDC_ARROW);RegisterClassW(&q);HWND h=CreateWindowExW(0,c.lpszClassName,L"社保卡 / 医保电子凭证",WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_VISIBLE,200,100,620,740,0,0,i,0);titleLabel=CreateWindowW(L"STATIC",L"社保卡读卡 / 医保电子凭证",WS_CHILD|WS_VISIBLE,28,18,500,36,h,0,i,0);subtitleLabel=CreateWindowW(L"STATIC",L"读卡生成二维码 · 扫码自动解析凭证信息",WS_CHILD|WS_VISIBLE,30,58,520,24,h,0,i,0);SendMessageW(titleLabel,WM_SETFONT,(WPARAM)titleFont,TRUE);SendMessageW(subtitleLabel,WM_SETFONT,(WPARAM)bodyFont,TRUE);qrView=CreateWindowExW(WS_EX_CLIENTEDGE,q.lpszClassName,L"",WS_CHILD|WS_VISIBLE,140,138,340,330,h,0,i,0);outBox=CreateWindowExW(WS_EX_CLIENTEDGE,L"EDIT",L"请放置社保卡,然后点击下方“读社保卡”。",WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|WS_VSCROLL|ES_READONLY,32,492,536,112,h,0,i,0);SendMessageW(outBox,WM_SETFONT,(WPARAM)bodyFont,TRUE);inputBox=CreateWindowW(L"EDIT",L"",WS_CHILD|WS_VISIBLE,-10,-10,1,1,h,(HMENU)11,i,0);inputProc=(WNDPROC)SetWindowLongPtrW(inputBox,GWLP_WNDPROC,(LONG_PTR)InputProc);cardButton=CreateWindowW(L"BUTTON",L"读社保卡",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,105,635,170,46,h,(HMENU)12,i,0);scanButton=CreateWindowW(L"BUTTON",L"扫医保电子凭证",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,305,635,210,46,h,(HMENU)10,i,0);Trace("gui.ready","log file is next to executable in logs");MSG msg;while(GetMessageW(&msg,0,0,0)){TranslateMessage(&msg);DispatchMessageW(&msg);}} static void RunGui(HINSTANCE i){headerBrush=CreateSolidBrush(RGB(25,72,132));backgroundBrush=CreateSolidBrush(RGB(242,246,251));titleFont=CreateFontW(-26,0,0,0,FW_BOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH,L"Microsoft YaHei UI");bodyFont=CreateFontW(-15,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH,L"Microsoft YaHei UI");buttonFont=CreateFontW(-16,0,0,0,FW_BOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH,L"Microsoft YaHei UI");WNDCLASSW c={};c.lpfnWndProc=Proc;c.hInstance=i;c.lpszClassName=L"readcardc";c.hCursor=LoadCursor(0,IDC_ARROW);RegisterClassW(&c);WNDCLASSW q={};q.lpfnWndProc=QrProc;q.hInstance=i;q.lpszClassName=L"readcardc_qr";q.hCursor=LoadCursor(0,IDC_ARROW);RegisterClassW(&q);HWND h=CreateWindowExW(0,c.lpszClassName,L"社保卡 / 医保电子凭证",WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_VISIBLE,200,100,620,740,0,0,i,0);titleLabel=CreateWindowW(L"STATIC",L"社保卡读卡 / 医保电子凭证",WS_CHILD|WS_VISIBLE,28,18,500,36,h,0,i,0);subtitleLabel=CreateWindowW(L"STATIC",L"读卡生成二维码 · 扫码自动解析凭证信息",WS_CHILD|WS_VISIBLE,30,58,520,24,h,0,i,0);SendMessageW(titleLabel,WM_SETFONT,(WPARAM)titleFont,TRUE);SendMessageW(subtitleLabel,WM_SETFONT,(WPARAM)bodyFont,TRUE);qrView=CreateWindowExW(WS_EX_CLIENTEDGE,q.lpszClassName,L"",WS_CHILD|WS_VISIBLE,140,138,340,330,h,0,i,0);outBox=CreateWindowExW(WS_EX_CLIENTEDGE,L"EDIT",L"请放置社保卡,然后点击下方“读社保卡”。",WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|WS_VSCROLL|ES_READONLY,32,492,536,112,h,0,i,0);SendMessageW(outBox,WM_SETFONT,(WPARAM)bodyFont,TRUE);inputBox=CreateWindowW(L"EDIT",L"",WS_CHILD|WS_VISIBLE,-10,-10,1,1,h,(HMENU)11,i,0);inputProc=(WNDPROC)SetWindowLongPtrW(inputBox,GWLP_WNDPROC,(LONG_PTR)InputProc);cardButton=CreateWindowW(L"BUTTON",L"读社保卡",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,105,635,170,46,h,(HMENU)12,i,0);scanButton=CreateWindowW(L"BUTTON",L"扫医保电子凭证",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,305,635,210,46,h,(HMENU)10,i,0);Trace("gui.ready","log file is next to executable in logs");MSG msg;while(GetMessageW(&msg,0,0,0)){TranslateMessage(&msg);DispatchMessageW(&msg);}}
int WINAPI wWinMain(HINSTANCE i,HINSTANCE,LPWSTR,int){wchar_t p[MAX_PATH];GetModuleFileNameW(0,p,MAX_PATH);root=std::wstring(p);root=root.substr(0,root.find_last_of(L"\\/"));LoadConfig();Trace("application.start","trace enabled");std::string e;if(!LoadDlls(e)){MessageBoxW(0,W(e).c_str(),L"readcard-c",MB_ICONERROR);return 1;}RunGui(i);return 0;} int WINAPI wWinMain(HINSTANCE i,HINSTANCE,LPWSTR,int){wchar_t p[MAX_PATH];GetModuleFileNameW(0,p,MAX_PATH);root=std::wstring(p);root=root.substr(0,root.find_last_of(L"\\/"));LoadConfig();Trace("application.start","trace enabled");std::string e;if(!LoadDlls(e)){MessageBoxW(0,W(e).c_str(),L"readcard-c",MB_ICONERROR);return 1;}RunGui(i);return 0;}

View File

@ -1,7 +1,7 @@
@echo off @echo off
setlocal setlocal
cd /d "%~dp0" cd /d "%~dp0"
title readcard-c package title Haining Card Reader package
where powershell.exe >nul 2>&1 where powershell.exe >nul 2>&1
if errorlevel 1 ( if errorlevel 1 (
@ -18,6 +18,6 @@ if not "%RESULT%"=="0" (
pause pause
exit /b %RESULT% exit /b %RESULT%
) )
echo Packaging completed. Output: %~dp0dist echo Packaging completed. Open the dist folder to find HainingCardReader.exe.
start "" "%~dp0dist" start "" "%~dp0dist"
pause pause