我正在开发使用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 。当我检测到这种情况时,我尝试SCardCancel
和SCardReleaseContext
,结束线程并建立新的上下文并使用这个新的上下文创建新的观察者线程,但这没有帮助,因为SCardGetStatusChange继续返回 SCARD_F_INTERNAL_ERROR 即可。只有当我关闭应用程序并再次运行时,问题才会消失。
它随机发生在我身上,我不能用一些已知的场景重现它。在PC中可以有更多读者,但我只与Omnikey 5421建立连接。
有人遇到过这个问题吗?
答案 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;