介绍
我正在学习如何使用智能卡 API,以便在将卡号插入读卡器后获取卡号。
到目前为止,我已经设法创建了一个运行(据我所见)没有错误的工作应用程序。
问题
在调试过程中,我看到了各种第一次机会异常,但每个智能卡 API 都会返回SCARD_S_SUCCESS
并且程序不会崩溃,也不会出现任何其他错误行为。
相关信息
- 我正在使用 Visual Studio C++ 2013
- 操作系统是Windows 8.1
- 使用的阅读器是ACR122U
- 重现问题的 MVCE:
#include <Windows.h>
#include <iostream>
#include <iomanip>
#include <winscard.h>
#pragma comment(lib, "Winscard.lib")
void f() // helper that transforms error code to meaningful message
{
LPSTR s = NULL;
if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ::GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), s, 0, NULL))
{
if (NULL != s)
{
std::cout << s << std::endl;
::LocalFree(s);
}
}
}
const char* error_code_to_text(LONG error_code) // converts SCARD error code to string
{
switch (error_code)
{
case SCARD_S_SUCCESS:
return "SCARD_S_SUCCESS";
case ERROR_BROKEN_PIPE:
return "ERROR_BROKEN_PIPE";
case SCARD_E_BAD_SEEK:
return "SCARD_E_BAD_SEEK";
case SCARD_E_CANCELLED:
return "SCARD_E_CANCELLED";
case SCARD_E_CANT_DISPOSE:
return "SCARD_E_CANT_DISPOSE";
case SCARD_E_CARD_UNSUPPORTED:
return "SCARD_E_CARD_UNSUPPORTED";
case SCARD_E_CERTIFICATE_UNAVAILABLE:
return "SCARD_E_CERTIFICATE_UNAVAILABLE";
case SCARD_E_COMM_DATA_LOST:
return "SCARD_E_COMM_DATA_LOST";
case SCARD_E_DIR_NOT_FOUND:
return "SCARD_E_DIR_NOT_FOUND";
case SCARD_E_DUPLICATE_READER:
return "SCARD_E_DUPLICATE_READER";
case SCARD_E_FILE_NOT_FOUND:
return "SCARD_E_FILE_NOT_FOUND";
case SCARD_E_ICC_CREATEORDER:
return "SCARD_E_ICC_CREATEORDER";
case SCARD_E_ICC_INSTALLATION:
return "SCARD_E_ICC_INSTALLATION";
case SCARD_E_INSUFFICIENT_BUFFER:
return "SCARD_E_INSUFFICIENT_BUFFER";
case SCARD_E_INVALID_ATR:
return "SCARD_E_INVALID_ATR";
case SCARD_E_INVALID_CHV:
return "SCARD_E_INVALID_CHV";
case SCARD_E_INVALID_HANDLE:
return "SCARD_E_INVALID_HANDLE";
case SCARD_E_INVALID_PARAMETER:
return "SCARD_E_INVALID_PARAMETER";
case SCARD_E_INVALID_TARGET:
return "SCARD_E_INVALID_TARGET";
case SCARD_E_INVALID_VALUE:
return "SCARD_E_INVALID_VALUE";
case SCARD_E_NO_ACCESS:
return "SCARD_E_NO_ACCESS";
case SCARD_E_NO_DIR:
return "SCARD_E_NO_DIR";
case SCARD_E_NO_FILE:
return "SCARD_E_NO_FILE";
case SCARD_E_NO_KEY_CONTAINER:
return "SCARD_E_NO_KEY_CONTAINER";
case SCARD_E_NO_MEMORY:
return "SCARD_E_NO_MEMORY";
case SCARD_E_NO_PIN_CACHE:
return "SCARD_E_NO_PIN_CACHE";
case SCARD_E_NO_READERS_AVAILABLE:
return "SCARD_E_NO_READERS_AVAILABLE";
case SCARD_E_NO_SERVICE:
return "SCARD_E_NO_SERVICE";
case SCARD_E_NO_SMARTCARD:
return "SCARD_E_NO_SMARTCARD";
case SCARD_E_NO_SUCH_CERTIFICATE:
return "SCARD_E_NO_SUCH_CERTIFICATE";
case SCARD_E_NOT_READY:
return "SCARD_E_NOT_READY";
case SCARD_E_NOT_TRANSACTED:
return "SCARD_E_NOT_TRANSACTED";
case SCARD_E_PCI_TOO_SMALL:
return "SCARD_E_PCI_TOO_SMALL";
case SCARD_E_PIN_CACHE_EXPIRED:
return "SCARD_E_PIN_CACHE_EXPIRED";
case SCARD_E_PROTO_MISMATCH:
return "SCARD_E_PROTO_MISMATCH";
case SCARD_E_READ_ONLY_CARD:
return "SCARD_E_READ_ONLY_CARD";
case SCARD_E_READER_UNAVAILABLE:
return "SCARD_E_READER_UNAVAILABLE";
case SCARD_E_READER_UNSUPPORTED:
return "SCARD_E_READER_UNSUPPORTED";
case SCARD_E_SERVER_TOO_BUSY:
return "SCARD_E_SERVER_TOO_BUSY";
case SCARD_E_SERVICE_STOPPED:
return "SCARD_E_SERVICE_STOPPED";
case SCARD_E_SHARING_VIOLATION:
return "SCARD_E_SHARING_VIOLATION";
case SCARD_E_SYSTEM_CANCELLED:
return "SCARD_E_SYSTEM_CANCELLED";
case SCARD_E_TIMEOUT:
return "SCARD_E_TIMEOUT";
case SCARD_E_UNEXPECTED:
return "SCARD_E_UNEXPECTED";
case SCARD_E_UNKNOWN_CARD:
return "SCARD_E_UNKNOWN_CARD";
case SCARD_E_UNKNOWN_READER:
return "SCARD_E_UNKNOWN_READER";
case SCARD_E_UNKNOWN_RES_MNG:
return "SCARD_E_UNKNOWN_RES_MNG";
case SCARD_E_UNSUPPORTED_FEATURE:
return "SCARD_E_UNSUPPORTED_FEATURE";
case SCARD_E_WRITE_TOO_MANY:
return "SCARD_E_WRITE_TOO_MANY";
case SCARD_F_COMM_ERROR:
return "SCARD_F_COMM_ERROR";
case SCARD_F_INTERNAL_ERROR:
return "SCARD_F_INTERNAL_ERROR";
case SCARD_F_UNKNOWN_ERROR:
return "SCARD_F_UNKNOWN_ERROR";
case SCARD_F_WAITED_TOO_LONG:
return "SCARD_F_WAITED_TOO_LONG";
case SCARD_P_SHUTDOWN:
return "SCARD_P_SHUTDOWN";
case SCARD_W_CANCELLED_BY_USER:
return "SCARD_W_CANCELLED_BY_USER";
case SCARD_W_CACHE_ITEM_NOT_FOUND:
return "SCARD_W_CACHE_ITEM_NOT_FOUND";
case SCARD_W_CACHE_ITEM_STALE:
return "SCARD_W_CACHE_ITEM_STALE";
case SCARD_W_CACHE_ITEM_TOO_BIG:
return "SCARD_W_CACHE_ITEM_TOO_BIG";
case SCARD_W_CARD_NOT_AUTHENTICATED:
return "SCARD_W_CARD_NOT_AUTHENTICATED";
case SCARD_W_CHV_BLOCKED:
return "SCARD_W_CHV_BLOCKED";
case SCARD_W_EOF:
return "SCARD_W_EOF";
case SCARD_W_REMOVED_CARD:
return "SCARD_W_REMOVED_CARD";
case SCARD_W_RESET_CARD:
return "SCARD_W_RESET_CARD";
case SCARD_W_SECURITY_VIOLATION:
return "SCARD_W_SECURITY_VIOLATION";
case SCARD_W_UNPOWERED_CARD:
return "SCARD_W_UNPOWERED_CARD";
case SCARD_W_UNRESPONSIVE_CARD:
return "SCARD_W_UNRESPONSIVE_CARD";
case SCARD_W_UNSUPPORTED_CARD:
return "SCARD_W_UNSUPPORTED_CARD";
case SCARD_W_WRONG_CHV:
return "SCARD_W_WRONG_CHV";
default:
return "Unknown error code";
}
}
DWORD WINAPI SmartCardListener(LPVOID arg)
{
// get thread ID to compare it with the one in Output window
std::cout << "SmartCardListener Thread ID = "
<< std::setw(8) << std::setfill('0') << std::hex
<< ::GetCurrentThreadId() << std::endl;
SCARDCONTEXT c = *reinterpret_cast<SCARDCONTEXT *>(arg);
LPTSTR r = NULL;
DWORD cch = SCARD_AUTOALLOCATE;
LONG l = SCardListReaders(c, NULL, (LPTSTR)&r, &cch);
if (SCARD_S_SUCCESS != l)
{
std::cout << "SCardListReaders failed with error code: "
<< error_code_to_text(l) << std::endl;
return 1;
}
SCARD_READERSTATE rs = {};
rs.dwCurrentState = SCARD_STATE_UNAWARE;
rs.szReader = &r[0];
do
{
l = ::SCardGetStatusChange(c, INFINITE, &rs, 1);
rs.dwCurrentState = rs.dwEventState;
}
while (l == SCARD_S_SUCCESS);
if (SCARD_E_CANCELLED != l)
std::cout << "SCardGetStatusChange failed with error code: "
<< error_code_to_text(l) << std::endl;
l = SCardFreeMemory(c, r);
if (SCARD_S_SUCCESS != l)
{
std::cout << "SCardFreeMemory failed with error code: "
<< error_code_to_text(l) << std::endl;
return 1;
}
return 0;
}
int main()
{
// get thread ID to compare it with the one in Output window
std::cout << "Main Thread ID = "
<< std::setw(8)
<< std::setfill('0')
<< std::hex
<< ::GetCurrentThreadId()
<< std::endl;
SCARDCONTEXT c;
LONG l = ::SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &c);
if (SCARD_S_SUCCESS != l)
{
std::cout << "SCardEstablishContext failed with error code: "
<< error_code_to_text(l)
<< std::endl;
return -1;
}
HANDLE t = ::CreateThread(NULL, 0, SmartCardListener, &c, 0, 0);
if (NULL == t)
{
std::cout << "CreateThread failed:" << std::endl;
f();
::SCardReleaseContext(c);
return 0;
}
std::cout << "Press ENTER to quit..." << std::endl;
std::cin.get();
l = ::SCardCancel(c);
if (SCARD_S_SUCCESS != l)
std::cout << "SCardCancel failed with error code: "
<< error_code_to_text(l)
<< std::endl;
DWORD d = ::WaitForSingleObject(t, INFINITE);
switch (d)
{
case WAIT_OBJECT_0:
std::cout << "Graceful exit" << std::endl;
break;
case WAIT_TIMEOUT:
std::cout << "Timeout" << std::endl;
break;
case WAIT_FAILED:
std::cout << "Wait failed: " << std::endl;
f();
::TerminateThread(t, 1); // what else can I do ?
break;
default:
std::cout << "Unknown error" << std::endl;
::TerminateThread(t, 1); // what else can I do ?
break;
}
::CloseHandle(t);
l = ::SCardReleaseContext(c);
if (SCARD_S_SUCCESS != l)
{
std::cout << "Failed to release context" << std::endl;
}
return 0;
}
我测试了2个案例:
- 阅读器未插入
- 读卡器已插入,程序按ENTER指示按终止
未插入时,应用程序正确显示错误消息,线程干净地退出,但我从输出窗口中获得以下相关内容:
First-chance exception at 0x76DD5EF8 (KernelBase.dll) in SO_Demo.exe: 0x8010002E: Cannot find a smart card reader.
First-chance exception at 0x76DD5EF8 (KernelBase.dll) in SO_Demo.exe: 0x0000071A: The remote procedure call was canceled, or if a call time-out was specified, the call timed out.
First-chance exception at 0x76DD5EF8 in SO_Demo.exe: Microsoft C++ exception: unsigned long at memory location 0x0142F970.
First-chance exception at 0x76DD5EF8 in SO_Demo.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
The thread 0x120c has exited with code 1 (0x1).
线程0x120C
来自SmartCardListener
演示。
当阅读器插入时,我按下ENTER,代码干净地关闭。
注意输出窗口内容的相关部分:
First-chance exception at 0x76DD5EF8 (KernelBase.dll) in SO_Demo.exe: 0x80100002: The action was cancelled by an SCardCancel request.
First-chance exception at 0x76DD5EF8 (KernelBase.dll) in SO_Demo.exe: 0x0000071A: The remote procedure call was canceled, or if a call time-out was specified, the call timed out.
First-chance exception at 0x76DD5EF8 in SO_Demo.exe: Microsoft C++ exception: unsigned long at memory location 0x0139F5E8.
First-chance exception at 0x76DD5EF8 in SO_Demo.exe: Microsoft C++ exception: unsigned long at memory location 0x0139F6D0.
The thread 0x1d2c has exited with code 0 (0x0).
线程0x1D2C
来自SmartCardListener
演示。
我的努力
在这两种情况下,我都做了以下事情:
- 转到 Visual Studio 菜单 Debug -> Exceptions 并选中
Thrown
复选框 - 通过按在调试模式下运行代码F10
- 每次它都会在 Smart Card API 处中断
- 弹出“第一次机会例外......”会出来
- 我会选择
Continue
(Break
并且Cancel
也被提供) - 弹出窗口会再次出现,我会再次选择
Continue
再次,程序将干净地关闭,检查返回值SCARD_S_SUCCESS
或预期值(SCARD_E_CANCELLED
何时SCardCancel
被调用或SCARD_E_NO_READERS_AVAILABLE
阅读器不存在时)。
我还注意到异常总是发生在内存位置0x76DD5EF8
,但我不知道如何使用这些信息来帮助我解决问题。
我试过谷歌搜索解决方案,我发现唯一有用的是这个。
通过阅读接受的答案中提供的链接,我得出的结论是,这些异常可能是误报。
问题
- 我的结论正确吗?这只是虚惊一场?
- 如果我的结论是错误的,你能指导我如何解决这个问题吗?