NFC Reader, Card Inserted/Ejected watcher

Viewed 304

I'm working on a project that needs NFC reader, Everything work perfect but I have a little issue.I have a method called Watch, This method triggered when card Read by NFC reader and when card ejected.

I call this method on Form load, So when the card on the NFC reader I can do Action automatically, But this method working even when I close the form that loaded the method and it will trigger even when I'm trying to write on the card and do the action That is supposed to do in the other form.

So how can I cancel the event for the method watch method when I want??

This is the Method watch :

public void Watch()
    {
        this.RdrState = new Card.SCARD_READERSTATE();
        readername = GetReadersList()[0];
        this.RdrState.RdrName = readername;

        states = new Card.SCARD_READERSTATE[1];
        states[0] = new Card.SCARD_READERSTATE();
        states[0].RdrName = readername;
        states[0].UserData = 0;
        states[0].RdrCurrState = Card.SCARD_STATE_EMPTY;
        states[0].RdrEventState = 0;
        states[0].ATRLength = 0;
        states[0].ATRValue = null;
        this._worker = new BackgroundWorker();
        this._worker.WorkerSupportsCancellation = true;
        this._worker.DoWork += WaitChangeStatus;
        this._worker.RunWorkerAsync();
    }

And this is The Full Code :

class NFCReader
{
    public int retCode, hCard, Protocol;
    int hContext;
    public bool connActive = false;
    public byte[] SendBuff = new byte[263];
    public byte[] RecvBuff = new byte[263];
    public int SendLen, RecvLen;
    internal enum SmartcardState
    {
        None = 0,
        Inserted = 1,
        Ejected = 2
    }

