2

我正在开发使用 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. 当这种情况发生时,SCardGetStatusChange 开始一直只产生SCARD_F_INTERNAL_ERROR。当我检测到这种情况时,我尝试结束线程SCardCancelSCardReleaseContext建立新上下文并使用这个新上下文创建新的观察者线程,但这没有帮助,因为 SCardGetStatusChange 继续返回SCARD_F_INTERNAL_ERROR。只有当我关闭应用程序并再次运行时,问题才会消失。它对我来说是随机发生的,我无法使用某些已知场景重现它。在 PC 中可以有更多的读者,但我只建立与 Omnikey 5421 的连接。

有人遇到过这个问题吗?

4

1 回答 1

1

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

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

所以尝试这样的事情(这是在浏览器中输入的,所以无法测试并且可能不会按原样编译):

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;
于 2015-05-27T16:31:03.203 回答