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;