This is a follow up article to an earlier article I wrote called Communication with a smart card from PowerBuilder. In that article I showed how to interact with a smart card once it was inserted in the reader. In this article, we're going to look at how we can monitor the card reader to determine when the user inserts or removes a smart card from it.
This article assumes you have the code in place from that earlier article. In particular, it assumes that you've got the code in place to establish the context for the API calls, read a list of the available smart card readers and close the context once we're done interacting with the readers.
The particular function we're going to call is SCardGetStatusChange. However, that function takes an array of a structure called SCARD_READERSTATE, so we're going to declare a structure in PowerBuilder to match that first. That structure looks like this:
global type s_readerstate from structure string reader unsignedlong userdata unsignedlong currentstate unsignedlong eventstate unsignedlong atr byte rgbatr end type
Now that we have the structure defined, we can declare our local external function:
Function ulong SCardGetStatusChange( & ulong hContext, & ulong dwTimeout, & ref s_readerstate rgReaderStates, & ulong pcchReaders & ) Library "winscard.dll" Alias for "SCardGetStatusChangeW"
Next, let's look at the code that calls it. We're assuming here that we've already established the API context and obtained a list of readers on the system from the code in the previous article.
integer i long ll_count ulong rc s_readerstate ls_readerstate ll_count = UpperBound ( readers ) FOR i = 1 TO ll_count ls_readerstate[i].reader = readers[i] ls_readerstate[i].currentstate = 0 ls_readerstate[i].atr = 36 NEXT rc = SCardGetStatusChange( context, 0, ls_readerstate, ll_count) ;
Some things to note:
- The ls_readerstate represents the array of readers that you want to monitor the status of. As written, this code will monitor every reader currently installed on the system. This same function can also monitor to determine if the user adds a new reader to the system, but I'm not going to demonstrate that as part of this article.
- You have some options with regard to the CurrentState parameter on the structure. If you know the state of the reader (perhaps from a previous call) you can pass that in and the return from the function call will tell you if the status changed. What I'm doing here is passing SCARD_STATE_UNAWARE (0) to indicate I don't know the state of the reader and so the return will always indicate there is a change and give me the current status. I'm assuming here that you will be saving off the status you are interested in and detecting the change within the PowerBuilder code.
- The atr attribute indicates the size of the buffer being passed in the rgbatr attribute. This should always be 36.
- The second parameter to the API call is the duration in milliseconds that the call should wait looking for a status change. I've set this to 0 so that we get an intermediate return. I'm assuming here that you'll add the code to an event/function triggered by a timer call so that it runs on a regular basis.
As this code is written, the eventstate attribute of the structure will tell you the current state of the reader. Here's an example from my system:
The eventstate value needs to be interpreted as an 8 character hexidecimal value. The first half of the value (first 4 characters) is a count, the meaning of which depends how the call was made. For our purposes, we don't care about that value. The second half (last 4 characters) contains the status information we're looking for. More details, including the values for the flags for the second half of the value can be found at: Reader State.
Looking at these two specific values, the one for the first reader is 131106. In hexidecimal that is 0002 0022. The first half (0002 is the count which we can ignore. The second half (0022) is the sum of SCARD_STATE_CHANGED (2) which we will always get as this code is written and SCARD_STATE_PRESENT (20) which indicates that there is a smart card installed in that reader. The value for the second reader is 65554. In hexidecimal that is 0001 0012. Once again we can ignore the first half of the value (0001). The second half (0012) is once again composed of SCARD_STATE_CHANGED (2) and another value, this time SCARD_STATE_EMPTY (10) which shows that reader is empty.
What I'm not showing here is parsing the values, tracking the status of each reader, and using a timer event or timer object to re-evaluate the status on a regular basis. I'm assuming the reader can take it from here.