КАК ВЫВЕСТИ СПИСОК УСТРОЙСТВ ЧТЕНИЯ СМАРТ-КАРТ С ПОМОЩЬЮ ПК / SC

Lord777

Professional
Messages
2,583
Reputation
15
Reaction score
1,266
Points
113
ВСТУПЛЕНИЕ
Это первая из нескольких статей об использовании API смарт-карт PC / SC в Windows с кардридерами PC / SC. В следующих статьях могут быть рассмотрены такие темы, как чтение и запись данных на карты MIFARE Ultralight, теги NFC с использованием OpenPCSC. К сожалению, мы не можем публиковать статьи с использованием MIFARE DESFire, поскольку эта технология находится под строгим соглашением о неразглашении с NXP.

Код в этих статьях будет написан на C ++ 11 с использованием набора символов Unicode и протестирован в сообществе Microsoft Visual Studio 2015. Также предполагалось, что читатель знаком с разработкой на C ++ 11 и Windows. В ходе этих статей мы будем разрабатывать простой класс для работы с API смарт-карт.

ФОН
В статье показано, как использовать Windows API PC / SC для вывода списка всех подключенных устройств чтения карт PC / SC.

ВЫВОД СПИСКА СЧИТЫВАТЕЛЕЙ PC / SC
Перечислить подключенные считыватели PC / SC довольно просто, но не без недостатков.

Чтобы использовать API смарт-карты PC / SC, необходимо включить заголовочный файл winscard.h и связать его с winscard.lib, что в Microsoft Visual Studio можно сделать следующим образом:
Code:
#include <winscard.h>
#pragma comment(lib, "winscard.lib")

Перед использованием любой из функций смарт-карты требуется дескриптор контекста смарт-карты (SCARDCONEXT), который необходимо освободить, когда мы закончим с ним. Наш класс обрабатывает это следующим образом:
Code:
using CReaderList = std::vector<CString>;
  
// defines wrapper class for PC/SC smart card API
class CSmartcard
{
public:
    CSmartcard();
    ~CSmartcard();
  
    // initialise interface, throws CSmartcardException
    void Init();
  
    // get a list of readers throws CSmartcardException
    const CReaderList& ListReaders();
  
protected:
    SCARDCONTEXT m_hSC;
    CReaderList m_readers;
};
 
// implements the smartcard class
// the constructor
CSmartcard::CSmartcard() : m_hSC(NULL) {}
  
// and the destructor
CSmartcard::~CSmartcard()
{
    if (m_hSC)
    {
        SCardReleaseContext(m_hSC);
    }
}
  
// initialise interface
void CSmartcard::Init()
{
    if (m_hSC == NULL)
    {
        LONG ret = SCardEstablishContext(
            SCARD_SCOPE_USER,
            NULL,
            NULL,
            &m_hSC);
  
        if (ret != SCARD_S_SUCCESS)
        {
            throw CSmartcardException(ret);
        }
    }
}

Параметры, используемые здесь для SCardEstablishContext, - это все, что обычно требуется. Вы заметите, что мы также генерируем исключение CSmartcardException в случае сбоя SCardEstablishContext, его реализация выглядит следующим образом:
Code:
// the definition of the exception class
class CSmartcardException
{
public:
    CSmartcardException(LONG errorCode) : m_errorCode(errorCode) {}
 
    // get error code
    inline LONG ErrorCode() const
    {
        return m_errorCode;
    }
  
    // get text for error code
    inline CString ErrorText() const
    {
        return CString(_com_error(m_errorCode).ErrorMessage());
    }
  
protected:
    LONG m_errorCode;
};

Функция получения списка читателей - SCardListReaders. Синтаксис следующий:
Code:
LONG WINAPI SCardListReaders (

_In_ SCARDCONTEXT hContext,
_In_opt_ LPCTSTR mszGroups,
_Out_ LPTSTR mszReaders,
_Inout_ LPDWORD pcchReaders

);

