2

I'm developing application that is using Mifare Classic 1K card and HID Omnikey 5421 (successor of 5321). I using thread to detect card remove/insert. Delphi code (thread method):

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;

I'm using SendMessage to notify my Smart Card class where I'm detecting proper state. Also I automatically connect and read data from smart card when I detect card insertion.

My application is working correctly for most of the time, but sometimes for e.g. once in the 10000 of card insertion I'm getting SCARD_F_INTERNAL_ERROR from SCardGetStatusChange. When this happen SCardGetStatusChange is starting to result only SCARD_F_INTERNAL_ERROR all the time. When I detected this situation I tried to SCardCancel and SCardReleaseContext, end thread and establish new context and create new watcher thread with this new context but this is not helping because SCardGetStatusChange was continue to returning SCARD_F_INTERNAL_ERROR. Only when I close application and run again problem disappears. It's happening randomly for me, I can't reproduce it using some known scenario. In PC can be more readers, but I'm establishing connection only to Omnikey 5421.

Someone met with this problem?

sliwinski.lukas
  • 1,412
  • 1
  • 18
  • 28

1 Answers1

1

It's hard to say what goes wrong but I have few remarks about your code, hope they help...

  • you should check the return value of the SCardGetStatusChange as the first thing and if it is SCARD_E_TIMEOUT then just skip all the processing and start next cycle;
  • instead of just RStates[0].dwCurrentState := RStates[0].dwEventState; you also have to clear out the SCARD_STATE_CHANGED bit from the state (that is, if the state actually changed);
  • it is my understanding that the resource manager context might become invalid, so before calling SCardGetStatusChange use SCardIsValidContext to make sure you still have good context, if not acquire new one;

So try something like this (this is typed to the browser, so untestead and probably wont compile as is):

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;
ain
  • 22,394
  • 3
  • 54
  • 74