Lord777
Professional
- Messages
- 2,579
- Reaction score
- 1,471
- 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 можно сделать следующим образом:
Перед использованием любой из функций смарт-карты требуется дескриптор контекста смарт-карты (SCARDCONEXT), который необходимо освободить, когда мы закончим с ним. Наш класс обрабатывает это следующим образом:
Параметры, используемые здесь для SCardEstablishContext, - это все, что обычно требуется. Вы заметите, что мы также генерируем исключение CSmartcardException в случае сбоя SCardEstablishContext, его реализация выглядит следующим образом:
Функция получения списка читателей - SCardListReaders. Синтаксис следующий:
Возвращаемое значение будет SCARD_S_SUCCESS, если функция завершилась успешно. В следующем коде добавлена функция, которую можно использовать для получения списка устройств чтения карт.
Чтобы использовать наши функции CSmartCard и ListReaders, мы можем сделать следующее:
Пример вывода выглядит следующим образом:
Экран, показывающий перечисленные устройства чтения смарт-карт в PC / SC Windows API.
В этом случае у меня было 3 подключенных кардридера: ACS ACR122, Identiv Cloud 3700 и Omnikey 5321, но вы заметите, что в списке указаны 4 кардридера! Это связано с тем, что Omnikey 5321 имеет как контактный, так и бесконтактный интерфейс и рассматривается как независимые устройства чтения карт.
ЧТО ТЕПЕРЬ!
Узнав имя (имена) устройства для чтения карт, вы можете подключиться к карте, которая находится на нем, и делать такие вещи, как: считывать UID, узнавать тип карты, считывать данные, хранящиеся на карте, и т. д.
Это первая из нескольких статей об использовании 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;
}
Пример вывода выглядит следующим образом:

Экран, показывающий перечисленные устройства чтения смарт-карт в PC / SC Windows API.
В этом случае у меня было 3 подключенных кардридера: ACS ACR122, Identiv Cloud 3700 и Omnikey 5321, но вы заметите, что в списке указаны 4 кардридера! Это связано с тем, что Omnikey 5321 имеет как контактный, так и бесконтактный интерфейс и рассматривается как независимые устройства чтения карт.
ЧТО ТЕПЕРЬ!
Узнав имя (имена) устройства для чтения карт, вы можете подключиться к карте, которая находится на нем, и делать такие вещи, как: считывать UID, узнавать тип карты, считывать данные, хранящиеся на карте, и т. д.