Here is a console app that demonstrates how to tie the smart card functions already covered into a working monitor that prints reader state changes and the certs and subject names for the cards that are present.


#pragma once

// Exclude rarely-used stuff from
// Windows headers
#define WIN32_LEAN_AND_MEAN
// skip min/max macros from
// Windows Headers
#define NOMINMAX
// Windows Header Files:
#include <windows.h>
#include <Unknwn.h>
#include <winscard.h>
#include <ncrypt.h>
#include <Wincrypt.h>
#include <credentialprovider.h>

// C++ library header files:
#include <type_traits>
#include <algorithm>
#include <new>
#include <memory>
#include <utility>
#include <limits>
#include <iterator>
#include <thread>
#include <future>
#include <mutex>
#include <vector>
#include <iostream>
#include <iomanip>

// local header files:
#define LIBRARIES_NAMESPACE nslib
#include <libraries.h>
namespace lib = LIBRARIES_NAMESPACE;

#include "..\win32project1\scard_monitor.h"

Set a breakpoint in each of these functions and see where error codes first make an appearance in this module. the static anchor is a device to make sure that the compiler does not eliminate or fold them together.


void unique_error_report_initiated(
HRESULT value,
unique_hresult_def::tag &&)
{
static HRESULT anchor;
anchor = value;
}

void unique_error_report_reset(
HRESULT value,
unique_hresult_def::tag &&)
{
static HRESULT anchor;
anchor = value;
}

void unique_error_report_initiated(
DWORD value,
unique_winerror_def::tag &&)
{
static DWORD anchor;
anchor = value;
}

void unique_error_report_reset(
DWORD value,
unique_winerror_def::tag &&)
{
static DWORD anchor;
anchor = value;
}

Helper functions to print status and certificate properties.


template<typename Certificate>
void printCertificates(
lib::rng::range<Certificate> certificates)
{
for (auto & key : certificates) {
unique_winerror winerror;

PCCERT_CONTEXT certcontext = (
CertCreateCertificateContext(
X509_ASN_ENCODING |
PKCS_7_ASN_ENCODING,
&key.cert[0],
key.cert.size()
)
);
winerror = make_winerror_if(!certcontext);

if (!winerror) {
std::wcout
<< L"could not get cert context"
<< std::endl;
continue;
}

DWORD sizesubject = 0;
std::wstring subjectname;
for (bool getsize = true; ; getsize = false) {
sizesubject = CertGetNameString(
certcontext,
CERT_NAME_FRIENDLY_DISPLAY_TYPE,
0,
NULL,
getsize ? nullptr : &subjectname[0],
sizesubject
);
if (sizesubject == 1) {
std::wcout
<< L"could not get subject name"
<< std::endl;
break;
}
if (getsize) {
subjectname.resize(sizesubject - 1);
} else {
std::wcout
<< L"key name: " << key.key.c_str()
<< L" subject name: " << subjectname.c_str()
<< std::endl;
break;
}
}
}
}

template<typename Stream>
Stream& printSCardState(Stream& stream, DWORD state)
{
stream
<< ((state == SCARD_STATE_UNAWARE) ?
L" SCARD_STATE_UNAWARE" : L"")
<< ((state & SCARD_STATE_PRESENT) ?
L" SCARD_STATE_PRESENT" : L"")
<< ((state & SCARD_STATE_ATRMATCH) ?
L" SCARD_STATE_ATRMATCH" : L"")
<< ((state & SCARD_STATE_CHANGED) ?
L" SCARD_STATE_CHANGED" : L"")
<< ((state & SCARD_STATE_EMPTY) ?
L" SCARD_STATE_EMPTY" : L"")
<< ((state & SCARD_STATE_EXCLUSIVE) ?
L" SCARD_STATE_EXCLUSIVE" : L"")
<< ((state & SCARD_STATE_IGNORE) ?
L" SCARD_STATE_IGNORE" : L"")
<< ((state & SCARD_STATE_INUSE) ?
L" SCARD_STATE_INUSE" : L"")
<< ((state & SCARD_STATE_MUTE) ?
L" SCARD_STATE_MUTE" : L"")
<< ((state & SCARD_STATE_UNAVAILABLE) ?
L" SCARD_STATE_UNAVAILABLE" : L"")
<< ((state & SCARD_STATE_UNKNOWN) ?
L" SCARD_STATE_UNKNOWN" : L"")
<< ((state & SCARD_STATE_UNPOWERED) ?
L" SCARD_STATE_UNPOWERED" : L"")
;
return stream;
}

int wmain(int argc, WCHAR* argv[])
{
unique_winerror winerror;
for (;;) {
SCARDCONTEXT context = NULL;

HANDLE waitfor[] = {SCardAccessStartedEvent()};
ON_UNWIND_AUTO([] {SCardReleaseStartedEvent();});

winerror = smart_card::monitor_smartcard_readers(
[&](SCARDCONTEXT context) {
context = context;
},
[&]() {
context = NULL;
},
[&]() -> bool {
if (WAIT_OBJECT_0 != WaitForMultipleObjects(
lib::rng::size(waitfor),
waitfor,
FALSE,
INFINITE)
) {
// monitor_smardcard_readers will return
// SCARD_E_CANCELLED
return false;
}
return true;
},
[&](
lib::rng::range<SCARD_READERSTATE*> readersrange
) {
for (auto & state : readersrange) {
auto stateChanges = (
(state.dwCurrentState ^ state.dwEventState) &
std::numeric_limits<unsigned short>::max()
);
std::wcout
<< L"nothread - "
<< state.szReader
<< L" changes: "
<< std::hex
<< std::showbase
<< stateChanges
<< L"["
;
printSCardState(std::wcout, stateChanges)
<< L"] state: "
<< std::hex
<< std::showbase
<< state.dwEventState
<< L"["
;
printSCardState(std::wcout, state.dwEventState)
<< L"]"
<< std::endl
;

if (
state.dwCurrentState !=
SCARD_STATE_UNAWARE &&
((state.dwEventState &
SCARD_STATE_PRESENT) !=
SCARD_STATE_PRESENT ||
stateChanges == SCARD_STATE_INUSE ||
stateChanges == SCARD_STATE_UNPOWERED ||
(state.dwEventState & (
SCARD_STATE_UNPOWERED |
SCARD_STATE_EMPTY |
SCARD_STATE_IGNORE |
SCARD_STATE_UNKNOWN |
SCARD_STATE_UNAVAILABLE |
SCARD_STATE_MUTE)) ||
state.cbAtr == 0)
) {
// we have seen this reader before
// and one of:
// no card
// only flipped INUSE
// only flipped UNPOWERED
// UNPOWERED EMPTY UNKNOWN UNAVAILABLE MUTE
// no atr
//
// don't try to read the card
continue;
}

CardWithProvider card;
std::tie(winerror, card) =
smart_card::smartcard_name_and_provider(
lib::rng::make_range_raw(state.rgbAtr)
);
if (!winerror) {
continue;
}

KeyWithCertificateVector certificates;
std::tie(winerror, certificates) =
smart_card::smartcard_certificates(
card.kspname);
if (!winerror) {
continue;
}

std::wcout << L"nothread -"
<< L" kspname: " << card.kspname.c_str()
<< std::endl;

printCertificates(
lib::rng::make_range_raw(certificates)
);
}
}
);
winerror.suppress();
}

return 0;
}