Win32 Scard.h API使用问题:读卡器列表获取异常
智能卡读卡器列表获取问题及修正思路
问题现象
调用SCardEstablishContext成功后,使用SCardListReadersA获取读卡器列表时出现异常:API返回成功,且返回的缓冲区大小为89字节,但无法读取到有效字符串。尝试过修改数据类型、插拔读卡器、重启系统、逐行调试等操作,问题依旧,要么得到89个空字节,要么触发断言失败。
问题代码
#include <Windows.h> #include <stdint.h> #include <winscard.h> #include <stdio.h> typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef uint32_t b32; typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64; #define l_persist static #define gvar static #define internal static #define KiloBytes(Value) ((Value) * 1024LL) #define MegaBytes(Value) (KiloBytes(Value) * 1024LL) #if INTERNAL #define Assert(exp) if (!(exp)) {*((u8*)0) = 0;} #define AssertWithMessage(exp, message) if (!(exp)) {OutputDebugString("\nAssertion failed: "); OutputDebugString(message); OutputDebugString("\n"); Assert(exp);} #else #define Assert(exp) #define AssertWithMessage(exp, message) #endif #define U32_MAX 0xFFFFFFFF b32 G_IsRunning = true; LRESULT CALLBACK MainWindowCallback(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) { LRESULT Result = 0; switch(Message) { case WM_CLOSE: case WM_QUIT: case WM_DESTROY: { G_IsRunning = false; } break; default: { Result = DefWindowProc(Window, Message, WParam, LParam); } } return Result; } void MessagePump(HWND Window) { MSG Message = {}; u32 InputCount = 0; while(PeekMessage(&Message, Window, 0, 0, PM_REMOVE)) { switch(Message.message) { case WM_QUIT: { G_IsRunning = false; } break; default: { TranslateMessage(&Message); DispatchMessage(&Message); } break; } } } INT CALLBACK WinMain(HINSTANCE Instance, HINSTANCE PrevInstance, LPSTR CommandLine, INT ShowCommand) { WNDCLASS WindowClass = {}; WindowClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; WindowClass.lpfnWndProc = MainWindowCallback; WindowClass.hInstance = Instance; WindowClass.lpszClassName = "MainWindowClass"; if (RegisterClass(&WindowClass)) { HWND Window = CreateWindowEx(0,WindowClass.lpszClassName, "Main Window", WS_OVERLAPPEDWINDOW|WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,0,0,Instance, 0); if (Window) { SCARDCONTEXT CardContext = U32_MAX; LONG CardContextSuccess = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, &CardContext); Assert(CardContextSuccess == SCARD_S_SUCCESS); u8 *Memory = (u8*)VirtualAlloc(0, KiloBytes(1), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); u8 *MemoryHead = Memory; LPSTR ReadersList = (char*)MemoryHead; LPSTR ReadersListWip = 0; DWORD ReadersListSize = 0; LONG CardReadersListStatus = SCardListReadersA(CardContext, 0, (LPSTR)&ReadersList, &ReadersListSize); Assert(ReadersList); Assert(ReadersListSize); switch(CardReadersListStatus) { case SCARD_S_SUCCESS: { ReadersListWip = ReadersList; while ( '\0' != *ReadersListWip ) { printf("Reader: %S\n", (wchar_t*)ReadersListWip ); // Advance to the next value. ReadersListWip = ReadersListWip + wcslen((wchar_t *)ReadersListWip) + 1; } } break; case SCARD_E_NO_READERS_AVAILABLE: { AssertWithMessage(CardReadersListStatus == SCARD_S_SUCCESS, "There were no card readers available"); G_IsRunning = false; } break; case SCARD_E_READER_UNAVAILABLE: { AssertWithMessage(CardReadersListStatus == SCARD_S_SUCCESS, "The card reader was unavailable"); G_IsRunning = false; } break; default: { AssertWithMessage(CardReadersListStatus == SCARD_S_SUCCESS, "Some other issue with card reader listing"); G_IsRunning = false; } } while(G_IsRunning) { MessagePump(Window); LPSCARDHANDLE CardHandle = 0; DWORD ActiveProtocol = 0; LONG CardConnectStatus = SCardConnectA(CardContext, ReadersList, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, (LPSCARDHANDLE)&CardHandle, &ActiveProtocol); Assert(CardConnectStatus == SCARD_S_SUCCESS); } } } return 0; }
问题根源
经分析,核心错误在于对SCardListReaders系列API的参数逻辑理解错误:
- 未遵循先获取缓冲区大小,再分配内存读取数据的标准流程:需要先传入
NULL作为输出缓冲区,获取所需的缓冲区长度后,再分配对应大小的内存进行第二次调用。 - 忽略了文档中
SCARD_AUTOALLOCATE参数的用法:可以通过该标记让系统自动分配内存,避免手动管理缓冲区的问题。 - 额外的类型不匹配错误:使用
SCardListReadersA(ANSI版本)却用%S格式符和wcslen处理宽字符串,导致读取逻辑异常。
替代API选项
如果需要替代Windows原生的Winscard API,可以考虑:
- PC/SC Lite:跨平台的PC/SC实现,接口与Winscard兼容,支持Windows、Linux、macOS等系统,适合跨平台开发。
- 智能卡厂商专属SDK:部分读卡器厂商(如HID、Gemalto)会提供针对性的SDK,包含更精细化的读取控制接口。
内容的提问来源于stack exchange,提问作者eternalNoob
相关产品推荐
相关产品推荐

