首页 > 解决方案 > NFC读卡器,卡插入/弹出观察器

问题描述

我正在做一个需要 NFC 读卡器的项目,一切正常,但我有一个小问题。我有一个名为 的方法Watch,该方法在 NFC 读卡器读取卡和卡弹出时触发。

我在表单加载时调用此方法,因此当 NFC 读卡器上的卡时,我可以自动执行操作,但即使我关闭加载该方法的表单,此方法也有效,即使我尝试在卡并执行应该以其他形式执行的操作。

那么如何在需要watch时取消方法方法的事件?

这是方法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();
    }

这是完整的代码:

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()
    {
    }
}

标签: c#

解决方案


完成事件处理程序后,您可以使用-=

this._worker.DoWork -= WaitChangeStatus;

如果您的实现确实是异步的,您可以在事件处理程序本身中取消事件处理程序的挂钩,然后仅在您再次需要时才重新挂钩。

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;
}

-=如果您正在启用事件处理程序,并且只希望它为一个事件触发一次,请在重新启用它之前通过禁用它来确保它尚未启用。我在上面的代码中做了这个作为例子。

如果您在 中设置this._worker和启用事件处理程序Form.Load,那么最好解开事件处理程序并在事件中-=处理和取消引用 。this._workerForm.Closing

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


推荐阅读