API смарт-карт выбрасывают исключения первого шанса, но программа работает нормально
ВСТУПЛЕНИЕ
Я учусь использовать 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
из демо-версии.Когда считыватель подключен, и я нажимаю ВХОДИТЬ, код выключается чисто.
Обратите внимание на соответствующую часть содержимого выходного окна:
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).
Резьба ox1D2C это
SmartCardListener
из демо-версии.ВОПРОС
- Верен ли мой вывод ? Была ли это просто ложная тревога ?
- Если мой вывод неверен, не могли бы вы проинструктировать меня, как решить эту проблему ?
Что я уже пробовал:
В обоих случаях я сделал следующее:
- Зашел в меню Visual Studio Debug -> исключения и проверил Брошенный флажок
- Запуск кода в режиме отладки осуществляется нажатием кнопки F10
- Каждый раз он ломался в API смарт-карт
- Появится всплывающее окно "исключение первого шанса..."
- Я бы выбрал продолжить (перерыв и отмена также были предложены)
- Всплывающее окно появится снова, и я снова выберу продолжить
Опять же, программа будет закрыта чисто, проверка возвращаемых значений показала
SCARD_S_SUCCESS
или ожидаемое значение (SCARD_E_CANCELLED
когда SCardCancel
был вызван или SCARD_E_NO_READERS_AVAILABLE
когда читатель не присутствовал).Я также заметил, что исключение всегда происходит в ячейке памяти 0x76DD5EF8, но я не знаю, как использовать эту информацию, чтобы помочь мне решить эту проблему.
Я попробовал поискать решение в Google, и единственная полезная вещь, которую я нашел, была этот[^].
Прочитав ссылку, приведенную в принятом ответе, я пришел к выводу, что эти исключения могут быть ложной тревогой.