mszGroupsУказывает, какие группы считывателей требуются, и типы групп здесь следующие: все считыватели (SCARD_ALL_READERS или NULL), считыватели по умолчанию (SCARD_DEFAULT_READERS), локальные считыватели (SCARD_LOCAL_READER) и системные считыватели (SCARD_SYSTEM_READERS). В большинстве случаев достаточно установить значение NULL.
mszReadersЭто многострочный тип (буфер с завершающим нулем, состоящий из конкатенированных строк с завершающим нулем). Успешно получить буфер с полным списком читателей можно одним из двух способов. Либо установите для этого параметра значение NULL, и pcchReaders будет содержать размер необходимого буфера, а затем вызвать функцию с указателем на новый буфер, либо передать указатель на указатель и установить для pcchReaders значение SCARD_AUTOALLOCATE. Обратите внимание, что при использовании последнего варианта выделенная память должна быть освобождена с помощью SCardFreeMemory, когда она больше не требуется.
pcchReadersЭто будет содержать количество символов в mszReaders. Его необходимо установить равным размеру буфера (0, если mszReaders равно NULL) или SCARD_AUTOALLOCATE. Длина включает все нулевые символы.

Возвращаемое значение будет SCARD_S_SUCCESS, если функция завершилась успешно. В следующем коде добавлена функция, которую можно использовать для получения списка устройств чтения карт.
Code:
// get a list of readers
const CReaderList& CSmartcard::ListReaders()
{
    m_readers.empty();
  
    // initialise if not already done
    Init();
  
    // will auto allocate memory for the list of readers
    TCHAR *pszReaderList = nullptr;
    DWORD len = SCARD_AUTOALLOCATE;
  
    LONG ret = SCardListReaders(
        m_hSC,
        NULL,   // groups, using null will list all readers in the system
        (LPWSTR)&pszReaderList, // pointer where to store the readers
        &len);  // will return the length of characters
            // in the reader list buffer
  
    if (ret == SCARD_S_SUCCESS)
    {
        TCHAR *pszReader = pszReaderList;
        while (*pszReader)
        {
            m_readers.push_back(pszReader);
            pszReader += _tcslen(pszReader) + 1;
        }
  
        // free the memory
        ret = SCardFreeMemory(m_hSC, pszReaderList);
    }
    else
    {
        throw CSmartcardException(ret);
    }
  
    return m_readers;
}

Чтобы использовать наши функции CSmartCard и ListReaders, мы можем сделать следующее:
Code:
int _tmain(int argc, _TCHAR* argv[])
{
    // to list the card readers attached
    CSmartcard smartcard;
  
    try
    {
        // initialise the smart-card class
        smartcard.Init();
  
        // get a list of attached readers
        auto readerList = smartcard.ListReaders();
  
        for (const auto &reader : readerList)
        {
            _tprintf(_T("Reader: %s\n"), reader);
        }
    }
    catch (const CSmartcardException &ex)
    {
        _tprintf(_T("Error %s (%08x)\n"),
            ex.ErrorText(),
            ex.ErrorCode());
    }
  
    return 0;
}

Пример вывода выглядит следующим образом:
screen-showing-listed-smart-card-readers-in-PC-SC-Windows-API.jpg

Экран, показывающий перечисленные устройства чтения смарт-карт в PC / SC Windows API.

В этом случае у меня было 3 подключенных кардридера: ACS ACR122, Identiv Cloud 3700 и Omnikey 5321, но вы заметите, что в списке указаны 4 кардридера! Это связано с тем, что Omnikey 5321 имеет как контактный, так и бесконтактный интерфейс и рассматривается как независимые устройства чтения карт.

ЧТО ТЕПЕРЬ!
Узнав имя (имена) устройства для чтения карт, вы можете подключиться к карте, которая находится на нем, и делать такие вещи, как: считывать UID, узнавать тип карты, считывать данные, хранящиеся на карте, и т. д.
 
Top