-
Notifications
You must be signed in to change notification settings - Fork 1
/
ReaderChangesMonitor.cpp
139 lines (128 loc) · 7.21 KB
/
ReaderChangesMonitor.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "ReaderChangesMonitor.h"
#include "Manager.h"
#include "PCSC/ReaderState.h"
#include "PCSC/Status.h"
#include "XFS/Logger.h"
ReaderChangesMonitor::ReaderChangesMonitor(Manager& manager)
: manager(manager), stopRequested(false)
{
// Запускаем поток ожидания изменений.
waitChangesThread.reset(new boost::thread(&ReaderChangesMonitor::run, this));
}
ReaderChangesMonitor::~ReaderChangesMonitor() {
// Запрашиваем остановку потока.
stopRequested = true;
// Сигнализируем о том, что необходимо прервать ожидание
cancel("ReaderChangesMonitor::~ReaderChangesMonitor");
// Ожидаем, пока дойдет.
waitChangesThread->join();
}
void ReaderChangesMonitor::run() {
{XFS::Logger() << "Reader changes dispatch thread runned";}
// Первоначально состояние неизвестное нам.
DWORD readersState = SCARD_STATE_UNAWARE;
while (!stopRequested) {
// На входе текущее состояние считывателей -- на выходе новое состояние.
readersState = getReadersAndWaitChanges(readersState);
}
XFS::Logger() << "Reader changes dispatch thread stopped";
}
/// Получаем список имен считывателей из строки со всеми именами, разделенными символом '\0'.
std::vector<const char*> getReaderNames(const std::vector<char>& readerNames) {
XFS::Logger l;
l << "Avalible readers:";
std::size_t i = 0;
std::size_t k = 0;
std::vector<const char*> names;
const std::size_t size = readerNames.size();
while (i < size) {
const char* name = &readerNames[i];
// Ищем начало следующей строки.
while (i < size && readerNames[i] != '\0') ++i;
// Пропускаем '\0'.
++i;
if (i < size) {
l << '\n' << ++k << ": " << name;
names.push_back(name);
}
}
return names;
}
DWORD ReaderChangesMonitor::getReadersAndWaitChanges(DWORD readersState) {
DWORD readersCount = 0;
// Определяем доступные считыватели: сначало количество, затем сами считыватели.
PCSC::Status st = SCardListReaders(manager.context(), NULL, NULL, &readersCount);
{XFS::Logger() << "SCardListReaders[count](count=&" << readersCount << "): " << st;}
// Получаем имена доступных считывателей. Все имена расположены в одной строке,
// разделены символом '\0' в в конце списка также символ '\0' (т.о. в конце массива
// идет подряд два '\0').
std::vector<char> readerNames(readersCount);
if (readersCount != 0) {
st = SCardListReaders(manager.context(), NULL, &readerNames[0], &readersCount);
XFS::Logger() << "SCardListReaders[data](count=&" << readersCount << "): " << st;
}
std::vector<const char*> names = getReaderNames(readerNames);
// Готовимся к ожиданию событий от всех обнаруженных считывателей и
// изменению их количества.
std::vector<SCARD_READERSTATE> readers(1 + names.size());
// Считыватель со специальным именем, означающем, что необходимо мониторить
// появление/пропажу считывателей.
readers[0].szReader = "\\\\?PnP?\\Notification";
readers[0].dwCurrentState = readersState;
// Заполняем структуры для ожидания событий от найденных считывателей.
for (std::size_t i = 0; i < names.size(); ++i) {
readers[1 + i].szReader = names[i];
}
// Ожидаем событий от считывателей. Если их количество обновилось,
// то прекращаем ожидание. Повторный вход в данную процедуру случится
// на следующем витке цикла в run.
while (!waitChanges(readers)) {
if (stopRequested) {
// Не важно, что возвращать, нам лишь бы выйти.
return 0;
}
}
// Возвращаем текущее состояние наблюдателя за считывателями.
return readers[0].dwCurrentState;
}
bool ReaderChangesMonitor::waitChanges(std::vector<SCARD_READERSTATE>& readers) {
// Данная функция блокирует выполнение до тех пор, пока не произойдет событие.
// Ждем его до таймаута ближайшей задачи на ожидание вставки карты.
PCSC::Status st = SCardGetStatusChange(manager.context(), manager.getTimeout(), &readers[0], (DWORD)readers.size());
{XFS::Logger() << "SCardGetStatusChange: " << st;}
// Если изменение вызвано таймаутом операции, выкидываем из очереди ожидания все
// задачи, чей таймаут уже наступил
if (st.value() == SCARD_E_TIMEOUT) {
manager.processTimeouts(bc::steady_clock::now());
}
bool readersChanged = false;
bool first = true;
for (std::vector<SCARD_READERSTATE>::iterator it = readers.begin(); it != readers.end(); ++it) {
{
PCSC::ReaderState diff = PCSC::ReaderState(it->dwCurrentState ^ it->dwEventState);
XFS::Logger() << "[" << it->szReader << "] old state = " << PCSC::ReaderState(it->dwCurrentState);
XFS::Logger() << "[" << it->szReader << "] new state = " << PCSC::ReaderState(it->dwEventState);
XFS::Logger() << "[" << it->szReader << "] diff = " << diff;
// XFS::Logger() << "[" << it->szReader << "] added = " << PCSC::ReaderState(it->dwEventState & diff.value());
// XFS::Logger() << "[" << it->szReader << "] removed = " << PCSC::ReaderState(it->dwCurrentState & diff.value());
}
// Если что-то изменилось, уведомляем об этом всех заинтересованных.
if (it->dwEventState & SCARD_STATE_CHANGED) {
// Первый элемент в списке -- объект, через который приходят
// уведомления об изменениях самих устройств.
if (first) {
readersChanged = true;
}
manager.notifyChanges(*it, first);
}
// Cообщаем PC/SC, что мы знаем текущее состояние
it->dwCurrentState = it->dwEventState;
first = false;
}
return readersChanged;
}
void ReaderChangesMonitor::cancel(const char* reason) const {
// Сигнализируем о том, что необходимо прервать ожидание
PCSC::Status st = SCardCancel(manager.context());
XFS::Logger() << "SCardCancel[" << reason << "](hContext=" << manager.context() << ") = " << st;
}