Reputation: 908
I am looking for an event or something similar to react to a new NFC token using the WinAPI
.
I've tried my luck with SCardAccessStartedEvent
but was unsuccessful. It's triggered twice but not on a new NFC token.
I have also tried it with WM_DEVICECHANGE
. It somehow works but not to my understanding. Msg.WParam
is neither DBT_DEVICEARRIVAL
nor DBT_DEVICEREMOVECOMPLETE
.
This is Msg
of WM_DEVICECHANGE
during debug time:
Msg (537, 7, 0, 0, 7, 0, (), 0, 0, (), 0, 0, ())
Msg 537
WParam 7
LParam 0
Result 0
WParamLo 7
WParamHi 0
WParamFiller ()
LParamLo 0
LParamHi 0
LParamFiller ()
ResultLo 0
ResultHi 0
ResultFiller ()
How did I come across WM_DEVICECHANGE
?
I have an ACR1252U. They provide utility tools like ACS QuickView v2.13 (For Windows). I played with this tool a little bit and had a look with Spy++. Everytime a new nfc token arrived a 'WM_DEVICECHANGE' is fired.
The code in BReadManuallyClick
works perfectly fine.
WinSCard, SCardErr, WinSmCrd
come from project JEDI.
This is my code:
unit MainU;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
WinSCard, SCardErr, WinSmCrd, Vcl.ExtCtrls;
type
TEventThread = class(TThread)
private
FEventHandle: THandle;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
end;
TMainF = class(TForm)
BReadManually: TButton;
MOutput: TMemo;
BStartEventThread: TButton;
PButtons: TPanel;
procedure BReadManuallyClick(Sender: TObject);
procedure BStartEventThreadClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
private
{ Private declarations }
eventthread: TEventThread;
procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
public
{ Public declarations }
end;
var
MainF: TMainF;
implementation
{
#define DBT_DEVICEARRIVAL 0x8000 // system detected a new device
#define DBT_DEVICEQUERYREMOVE 0x8001 // wants to remove, may fail
#define DBT_DEVICEQUERYREMOVEFAILED 0x8002 // removal aborted
#define DBT_DEVICEREMOVEPENDING 0x8003 // about to remove, still avail.
#define DBT_DEVICEREMOVECOMPLETE 0x8004 // device is gone
#define DBT_DEVICETYPESPECIFIC 0x8005 // type specific event
#define DBT_CUSTOMEVENT 0x8006 // user-defined event
}
const
DBT_DEVICEARRIVAL = $8000; // system detected a new device
DBT_DEVICEQUERYREMOVE = $8001; // wants to remove, may fail
DBT_DEVICEQUERYREMOVEFAILED = $8002; // removal aborted
DBT_DEVICEREMOVEPENDING = $8003; // about to remove, still avail.
DBT_DEVICEREMOVECOMPLETE = $8004; // device is gone
DBT_DEVICETYPESPECIFIC = $8005; // type specific event
DBT_CUSTOMEVENT = $8006; // user-defined event
{$R *.dfm}
procedure TMainF.BReadManuallyClick(Sender: TObject);
var
scc: SCardContext;
readers, debug: integer;
name: array of WideChar;
cardhandle: SCardHandle;
activeprotocol: DWORD;
request: WinSCard.PSCARD_IO_REQUEST;
cmdread: array[0..4] of Byte;
recvbuff: array[0..511] of Byte;
//offset: word;
begin
//offset := 0;
if (SCardEstablishContext(SCARD_SCOPE_SYSTEM, nil, nil, @scc) <> SCARD_S_SUCCESS) then
RaiseLastOSError;
if (SCardListReadersW(scc, nil, nil, readers) <> SCARD_S_SUCCESS) then
RaiseLastOSError;
SetLength(name, readers);
if SCardListReadersW(scc, nil, PWideChar(name), readers) <> SCARD_S_SUCCESS then
RaiseLastOSError;
debug := SCardConnectW(scc, PWideChar(name), SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 OR SCARD_PROTOCOL_T1, cardhandle, @activeprotocol);
if (debug <> SCARD_S_SUCCESS) then
RaiseLastOSError
else begin
request := Getg_rgSCard(activeprotocol);
cmdread[0] := $00;
cmdread[1] := $00;
cmdread[2] := $00;
cmdread[3] := $00;
cmdread[4] := $00;
try
if (SCardTransmit(cardhandle, request, PByte(@cmdread[0]), SizeOf(cmdread), nil, PByte(@recvbuff[0]), @recvbuff) <> SCARD_S_SUCCESS) then
RaiseLastOSError;
finally
SCardDisconnect(cardhandle, SCARD_LEAVE_CARD);
end;
end;
end;
procedure TMainF.BStartEventThreadClick(Sender: TObject);
begin
eventthread := TEventThread.Create;
eventthread.Start;
end;
procedure TMainF.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
eventthread.Terminate;
CanClose := eventthread.Terminated;
end;
procedure TMainF.WMDeviceChange(var Msg: TMessage);
begin
case Msg.WParam of
DBT_DEVICEARRIVAL:
MOutput.Lines.Add('Device arrived');
DBT_DEVICEREMOVECOMPLETE:
MOutput.Lines.Add('Device removed');
end;
end;
{ TEventThread }
constructor TEventThread.Create;
begin
inherited Create(True);
FEventHandle := SCardAccessStartedEvent;
if FEventHandle = 0 then
raise Exception.Create('Failed to access started event');
FreeOnTerminate := True;
end;
destructor TEventThread.Destroy;
begin
inherited Destroy;
end;
procedure TEventThread.Execute;
begin
while not Terminated do
begin
if WaitForSingleObject(FEventHandle, INFINITE) = WAIT_OBJECT_0 then
begin
Synchronize(procedure
begin
ShowMessage('Event occurred!');
end);
SCardReleaseStartedEvent;
end;
end;
end;
end.
Upvotes: 0
Views: 73