    public delegate void CardEventHandler();
    public event CardEventHandler CardInserted;
    public event CardEventHandler CardEjected;
    public event CardEventHandler DeviceDisconnected;
    private BackgroundWorker _worker;
    private Card.SCARD_READERSTATE RdrState;
    private string readername;
    private Card.SCARD_READERSTATE[] states;
    private void WaitChangeStatus(object sender, DoWorkEventArgs e)
    {
        while (!e.Cancel)
        {
            int nErrCode = Card.SCardGetStatusChange(hContext, 1000, ref states[0], 1);

            if (nErrCode == Card.SCARD_E_SERVICE_STOPPED)
            {
                DeviceDisconnected();
                e.Cancel = true;
            }

            //Check if the state changed from the last time.
            if ((this.states[0].RdrEventState & 2) == 2)
            {
                //Check what changed.
                SmartcardState state = SmartcardState.None;
                if ((this.states[0].RdrEventState & 32) == 32 && (this.states[0].RdrCurrState & 32) != 32)
                {
                    //The card was inserted. 
                    state = SmartcardState.Inserted;
                }
                else if ((this.states[0].RdrEventState & 16) == 16 && (this.states[0].RdrCurrState & 16) != 16)
                {
                    //The card was ejected.
                    state = SmartcardState.Ejected;
                }
                if (state != SmartcardState.None && this.states[0].RdrCurrState != 0)
                {
                    switch (state)
                    {
                        case SmartcardState.Inserted:
                            {
                                //MessageBox.Show("Card inserted");
                                CardInserted();
                                break;
                            }
                        case SmartcardState.Ejected:
                            {
                                //MessageBox.Show("Card ejected");
                                CardEjected();
                                break;
                            }
                        default:
                            {
                                //MessageBox.Show("Some other state...");
                                break;
                            }
                    }
                }
                //Update the current state for the next time they are checked.
                this.states[0].RdrCurrState = this.states[0].RdrEventState;
            }
        }
    }
    public Card.SCARD_IO_REQUEST pioSendRequest;
    private int SendAPDUandDisplay(int reqType)
    {
        int indx;
        string tmpStr = "";

        pioSendRequest.dwProtocol = Protocol;
        pioSendRequest.cbPciLength = 8;

        //Display Apdu In
        for (indx = 0; indx <= SendLen - 1; indx++)
        {
            tmpStr = tmpStr + " " + string.Format("{0:X2}", SendBuff[indx]);
        }

        retCode = Card.SCardTransmit(hCard, ref pioSendRequest, ref SendBuff[0],
                             SendLen, ref pioSendRequest, ref RecvBuff[0], ref RecvLen);

        if (retCode != Card.SCARD_S_SUCCESS)
        {
            return retCode;
        }

        else
        {
            try
            {
                tmpStr = "";
                switch (reqType)
                {
                    case 0:
                        for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
                        {
                            tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                        }

                        if ((tmpStr).Trim() != "90 00")
                        {
                            //MessageBox.Show("Return bytes are not acceptable.");
                            return -202;
                        }

                        break;

                    case 1:

                        for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
                        {
                            tmpStr = tmpStr + string.Format("{0:X2}", RecvBuff[indx]);
                        }

                        if (tmpStr.Trim() != "90 00")
                        {
                            tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                        }

                        else
                        {
                            tmpStr = "ATR : ";
                            for (indx = 0; indx <= (RecvLen - 3); indx++)
                            {
                                tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                            }
                        }

                        break;

                    case 2:

                        for (indx = 0; indx <= (RecvLen - 1); indx++)
                        {
                            tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                        }

                        break;
                }
            }
            catch (IndexOutOfRangeException)
            {
                return -200;
            }
        }
        return retCode;
    }
    private void ClearBuffers()
    {
        long indx;

        for (indx = 0; indx <= 262; indx++)
        {
            RecvBuff[indx] = 0;
            SendBuff[indx] = 0;
        }
    }
    private bool AuthBlock(String block)
    {
        ClearBuffers();
        SendBuff[0] = 0xFF;                         // CLA
        SendBuff[2] = 0x00;                         // P1: same for all source types 
        SendBuff[1] = 0x86;                         // INS: for stored key input
        SendBuff[3] = 0x00;                         // P2 : Memory location;  P2: for stored key input
        SendBuff[4] = 0x05;                         // P3: for stored key input
        SendBuff[5] = 0x01;                         // Byte 1: version number
        SendBuff[6] = 0x00;                         // Byte 2
        SendBuff[7] = (byte)int.Parse(block);       // Byte 3: sectore no. for stored key input
        SendBuff[8] = 0x61;                         // Byte 4 : Key B for stored key input
        SendBuff[9] = (byte)int.Parse("1");         // Byte 5 : Session key for non-volatile memory

        SendLen = 0x0A;
        RecvLen = 0x02;

        retCode = SendAPDUandDisplay(0);

        if (retCode != Card.SCARD_S_SUCCESS)
        {
            //MessageBox.Show("FAIL Authentication! No:" + retCode.ToString());
            return false;
        }

        return true;
    }
    public string GetCardUID()
    {
        string cardUID = "";
        byte[] receivedUID = new byte[256];
        Card.SCARD_IO_REQUEST request = new Card.SCARD_IO_REQUEST();
        request.dwProtocol = Card.SCARD_PROTOCOL_T1;
        request.cbPciLength = System.Runtime.InteropServices.Marshal.SizeOf(typeof(Card.SCARD_IO_REQUEST));
        byte[] sendBytes = new byte[] { 0xFF, 0xCA, 0x00, 0x00, 0x00 };
        int outBytes = receivedUID.Length;
        int status = Card.SCardTransmit(hCard, ref request, ref sendBytes[0], sendBytes.Length, ref request, ref receivedUID[0], ref outBytes);

        if (status != Card.SCARD_S_SUCCESS)
            cardUID = "";
        else
            cardUID = BitConverter.ToString(receivedUID.Take(4).ToArray()).Replace("-", string.Empty).ToLower();
        return cardUID;
    }
    public List<string> GetReadersList()
    {
        string ReaderList = "" + Convert.ToChar(0);
        int indx;
        int pcchReaders = 0;
        string rName = "";
        List<string> lstReaders = new List<string>();
        //Establish Context
        retCode = Card.SCardEstablishContext(Card.SCARD_SCOPE_USER, 0, 0, ref hContext);

        if (retCode != Card.SCARD_S_SUCCESS)
        {
            throw new Exception("Error SCardEstablishContext");
        }

        // 2. List PC/SC card readers installed in the system

        retCode = Card.SCardListReaders(this.hContext, null, null, ref pcchReaders);

        if (retCode != Card.SCARD_S_SUCCESS)
        {
            throw new Exception("Error SCardListReaders");
        }

        byte[] ReadersList = new byte[pcchReaders];

        // Fill reader list
        retCode = Card.SCardListReaders(this.hContext, null, ReadersList, ref pcchReaders);

        if (retCode != Card.SCARD_S_SUCCESS)
        {
            throw new Exception("Error SCardListReaders");
        }

        rName = "";
        indx = 0;


        while (ReadersList[indx] != 0)
        {

            while (ReadersList[indx] != 0)
            {
                rName += (char)ReadersList[indx];
                indx++;
            }


            lstReaders.Add(rName);
            rName = "";
            indx++;

        }
        return lstReaders;
    }
    /*
    public bool CleanCard(int maxblock)
    {
        int i = 0;
        string clean = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
        while (i < maxblock)
        {
            WriteBlock(clean, (i + 4).ToString());
            i++;
        }
        return true;
    }
    */
    public bool WriteBlock(String Text, String Block)
    {

        char[] tmpStr = Text.ToArray();
        int indx;
        if (AuthBlock(Block))
        {
            ClearBuffers();
            SendBuff[0] = 0xFF;                             // CLA
            SendBuff[1] = 0xD6;                             // INS
            SendBuff[2] = 0x00;                             // P1
            SendBuff[3] = (byte)int.Parse(Block);           // P2 : Starting Block No.
            SendBuff[4] = (byte)int.Parse("16");            // P3 : Data length

            for (indx = 0; indx <= (tmpStr).Length - 1; indx++)
            {
                SendBuff[indx + 5] = (byte)tmpStr[indx];
            }
            SendLen = SendBuff[4] + 5;
            RecvLen = 0x02;

            retCode = SendAPDUandDisplay(2);

            if (retCode != Card.SCARD_S_SUCCESS)
                return false;
            else
                return true;
        }
        else
            return false;
    }
    /*
    public string ReadString() 
    {
        int i = 0;
        string ret="";
        string tmpStr= String.Concat(ReadBlock((i + 4).ToString()));
        ret += String.Concat(tmpStr);
        i++;
        while (!(tmpStr.Contains("\0\0")))
        {
            tmpStr = String.Concat(ReadBlock((i + 4).ToString()));
            ret += tmpStr;
            i++;
        }
        return ret.Replace("\0", string.Empty);

    */
    /*
    public bool WriteString(String Text) 
    {
        string[] parts = Helper.SplitByLength(Text, 16).ToArray();
        double textlen = Text.Length, delim = 16;
        int blocklen = (int)Math.Ceiling(textlen / delim);
        for (int i = 0; i < blocklen; i++)
        {
            if(!WriteBlock(parts[i],(i+4).ToString()))return false;
        }
        return true;
    }
    */
    public byte[] ReadBlock(String Block)
    {
        byte[] tmpStr;
        int indx;

        if (AuthBlock(Block))
        {
            ClearBuffers();
            SendBuff[0] = 0xFF; // CLA 
            SendBuff[1] = 0xB0;// INS
            SendBuff[2] = 0x00;// P1
            SendBuff[3] = (byte)int.Parse(Block);// P2 : Block No.
            SendBuff[4] = (byte)int.Parse("16");// Le

            SendLen = 5;
            RecvLen = SendBuff[4] + 2;

            retCode = SendAPDUandDisplay(2);

            if (retCode == -200)
            {
                return new byte[] { };
            }

            if (retCode == -202)
            {
                return new byte[] { };
            }

            if (retCode != Card.SCARD_S_SUCCESS)
            {
                return new byte[] { };
            }

            // Display data in text format
            List<byte> t = new List<byte>();
            for (indx = 0; indx <= RecvLen - 1; indx++)
            {
                t.Add(RecvBuff[indx]);
            }
            tmpStr = t.ToArray();
            return tmpStr;
        }
        else return new byte[] { };
    }
    public bool Connect()
    {
        string readerName = GetReadersList()[0];
        connActive = true;
        retCode = Card.SCardConnect(hContext, readerName, Card.SCARD_SHARE_SHARED,
                             Card.SCARD_PROTOCOL_T0 | Card.SCARD_PROTOCOL_T1, ref hCard, ref Protocol);
        if (retCode != Card.SCARD_S_SUCCESS)
        {
            connActive = false;
            return false;
        }
        else
            return true;
    }
    public void Disconnect()
    {
        if (connActive)
        {
            retCode = Card.SCardDisconnect(hCard, Card.SCARD_UNPOWER_CARD);
        }
        //retCode = Card.SCardReleaseContext(hCard);
    }
    public string Transmit(byte[] buff)
    {
        string tmpStr = "";
        int indx;

        ClearBuffers();

        for (int i = 0; i < buff.Length; i++)
        {
            SendBuff[i] = buff[i];
        }
        SendLen = 5;
        RecvLen = SendBuff[SendBuff.Length - 1] + 2;

        retCode = SendAPDUandDisplay(2);


        // Display data in text format
        for (indx = 0; indx <= RecvLen - 1; indx++)
        {
            tmpStr = tmpStr + Convert.ToChar(RecvBuff[indx]);
        }

        return tmpStr;
    }
    public void Watch()
    {
        this.RdrState = new Card.SCARD_READERSTATE();
        readername = GetReadersList()[0];
        this.RdrState.RdrName = readername;

        states = new Card.SCARD_READERSTATE[1];
        states[0] = new Card.SCARD_READERSTATE();
        states[0].RdrName = readername;
        states[0].UserData = 0;
        states[0].RdrCurrState = Card.SCARD_STATE_EMPTY;
        states[0].RdrEventState = 0;
        states[0].ATRLength = 0;
        states[0].ATRValue = null;
        this._worker = new BackgroundWorker();
        this._worker.WorkerSupportsCancellation = true;
        this._worker.DoWork += WaitChangeStatus;
        this._worker.RunWorkerAsync();
    }
    public NFCReader()
    {
    }
}
1 Answers

When you are finished with an event handler, you can unhook it with a -=

this._worker.DoWork -= WaitChangeStatus;

If your implemenation is really async, you can unhook the event handler in the event handler itself, and then only hook it back up if you need it again.

private void WaitChangeStatus(object sender, DoWorkEventArgs e)
{
    // unhook event handler
    this._worker.DoWork -= WaitChangeStatus;

    // do stuff . . .
    // this event handler is disabled now, 
    // so the event will not fire anymore if this routine exits early.
    // . . .

    // hook up the event handler because I need it again
    this._worker.DoWork -= WaitChangeStatus;
    this._worker.DoWork += WaitChangeStatus;
}

If you are enabling the event handler, and only want it to fire ONE TIME for an event, make sure it is not already enabled by -= disabling it prior to re-enabling it. I did this in the above code as an example.

If you are setting up this._worker and enabling the event handler in the Form.Load, then it might be good to -= unhook the event handler and dispose and dereference this._worker in the Form.Closing event.

/// Call Unwatch(); in the Form.Closing event handler
public void Unwatch()
{
    this._worker.DoWork -= WaitChangeStatus;
    this._worker.Dispose();
    this._worker = null;
}

Related