2012-07-02 2 views

답변

2

다음은 개인 블로그 프로젝트 용으로 작성한 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; 
} 
2

내가 늦게 이년 이상 해요 알고,하지만 어쩌면 내 대답은 그럼에도 불구하고 다른 사람을 도울 수 있습니다.

추가 개발을위한 출발점으로 간단한 코드가 있습니다. 나는 Windows 7에서 처음 만들었습니다. AFAICS 그것은 윈도우 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; 
} 

는 키 것은 당신이 당신이 믿는 것에 dwCurrentState을 설정한다는 것입니다 독자의 현재 상태입니다. SCardGetStatusChange()는 현재 상태를 고려하여 상태 변경을 구성하는 요소를 결정합니다.

관련 문제