complete_stranger
complete_stranger

Reputation: 908

Respond/react to NFC event/new NFC token

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.

spyplusplus

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

Answers (0)

Related Questions