4

I'm trying to detect when a card has been inserted into a reader. If I do a nasty polling loop like this:

      public struct SCARD_READERSTATE
        {
            [MarshalAs(UnmanagedType.LPWStr)]
            public string szReader;
            public byte[] pvUserData;
            public byte[] rgbAtr;
            public uint dwCurrentState;
            public uint dwEventState;
            public uint cbAtr;
        }

   byte[] atr = null;
   SCARD_READERSTATE[] rs = new SCARD_READERSTATE[1];
   rs[0].szReader = readersList[0];
   rs[0].dwCurrentState = SCARD_STATE_UNAWARE;
   rs[0].dwEventState = SCARD_STATE_PRESENT;
   int hctx = hContext.ToInt32();
   var cardResult = SCardGetStatusChange(hctx, 100, rs, 1);
   if (cardResult == 0 && rs[0].cbAtr > 0 && rs[0].rgbAtr != null)
   {
       atr = new byte[rs[0].cbAtr];
       Array.Copy(rs[0].rgbAtr, atr, rs[0].cbAtr);
   }

  while ( (rs[0].dwCurrentState & SCARD_STATE_PRESENT) == 0)
  {
       rs = new SCARD_READERSTATE[1];
       rs[0].szReader = readersList[0];
       //rs[0].dwCurrentState = SCARD_STATE_PRESENT;
       //rs[0].dwEventState = SCARD_STATE_PRESENT;
       SCardGetStatusChange(hctx, 100000000, rs, 1);
       System.Threading.Thread.Sleep(1000);
  }

it works, but it has a nasty thread sleep in it. Ideally I'd like to make a blocking call to SCardGetStatusChange on a background thread and then surface up the events.

Apparently by setting the szReader to the value "\\?PnP?\Notification" it should block, as long as everything else in the struct is 0.

I've changed the code to

   rs[0].szReader = "\\\\?PnP?\\Notification";
   rs[0].cbAtr = 0;
   rs[0].dwCurrentState = 0;
   rs[0].dwEventState = 0;
   rs[0].pvUserData = new byte[0];
   rs[0].rgbAtr = new byte0];
   SCardGetStatusChange(hctx, 100000000, rs, 1);

but it just returns a success result immediately. Can any pInvoke masters out there see what's wrong?

Dylan
  • 1,306
  • 1
  • 11
  • 29

1 Answers1

5
  1. In your sample the second call to SCardGetStatusChange should block if you copy dwEventState into dwCurrentState and then reset dwEventState, so there's no need for the sleep.

  2. The "\\?PnP?\Notification" struct is to tell you when a new smart card reader has been attached, not when a card has been inserted. From the MSDN page on SCardGetStatusChange:

    To be notified of the arrival of a new smart card reader, set the szReader member of a SCARD_READERSTATE structure to "\\?PnP?\Notification", and set all of the other members of that structure to zero.

  3. When using the "\\?PnP?\Notification" struct:

    • the pvUserData and rgbAttr fields should be set to null
      • a new byte[0] is a valid pointer to a zero length array, but what the API needs here is null pointers or zero values)
    • the high 16 bits of dwCurrentState should contain the current reader count
      • i.e. rs[0].dwCurrentState = (readerCount << 16);
      • the MSDN page is currently inaccurate on this point.
IanS
  • 191
  • 3