如何在Windows 8上正确使用SCardGetStatusChange?

2024-05-16

智能卡服务在 Windows 8 上的行为有所不同,并且 MSDN 尚未更新其文档。任何人都可以提供有关如何正确调用 SCardGetStatusChange 来监视 Windows 8 上的智能卡操作的代码片段吗?提前致谢!


这是我为个人博客项目编写的 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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在Windows 8上正确使用SCardGetStatusChange? 的相关文章

随机推荐

  • 将 dll 注册到 GAC 或从 ASP.NET 中的 bin 文件夹引用它们是否更好

    如果答案是 视情况而定 您能否提供一个简短的解释 GAC 旨在包含以下组件跨多个应用程序共享 如果是这种情况 您应该对程序集进行强命名并向 GAC 注册 如果不是 请将程序集保留为私有程序集并将其作为项目 dll 引用进行引用 PS 没有真
  • 我在 Rails 中使用了保留字吗?

    这是我的模型 class Record lt ActiveRecord Base belongs to user belongs to directory end class Directory lt ActiveRecord Base h
  • PHP 用星号替换所有字符

    假设我有一个字符串形式的密码 password thisisaplaintextpassword 我怎样才能把它变成下面的样子 password 我想通过电子邮件向用户发送他们的帐户详细信息 但不想发送整个内容 Use 字符串重复 http
  • 如何使用 Bochs 运行汇编代码?

    我想使用 Bochs 作为 8086 模拟器 是否有捷径可寻 我想要的是类似 emu8086 的东西 http www emu8086 com http www emu8086 com 如果程序的初始部分适合 512 字节 并且您不介意将自
  • JasperReports Server 参数依赖关系

    我有一份有两个输入参数的报告 两者都有一个保管箱 我可以在其中选择值 假设参数A 和参数B 是否可以使 ParameterB 依赖于 ParameterA 例如 如果我在 ParameterS 中选择汽车品牌 Honda 我应该只能看到 H
  • 在 C++ 代码 gdb 中回溯指针

    我在运行 C 应用程序时遇到段错误 在 gdb 中 它显示我的一个指针位置已损坏 但我在应用程序期间创建了 10 万个这样的对象指针 我怎样才能看到导致崩溃的一个 我可以在 bt 命令中执行任何操作来查看该指针的生命周期吗 谢谢 鲁奇 据我
  • 如何将事物的组合映射到关系数据库?

    我有一个表 其记录代表某些对象 为了简单起见 我假设该表只有一列 这是唯一的ObjectId 现在我需要一种方法来存储该表中的对象组合 组合必须是唯一的 但可以是任意长度 例如 如果我有ObjectIds 1 2 3 4 我想存储以下组合
  • 如何对STL向量进行排序?

    我想排序一个vector vector
  • WinForms - 加载表单时如何使用 PaintEventArgs 运行函数?

    我试图理解图形 在 Graphics FromImage 文档中 它有这样的示例 private void FromImageImage PaintEventArgs e Create image Image imageFile Image
  • 使用单独的线程在java中读取和写入文件

    我创建了两个线程并修改了 run 函数 以便一个线程读取一行 另一个线程将同一行写入新文件 这种情况会发生直到整个文件被复制为止 我遇到的问题是 即使我使用变量来控制线程一一执行 但线程的执行仍然不均匀 即一个线程执行多次 然后控制权转移
  • 选择里面的 Include in EF Core

    我有一个如下所示的实体 为简洁起见 部分删除 它包括许多其他属性 public class Tender Key DatabaseGenerated DatabaseGeneratedOption Identity public int I
  • 创建 jar 文件 - 保留文件权限

    我想知道如何创建一个保留其内容的文件权限的 jar 文件 我将源代码和可执行文件打包在一个 jar 文件中 该文件将在使用前提取 人们应该能够通过运行批处理 shell 脚本文件立即运行示例和演示 然后他们应该能够修改源代码并重新编译所有内
  • 如何让 PyC​​harm 始终显示行号

    我似乎无法找到启用行号的设置all文件 但我必须始终右键单击并在每个文件的基础上启用此功能 必须有一个全局设置 对吧 2 6及以上版本 PyCharm 最左侧菜单 gt 首选项 gt 编辑器 左下部分 gt 常规 gt 外观 gt 显示行号
  • 如何将自定义 C 代码放入 SwiftPM 包中?

    我正在尝试将 C 代码打包到 Swift 模块中 我们称之为CModule 一旦我将其放入项目的基本文件夹中 Swift模块 并配置了搜索路径 我可以在 Swift 文件中自动完成工作 并检测错误 警告 问题是 导入时它无法识别该模块 并且
  • Akka-Http 2.4.9 抛出 java.lang.NoClassDefFoundError: akka/actor/ActorRefFactory 异常

    我正在尝试使用 Akka http 构建一个简单的 Web 服务 我遵循了这个指南 http doc akka io docs akka 2 4 9 scala http low level server side api html htt
  • MinGW Make 抛出“系统找不到指定的路径。”错误

    我正在尝试在 Windows 7 上使用 cmake 生成一个 c 项目 在实际创建项目之前 cmake 会对您的工具链进行快速测试 我正在使用 MinGW 这就是我的问题所在 Cmake 触发 make 构建 最终失败并返回 系统找不到指
  • 选择 asp.net CheckBoxList 中的所有项目

    ASP NET 和 C 我想要一个带有 全选 项目的复选框列表 当这个特定项目是 已选择 所有其他都将被选择 也 当选择被删除时 这个项目 也将来自所有人 其他物品 选中 取消选中 任何其他项目只会有一个 对特定项目的影响 无论选择状态如何
  • 当我使用可变参数而不是常量参数时,为什么我的内联表 UDF 慢得多?

    我有一个表值内联 UDF 我想过滤该 UDF 的结果以获得一个特定值 当我使用常量参数指定过滤器时 一切都很好 并且性能几乎是瞬时的 当我使用可变参数指定过滤器时 它会花费明显更大的时间块 大约是逻辑读取的 500 倍和持续时间的 20 倍
  • 测验;这个编译了吗?如果是的话它会返回什么(我知道答案)

    我最近发现这个错字 if name find string npos 显然开发者的意思是输入 if name find string npos 但令我惊讶的是发现错误甚至编译 Wall Werror 没有尝试过 pedantic 那么 咖啡
  • 如何在Windows 8上正确使用SCardGetStatusChange?

    智能卡服务在 Windows 8 上的行为有所不同 并且 MSDN 尚未更新其文档 任何人都可以提供有关如何正确调用 SCardGetStatusChange 来监视 Windows 8 上的智能卡操作的代码片段吗 提前致谢 这是我为个人博