Windows 8 ではスマート カード サービスの動作が異なり、MSDN はドキュメントを更新していません。SCardGetStatusChange を正しく呼び出して Windows 8 でスマート カードの動作を監視する方法について、コード スニペットを提供できる人はいますか? 前もって感謝します!
質問する
6623 次
2 に答える
3
これは、個人のブログ プロジェクト用に作成した C++ テンプレート関数です。私が開発中の github にあるライブラリを使用していますが、ロジックを独自のコンテキストに作り直すこともできます。
template<typename SetContext, typename ClearContext, typename Wait, typename Report>
unique_winerror monitor_smartcard_readers(
SetContext&& setContext,
ClearContext&& clearContext,
Wait&& wait,
Report&& report
)
{
unique_winerror winerror;
std::vector<wchar_t> readernames;
std::vector<SCARD_READERSTATE> readers;
while (winerror)
{
//
// make sure that the scard service has started
// and that the loop has not been cancelled
//
if (!std::forward<Wait>(wait)())
{
return winerror_cast(SCARD_E_CANCELLED);
}
monitor_error_contract(
[&] ()
{
unique_close_scardcontext context;
ON_UNWIND_AUTO(
[&]
{
std::forward<ClearContext>(clearContext)();
}
);
//
// need a fresh context whenever we start over.
// lots of sytem changes could have caused this
// restart including the scard service stopping
// and then restarting.
//
winerror.reset(
SCardEstablishContext(
SCARD_SCOPE_USER,
NULL,
NULL,
context.replace()
)
);
if (!winerror || !context)
{
return;
}
std::forward<SetContext>(setContext)(context.get());
//
// make sure that loop has not been cancelled.
// without this there is a race where the new
// context is not cancelled because the caller
// cancelled at a time when there was no
// context yet.
//
if (!std::forward<Wait>(wait)())
{
winerror = winerror_cast(SCARD_E_CANCELLED);
return;
}
if (readers.empty())
{
//
// add PnP state query
// setting the state to unaware causes SCardGetStatusChange
// to return immediately with the actual pnp state.
//
readers.push_back(make(L"\\\\?PnP?\\Notification"));
}
for(;;)
{
auto readersstaterange = lib::rng::make_range_raw(readers);
winerror.reset(
SCardGetStatusChange(
context.get(),
INFINITE,
readersstaterange.begin(),
lib::rng::size_cast<DWORD>(readersstaterange.size())
)
);
if (!winerror)
{
// exit
return;
}
//
// report changes
//
auto readersrange = lib::rng::make_range_raw(readers, 0, -1);
if (!readersrange.empty())
{
std::forward<Report>(report)(readersrange);
}
//
// record the changes we have reported
//
for (auto& state : readers)
{
state.dwCurrentState = state.dwEventState;
}
if ((readers.back().dwEventState & SCARD_STATE_CHANGED) == SCARD_STATE_CHANGED)
{
// Pnp event - list readers.
break;
}
}
// keep the old allocations for use to build the new list.
std::vector<wchar_t> oldreadernames(std::move(readernames));
std::vector<SCARD_READERSTATE> oldreaders(std::move(readers));
// exclude the pnp reader
auto oldreaderssortedrange = lib::rng::make_range(oldreaders, 0, -1);
LPWSTR concatreaderstrings = nullptr;
ON_UNWIND_AUTO(
[&] { if (concatreaderstrings) {SCardFreeMemory(context.get(), concatreaderstrings);};}
);
DWORD totallength = SCARD_AUTOALLOCATE;
winerror.reset(
SCardListReaders(
context.get(),
nullptr,
reinterpret_cast<LPWSTR>(&concatreaderstrings),
&totallength
)
);
if (winerror == winerror_cast(SCARD_E_NO_READERS_AVAILABLE))
{
// no readers is not an error, loop around to wait
// for a reader to be connected
winerror.suppress().release();
return;
}
else if (!winerror)
{
return;
}
// keep the names around because the state array will have pointers into this
readernames.assign(concatreaderstrings, concatreaderstrings + totallength);
auto readerstateless = [](const SCARD_READERSTATE& lhs, const SCARD_READERSTATE& rhs) -> bool
{
return _wcsicmp(lhs.szReader, rhs.szReader) < 0;
};
//
// all the reader names are concatenated in this array with
// embedded nulls for each and two nulls to mark the end
//
auto cursorreadernames = lib::rng::make_range_raw(readernames);
while(!cursorreadernames.empty() && cursorreadernames.front() != L'\0')
{
// access the current name
auto namerange = lib::rng::make_range(
cursorreadernames,
0,
wcslen(cursorreadernames.begin()) - cursorreadernames.size()
);
// skip to the next name
cursorreadernames = lib::rng::make_range(namerange, namerange.size() + 1, 0);
auto oldreader = std::equal_range(
oldreaderssortedrange.begin(),
oldreaderssortedrange.end(),
make(namerange.begin()),
readerstateless
);
if (oldreader.first != oldreader.second)
{
// keep the old state for this reader
readers.push_back(*oldreader.first);
// must use the new string allocation,
// the old one will be gone soon
readers.back().szReader = namerange.begin();
}
else
{
readers.push_back(make(namerange.begin()));
}
}
// keeping them sorted makes the updates more stable and allows the
// equal_range above instead of a linear find.
std::sort(readers.begin(), readers.end(), readerstateless);
//
// add PnP state query
// keep the existing state, and keep it at the
// end, out of the sorted area.
//
readers.push_back(oldreaders.back());
}
);
}
return winerror;
}
使用法は次のようになります。
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff 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>
// TODO: reference additional headers your program requires here
#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>
int wmain(int argc, WCHAR* argv[])
{
unique_winerror winerror;
for (;;)
{
SCARDCONTEXT context = NULL;
// if you monitor in a separate thread, then add a cancel or shutdown event
// into the waitfor array and handle it in the Wait lambda
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;
}
// read the card in the reader and list the certs on the card
}
}
);
winerror.suppress();
}
return 0;
}
于 2012-12-11T00:45:28.100 に答える
3
私は2年以上遅れていることを知っていますが、それでも私の返信が誰かを助けることができるかもしれません.
さらなる開発の出発点として、いくつかの単純なコードがあります。最初に Windows 7 で作成しました。AFAICS Windows 8 でも問題なく動作します。これは単一のリーダーのみを使用しますが、以前の反復ではリーダーのリストを使用し、同様に機能しました。関連する部分は次のとおりです。
リーダー状態構造の初期化:
memset(&m_State, 0, sizeof(m_State));
m_State.szReader = _wcsdup(m_ReaderName.c_str());
m_State.dwCurrentState = SCARD_STATE_UNAWARE;
イベントを待っています:
bool TSmartCardReader::WaitForEvent(DWORD Timeout, TCardEvent &CardEvent)
{
CardEvent = None;
// Reset reader structure, except the specific fields we need
// (because that's what the docs say: "Important: Each member of each structure
// in this array must be initialized to zero and then set to specific values as
// necessary. If this is not done, the function will fail in situations that
// involve remote card readers.")
const wchar_t *szReader = m_State.szReader;
DWORD dwCurrentState = m_State.dwCurrentState;
memset(&m_State, 0, sizeof(m_State));
m_State.szReader = szReader;
m_State.dwCurrentState = dwCurrentState;
LONG rv = SCardGetStatusChangeW(m_hContext, Timeout, &m_State, 1);
if (rv == SCARD_S_SUCCESS)
{
HandleStatusChange(CardEvent);
// I'm not sure we really need to reset the SCARD_STATE_CHANGED bit
m_State.dwCurrentState = m_State.dwEventState & ~SCARD_STATE_CHANGED;
}
else if (rv == SCARD_E_TIMEOUT)
return false; // No status changes
else if (rv == SCARD_E_NO_READERS_AVAILABLE)
throw ESCNoReaders("No readers available");
else
throw ESCWaitForEvent(GetErrorText(rv));
return CardEvent != None;
}
私がドキュメントを理解している限り、重要なことは、 set dwCurrentState をリーダーの現在の状態であると信じているものに設定することです。SCardGetStatusChange() は、その現在の状態を考慮して、何が状態変化を構成するかを決定します。
于 2014-12-02T09:27:16.570 に答える