SCARD_F_INTERNAL_ERROR результат SCardGetStatusChange

Я занимаюсь разработкой приложения, использующего карту 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 вставки карты, я получаю SCARD_F_INTERNAL_ERROR от SCardGetStatusChange, Когда это происходит, SCardGetStatusChange начинает приводить только к SCARD_F_INTERNAL_ERROR все время. Когда я обнаружил эту ситуацию, я попытался SCardCancel а также SCardReleaseContextЗавершите поток, установите новый контекст и создайте новый поток наблюдателя с этим новым контекстом, но это не помогает, поскольку SCardGetStatusChange продолжал возвращать SCARD_F_INTERNAL_ERROR. Только когда я закрываю приложение и запускаю снова, проблема исчезает. Это происходит случайным образом для меня, я не могу воспроизвести его по известному сценарию. В ПК может быть больше читателей, но я устанавливаю соединение только с Omnikey 5421.

Кто-то встречался с этой проблемой?

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;
Другие вопросы по тегам