diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d60163..35ba7c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ 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 .. # 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_REQUIRED ON) -add_executable(readcard-c WIN32 src/main.cpp src/qrcodegen.c) -target_compile_definitions(readcard-c PRIVATE UNICODE _UNICODE WIN32_LEAN_AND_MEAN) -target_link_libraries(readcard-c PRIVATE ws2_32 comctl32) +add_executable(HainingCardReader WIN32 src/main.cpp src/qrcodegen.c) +target_compile_definitions(HainingCardReader PRIVATE UNICODE _UNICODE WIN32_LEAN_AND_MEAN) +target_link_libraries(HainingCardReader PRIVATE ws2_32 comctl32) 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. - 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() diff --git a/README.md b/README.md index 1d57d95..958abc8 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ ## 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 -cmake -S . -B build -A Win32 -T v141_xp +cmake -S . -B build -A Win32 -T v141 cmake --build build --config Release ``` @@ -16,8 +16,7 @@ cmake --build build --config Release 将以下内容一起发布: ```text -readcard-c.exe -config.json +HainingCardReader.exe package/DWCardReaderDLL/ 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`。 diff --git a/build.ps1 b/build.ps1 index 0fae235..89618c8 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,7 +3,9 @@ $ErrorActionPreference = "Stop" $Root = $PSScriptRoot $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" $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 $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)) { throw "Build output was not found: $Binary" } -$ExeDestination = Join-Path -Path $Dist -ChildPath "readcard-c.exe" -$ConfigSource = Join-Path -Path $Root -ChildPath "config.json" +$ExeDestination = Join-Path -Path $Dist -ChildPath "HainingCardReader.exe" $ConfigDestination = Join-Path -Path $Dist -ChildPath "config.json" $PackageSource = Join-Path -Path $Root -ChildPath "package" $PackageDestination = Join-Path -Path $Dist -ChildPath "package" -Copy-Item -LiteralPath $Binary -Destination $ExeDestination -Force -Copy-Item -LiteralPath $ConfigSource -Destination $ConfigDestination -Force +try { + 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 Write-Host "Build completed: $Dist" diff --git a/config.json b/config.json index d594c2f..2d48615 100644 --- a/config.json +++ b/config.json @@ -12,6 +12,6 @@ "emic_office_id": "D99", "emic_office_name": "其他科室", "operation_trace_enabled": true, - "operation_trace_include_sensitive": true, + "operation_trace_include_sensitive": false, "log_dir": "logs" } diff --git a/src/main.cpp b/src/main.cpp index b9f163e..c167c64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,8 +18,8 @@ // with the v141_xp toolset. Both vendor DLLs are x86 stdcall libraries. struct Config { 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"; - int port=17880; bool trace=true, traceSensitive=true; + 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=false; }; static std::wstring root; static Config cfg; static std::mutex vendorLock; static std::atomic 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 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<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\":\""< > completed(new std::atomic(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\":\""< ib(in.begin(),in.end());ib.push_back(0);char out[16384]={};std::lock_guardl(vendorLock);Trace("emic.dll.call.start",cfg.emicUrl);std::shared_ptr > completed(new std::atomic(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 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 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 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=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 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);}} 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;} diff --git a/一键打包.bat b/一键打包.bat index 0a22374..f33bcce 100644 --- a/一键打包.bat +++ b/一键打包.bat @@ -1,7 +1,7 @@ @echo off setlocal cd /d "%~dp0" -title readcard-c package +title Haining Card Reader package where powershell.exe >nul 2>&1 if errorlevel 1 ( @@ -18,6 +18,6 @@ if not "%RESULT%"=="0" ( pause exit /b %RESULT% ) -echo Packaging completed. Output: %~dp0dist +echo Packaging completed. Open the dist folder to find HainingCardReader.exe. start "" "%~dp0dist" pause