来自SCardGetStatusChange

时间:2015-05-27 14:46:54

标签: delphi smartcard pcsc winscard

我正在开发使用Mifare Classic 1K卡和HID Omnikey 5421(5321的后继者)的应用程序。我使用线程检测卡删除/插入。 Delphi代码(线程方法):

function CardWatcherThread(PContext: Pointer): integer;
var
  RetVar   : cardinal;
  RContext : cardinal;
  RStates  : array[0..0] of SCARD_READERSTATEA;
begin
  try
    RContext := Cardinal(PContext^);
    FillChar(RStates,SizeOf(RStates),#0);
    RStates[0].szReader       := SelectedReader;
    RStates[0].pvUserData     := nil;
    RStates[0].dwCurrentState := SCARD_STATE_UNAWARE;
    while ReaderOpen and (not Application.Terminated) do begin
      RetVar := SCardGetStatusChange(RContext, MAX_WAIT_TIME_SCARDSTATUSCHANGE, @RStates, 1);
      RStates[0].dwCurrentState := RStates[0].dwEventState;
      ActReaderState := RStates[0].dwEventState;

      // Avoid sedning error about timemout if MAX_WAIT_TIME_SCARDSTATUSCHANGE is not infinite
      if (RetVar <> SCARD_E_TIMEOUT) or (MAX_WAIT_TIME_SCARDSTATUSCHANGE = -1) then begin
        SendMessage(NotifyHandle, WM_CARDSTATE, RetVar, 0);
      end;
    end;
  finally
    Result := 0;
  end;
end;

我正在使用SendMessage通知我的智能卡类,我正在检测正确的状态。当我检测到卡插入时,我也会自动连接并读取智能卡中的数据。

我的应用程序在大多数情况下都能正常工作,但有时也适用于一旦插入10000张卡片,我就会从SCardGetStatusChange获得 SCARD_F_INTERNAL_ERROR 。发生这种情况时,SCardGetStatusChange始终只开始 SCARD_F_INTERNAL_ERROR 。当我检测到这种情况时,我尝试SCardCancelSCardReleaseContext,结束线程并建立新的上下文并使用这个新的上下文创建新的观察者线程,但这没有帮助,因为SCardGetStatusChange继续返回 SCARD_F_INTERNAL_ERROR 即可。只有当我关闭应用程序并再次运行时,问题才会消失。  它随机发生在我身上,我不能用一些已知的场景重现它。在PC中可以有更多读者,但我只与Omnikey 5421建立连接。

有人遇到过这个问题吗?

1 个答案:

答案 0 :(得分:1)

很难说出现了什么问题,但我对你的代码几乎没有评论,希望他们有所帮助......

  • 您应首先检查SCardGetStatusChange的返回值,如果是SCARD_E_TIMEOUT,则只需跳过所有处理并开始下一个周期;
  • 而不仅仅是RStates[0].dwCurrentState := RStates[0].dwEventState;你还需要清除状态中的SCARD_STATE_CHANGED位(即,如果状态实际发生了变化);
  • 我的理解是资源管理器上下文可能变得无效,因此在调用SCardGetStatusChange之前使用SCardIsValidContext确保您仍然拥有良好的上下文,如果没有获得新的上下文;

所以尝试类似这样的东西(这是在浏览器中输入的,所以untestead并且可能不会按原样编译):

function CardWatcherThread(PContext: Pointer): integer;
var
  RetVar   : cardinal;
  RContext : cardinal;
  RStates  : array[0..0] of SCARD_READERSTATEA;
begin
  try
    RContext := Cardinal(PContext^);
    FillChar(RStates,SizeOf(RStates),#0);
    RStates[0].szReader       := SelectedReader;
    RStates[0].pvUserData     := nil;
    RStates[0].dwCurrentState := SCARD_STATE_UNAWARE;
    while ReaderOpen and (not Application.Terminated) do begin
      if(SCardIsValidContext(RContext) <> SCARD_S_SUCCESS)then begin
         RetVal := SCardEstablishContext(...);
      end;
      RetVar := SCardGetStatusChange(RContext, MAX_WAIT_TIME_SCARDSTATUSCHANGE, @RStates, 1);
      case RetVal of
        SCARD_E_TIMEOUT:;
        SCARD_S_SUCCESS: begin
           if((RStates[0].dwEventState and SCARD_STATE_CHANGED) <> 0)then begin
              RStates[0].dwCurrentState := RStates[0].dwEventState xor SCARD_STATE_CHANGED;
              // reader's state changed, do something
           end;
        end;
      end;
    end;
  finally
    Result := 0;
  end;
end;