AlwaysLearningNewStuff Ответов: 1

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, и единственная полезная вещь, которую я нашел, была этот[^].

Прочитав ссылку, приведенную в принятом ответе, я пришел к выводу, что эти исключения могут быть ложной тревогой.

1 Ответов

Рейтинг:
2

Gerry Schmitz

Что такое "SO_Demo.exe-что?

ИМО, "демо" не обрабатывает последовательность запуска / com / выключения должным образом; и только "руководство" скажет вам об этом.

Все / большинство устройств имеют протокол, который включает в себя "время ожидания" и т. д., Прежде чем вы сможете ожидать ответа и т. д. Игнорируйте это, и все начнет задыхаться, будь то ваше приложение или "поставщик".