How to use Winscard API to check Smart Card status Topic is solved

Get help with using AutoHotkey and its commands and hotkeys
tsaiid
Posts: 5
Joined: 14 May 2014, 22:13

How to use Winscard API to check Smart Card status

04 Jan 2017, 18:56

I tried to create a script to check if there is a smart card presence.

As I know, the procedure may include

1. SCardEstablishContext (https://msdn.microsoft.com/zh-tw/librar ... s.85).aspx)
2. SCardListReaders (https://msdn.microsoft.com/en-us/librar ... s.85).aspx)
3. SCardConnect (https://msdn.microsoft.com/zh-tw/librar ... s.85).aspx)
4. SCardStatus (https://msdn.microsoft.com/en-us/librar ... s.85).aspx)

The following code failed even on SCardEstablishContext.

Code: Select all

VarSetCapacity(hSC, 100000, 0)
lReturn := DllCall("Winscard.dll\SCardEstablishContext"
          , "UInt", 0x0000
          , "Ptr", 0,
          , "Ptr", 0,
          , "Ptr", &hSC
          , "UInt")

If (lReturn != 0x00000000) {
  MsgBox, SCardEstablishContext Error
} Else {
  lReturn := DllCall("Winscard.dll\SCardListReaders"
            , "Ptr", &hSC
            , "Str", "SCard$AllReaders"
            , "Ptr", 0
            , "UIntP", cchReaders
            , "UInt")
  VarSetCapacity(mszReaders, cchReaders << !!(A_IsUnicode), 0)
  lReturn := DllCall("Winscard.dll\SCardListReaders"
            , "Ptr", &hSC
            , "Str", "SCard$AllReaders"
            , "Ptr", &mszReaders
            , "UIntP", cchReaders
            , "UInt")

  VarSetCapacity(hCardHandle, 10000, 0)
  VarSetCapacity(dwAP, 10000, 0)
  lReturn := DllCall("Winscard.dll\SCardConnect"
            , "Ptr", &hSC
            , "Str", mszReaders
            , "UInt", 0x0002
            , "UInt", 0x0001 | 0x0002
            , "Ptr", &hCardHandle
            , "Ptr", &dwAP)
}
A strange behavior is that if I pass

Code: Select all

"Ptr", 0
for SCARDCONTEXT in SCardListReaders:

Code: Select all

  lReturn := DllCall("Winscard.dll\SCardListReaders"
            , "Ptr", 0
            , "Str", "SCard$AllReaders"
            , "Ptr", 0
            , "UIntP", cchReaders
            , "UInt")
  VarSetCapacity(mszReaders, cchReaders << !!(A_IsUnicode), 0)
  lReturn := DllCall("Winscard.dll\SCardListReaders"
            , "Ptr", 0
            , "Str", "SCard$AllReaders"
            , "Ptr", &mszReaders
            , "UIntP", cchReaders
            , "UInt")
It could successfully get my smart card reader.
But it does not work in SCardEstablishContext and SCardConnect.
Do you have any idea or suggestion?
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: How to use Winscard API to check Smart Card status

04 Jan 2017, 22:54

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.

VarSetCapacity(hSC, A_PtrSize, 0)
lReturn := DllCall("Winscard.dll\SCardEstablishContext"
          , "UInt", 0x0000
          , "Ptr", 0
          , "Ptr", 0
          , "Ptr", &hSC)

If (lReturn != 0x00000000) {
  MsgBox, SCardEstablishContext Error
} Else {
  cchReaders := -1
  lReturn := DllCall("Winscard.dll\SCardListReaders"
            , A_PtrSize == 8 ? "UInt64" : "UPtr", hSC
            , "Str", "SCard$AllReaders" ; MSDN docs seem to show this string should be double-null terminated, but it (thankfully) seems optional
            , "Ptr*", mszReaders
            , "UInt*", cchReaders)
   readers := []
   if (lReturn == 0x00000000) {
		pReader := mszReaders
		while (*pReader != 0) {
			reader := StrGet(pReader,, A_IsUnicode ? "UTF-16" : "CP0")
			readers.Push(reader)
			MsgBox % reader
			pReader := pReader + (DllCall(A_IsUnicode ? "msvcrt\wcslen" : "msvcrt\strlen", "Ptr", pReader) * (A_IsUnicode + 1)) + (A_IsUnicode + 1)
		}
		lReturn := DllCall("Winscard.dll\SCardFreeMemory", "Ptr", hSC, "Ptr", pmszReaders)
   }
   VarSetCapacity(hCardHandle, A_PtrSize, 0)
   lReturn := DllCall("Winscard.dll\SCardConnect"
            , A_PtrSize == 8 ? "UInt64" : "UPtr", hSC
            , "Str", readers[1]
            , "UInt", 2
            , "UInt", 0x00000001 | 0x00000002
            , "Ptr", &hCardHandle
            , "UInt*", dwAP)
   MsgBox % lReturn
}
works to get the context and the readers' names. SCardConnect fails with return value 6, but I suspect (read: hoping) that that's because the smart card readers I'm dealing with aren't real.
tsaiid
Posts: 5
Joined: 14 May 2014, 22:13

Re: How to use Winscard API to check Smart Card status

04 Jan 2017, 23:07

qwerty12 wrote:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.

VarSetCapacity(hSC, A_PtrSize, 0)
lReturn := DllCall("Winscard.dll\SCardEstablishContext"
          , "UInt", 0x0000
          , "Ptr", 0
          , "Ptr", 0
          , "Ptr", &hSC)

If (lReturn != 0x00000000) {
  MsgBox, SCardEstablishContext Error
} Else {
  cchReaders := -1
  lReturn := DllCall("Winscard.dll\SCardListReaders"
            , A_PtrSize == 8 ? "UInt64" : "UPtr", hSC
            , "Str", "SCard$AllReaders" ; MSDN docs seem to show this string should be double-null terminated, but it (thankfully) seems optional
            , "Ptr*", mszReaders
            , "UInt*", cchReaders)
   readers := []
   if (lReturn == 0x00000000) {
		pReader := mszReaders
		while (*pReader != 0) {
			reader := StrGet(pReader,, A_IsUnicode ? "UTF-16" : "CP0")
			readers.Push(reader)
			MsgBox % reader
			pReader := pReader + (DllCall(A_IsUnicode ? "msvcrt\wcslen" : "msvcrt\strlen", "Ptr", pReader) * (A_IsUnicode + 1)) + (A_IsUnicode + 1)
		}
		lReturn := DllCall("Winscard.dll\SCardFreeMemory", "Ptr", hSC, "Ptr", pmszReaders)
   }
   VarSetCapacity(hCardHandle, A_PtrSize, 0)
   lReturn := DllCall("Winscard.dll\SCardConnect"
            , A_PtrSize == 8 ? "UInt64" : "UPtr", hSC
            , "Str", readers[1]
            , "UInt", 2
            , "UInt", 0x00000001 | 0x00000002
            , "Ptr", &hCardHandle
            , "UInt*", dwAP)
   MsgBox % lReturn
}
works to get the context and the readers' names. SCardConnect fails with return value 6, but I suspect (read: hoping) that that's because the smart card readers I'm dealing with aren't real.
I tried the code above, and with a real smart card reader, SCardConnect still fails with return value 6. Any other suggestion?
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: How to use Winscard API to check Smart Card status

05 Jan 2017, 06:55

tsaiid wrote:I tried the code above, and with a real smart card reader, SCardConnect still fails with return value 6. Any other suggestion?
No, sorry. I should've checked with the C example MS gives on that page first: it returns SCARD_W_REMOVED_CARD, which is appropriate for my configuration, but the AutoHotkey code returns 6, which has ERROR_INVALID_HANDLE as one of its values. According to VS, SCARDCONTEXT is the size of a pointer, so I think I got that right, and if I check, both AutoHotkey and the C example sets the SCARDCONTEXT to 00 00 00 00 01 00 00 cd so I have no reason to believe obtaining the context is the part that is failing. Rather, I guess I'm passing it incorrectly to SCardConnect. I'll reply if I somehow work it out, but I don't really know what I'm doing; hopefully, someone smarter than me chimes in...
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: How to use Winscard API to check Smart Card status  Topic is solved

10 Jan 2017, 00:10

Shit, I'm an idiot. SCardEstablishContext returns a handle, said handle is then used to lookup the actual contextual data from a table internal to Winscard.dll when SCardConnect etc. is called.

DllCall of course does the right thing and makes sure to free the library it loaded after it's done actually calling SCardEstablishContext. Since we don't load Winscard.dll ourselves, and it's not present otherwise, it gets unloaded entirely and the next DllCall to SCardConnect loads up winscard.dll again, which has a clean slate, and our handle means nothing to it.

In short, try this:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.

if (!WinscardMod := DllCall("LoadLibrary", "Str", "Winscard.dll", "Ptr"))
	return

lReturn := DllCall("Winscard.dll\SCardEstablishContext"
          , "UInt", 0x0000
          , "Ptr", 0
          , "Ptr", 0
          , A_PtrSize == 8 ? "UInt64*" : "UInt*", hSC)

If (lReturn != 0x00000000) {
  MsgBox, SCardEstablishContext Error
} Else {
  cchReaders := -1
  lReturn := DllCall("Winscard.dll\SCardListReaders"
            , A_PtrSize == 8 ? "UInt64" : "UInt", hSC
            , "Str", "SCard$AllReaders"
            , "Ptr*", mszReaders
            , "UInt*", cchReaders)
   readers := []
   if (lReturn == 0x00000000) {
		pReader := mszReaders
		; get *all* the card readers' names
		while (*pReader) {
			reader := StrGet(pReader,, A_IsUnicode ? "UTF-16" : "CP0")
			readers.Push(reader)
			MsgBox % reader
			pReader := pReader + (DllCall(A_IsUnicode ? "msvcrt\wcslen" : "msvcrt\strlen", "Ptr", pReader, "Cdecl UInt") * (A_IsUnicode + 1)) + (A_IsUnicode + 1) ; Advance to next null-terminated string to get next card reader name
		}
		lReturn := DllCall("Winscard.dll\SCardFreeMemory", A_PtrSize == 8 ? "UInt64" : "UInt", hSC, "Ptr", mszReaders)
   }

   lReturn := DllCall("Winscard.dll\SCardConnect"
            , A_PtrSize == 8 ? "UInt64" : "UInt", hSC
            , "Str", readers[1]
            , "UInt", 2
            , "UInt", 0x00000001 | 0x00000002
            , A_PtrSize == 8 ? "UInt64*" : "UInt*", hCardHandle
            , "UInt*", dwAP)
   MsgBox % lReturn
   ; Insert appropriate call to SCardDisconnect here if connect succeeded, using UInt64/UInt as above (w/out asterisk) for the handle
   DllCall("Winscard.dll\SCardReleaseContext", A_PtrSize == 8 ? "UInt64" : "UInt", hSC), hSC := -1
}

DllCall("FreeLibrary", "Ptr", WinscardMod), WinscardMod := 0
tornj3

Re: How to use Winscard API to check Smart Card status

16 Apr 2017, 11:46

Is it possible to use Winscard api to get UID from the card?
I am using ACS NFC-reader, and I see on the example on page 11, that I am supposed to send FF CA 00 00 04 [xx xx xx xx] (9000) to the device, to get the uid back.
http://downloads.acs.com.hk/drivers/en/ ... -2.02.pdf
SCardTransmit?
https://msdn.microsoft.com/en-us/librar ... s.85).aspx

Possible?
Drako
Posts: 29
Joined: 14 Jan 2016, 15:08

Re: How to use Winscard API to check Smart Card status

Yesterday, 13:33

Hi, did you ever managed to get the UID? I am trying to do the same!

Return to “Ask For Help”

Who is online

Users browsing this forum: Bing [Bot], boiler, Lem2001, mikeyww, william_ahk and 37 guests