You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用ACR122U阅读器+PCSC库实现VB.net自动读取Mifare Classic标签UID

解决方案:自动检测并读取Mifare Classic UID(.NET 8 WPF)

针对你的需求,我们可以利用PCSC-sharp的事件监听机制实现自动卡片检测,同时简化UID读取代码,避免UI冻结。以下提供C#和VB.NET两种实现方案:

核心改进点

  • 用SCardContext.StatusChanged事件替代轮询/递归,异步监听卡片状态变化
  • 使用PCSC-sharp的异步API(ConnectAsync/TransmitAsync)避免阻塞UI线程
  • 简化APDU命令:使用ACR122U支持的标准UID读取命令FF CA 00 00 00
  • 加入重复UID过滤,避免同一卡片重复触发读取
  • 正确释放资源,防止内存泄漏

C# 实现代码

MainWindow.xaml

<Window x:Class="WpfRfidReader.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="RFID UID Reader" Height="300" Width="400">
    <Grid>
        <TextBlock x:Name="UidTextBlock" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20" Text="等待卡片插入..."/>
    </Grid>
</Window>

MainWindow.xaml.cs

using PCSC;
using PCSC.Iso7816;
using System.Windows;

namespace WpfRfidReader
{
    public partial class MainWindow : Window
    {
        private SCardContext _cardContext;
        private string _lastReadUid = string.Empty;

        public MainWindow()
        {
            InitializeComponent();
            InitializeCardReader();
        }

        private void InitializeCardReader()
        {
            _cardContext = new SCardContext();
            _cardContext.Establish(SCardScope.System);

            // 注册卡片状态变化监听事件
            _cardContext.StatusChanged += CardContext_StatusChanged;

            // 启动阅读器状态监控
            var readers = _cardContext.GetReaders();
            if (readers.Any())
            {
                _cardContext.StartMonitoring(readers);
            }
        }

        private async void CardContext_StatusChanged(object? sender, StatusChangeEventArgs e)
        {
            foreach (var readerState in e.StateChanges)
            {
                // 检测卡片插入事件:当前阅读器存在卡片且ATR有效
                if ((readerState.EventState & SCardState.Present) != 0 && 
                    readerState.Atr != null && readerState.Atr.Length > 0)
                {
                    await ReadCardUidAsync(readerState.ReaderName);
                }
                // 检测卡片移除事件,重置UI显示
                else if ((readerState.EventState & SCardState.Empty) != 0)
                {
                    Dispatcher.Invoke(() =>
                    {
                        UidTextBlock.Text = "等待卡片插入...";
                        _lastReadUid = string.Empty;
                    });
                }
            }
        }

        private async Task ReadCardUidAsync(string readerName)
        {
            try
            {
                using var reader = new SCardReader(_cardContext);
                // 异步连接阅读器
                var connectResult = await reader.ConnectAsync(readerName, SCardShareMode.Shared, SCardProtocol.Any);
                if (connectResult != SCardError.Success)
                {
                    return;
                }

                // 构造简化的UID读取APDU命令:FF CA 00 00 00
                var apdu = new CommandApdu(IsoCase.Case2Short, reader.ActiveProtocol)
                {
                    CLA = 0xFF,
                    Instruction = InstructionCode.GetData,
                    P1 = 0x00,
                    P2 = 0x00,
                    Le = 0x00 // 自动适配UID长度
                };

                // 异步发送APDU命令并获取响应
                var transmitResult = await reader.TransmitAsync(apdu, out ResponseApdu response);
                if (transmitResult != SCardError.Success)
                {
                    return;
                }

                if (response.HasData)
                {
                    // 格式化UID字符串(去除分隔符)
                    var uid = BitConverter.ToString(response.GetData()).Replace("-", "");
                    // 避免重复读取同一卡片
                    if (uid != _lastReadUid)
                    {
                        _lastReadUid = uid;
                        // 回到UI线程更新控件
                        Dispatcher.Invoke(() =>
                        {
                            UidTextBlock.Text = $"UID: {uid}";
                            // 此处添加UID获取后的业务逻辑
                        });
                    }
                }

                // 异步断开连接
                await reader.DisconnectAsync(SCardReaderDisposition.Leave);
            }
            catch (Exception ex)
            {
                Dispatcher.Invoke(() => UidTextBlock.Text = $"读取错误: {ex.Message}");
            }
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            // 释放资源
            _cardContext?.CancelMonitoring();
            _cardContext?.Release();
            _cardContext?.Dispose();
        }
    }
}

VB.NET 实现代码

MainWindow.xaml

同C#版本的XAML代码

MainWindow.xaml.vb

Imports PCSC
Imports PCSC.Iso7816
Imports System.Windows

Class MainWindow
    Private _cardContext As SCardContext
    Private _lastReadUid As String = String.Empty

    Public Sub New()
        InitializeComponent()
        InitializeCardReader()
    End Sub

    Private Sub InitializeCardReader()
        _cardContext = New SCardContext()
        _cardContext.Establish(SCardScope.System)

        ' 注册卡片状态变化监听事件
        AddHandler _cardContext.StatusChanged, AddressOf CardContext_StatusChanged

        ' 启动阅读器状态监控
        Dim readers = _cardContext.GetReaders()
        If readers.Any() Then
            _cardContext.StartMonitoring(readers)
        End If
    End Sub

    Private Async Sub CardContext_StatusChanged(sender As Object, e As StatusChangeEventArgs)
        For Each readerState In e.StateChanges
            ' 检测卡片插入事件
            If (readerState.EventState And SCardState.Present) <> 0 AndAlso 
               readerState.Atr IsNot Nothing AndAlso readerState.Atr.Length > 0 Then
                Await ReadCardUidAsync(readerState.ReaderName)
            End If
            ' 检测卡片移除事件
            If (readerState.EventState And SCardState.Empty) <> 0 Then
                Dispatcher.Invoke(Sub()
                                      UidTextBlock.Text = "等待卡片插入..."
                                      _lastReadUid = String.Empty
                                  End Sub)
            End If
        Next
    End Sub

    Private Async Function ReadCardUidAsync(readerName As String) As Task
        Try
            Using reader = New SCardReader(_cardContext)
                ' 异步连接阅读器
                Dim connectResult = Await reader.ConnectAsync(readerName, SCardShareMode.Shared, SCardProtocol.Any)
                If connectResult <> SCardError.Success Then
                    Return
                End If

                ' 构造UID读取APDU命令
                Dim apdu = New CommandApdu(IsoCase.Case2Short, reader.ActiveProtocol) With {
                    .CLA = &HFF,
                    .Instruction = InstructionCode.GetData,
                    .P1 = &H0,
                    .P2 = &H0,
                    .Le = &H0
                }

                ' 异步发送命令
                Dim response As ResponseApdu = Nothing
                Dim transmitResult = Await reader.TransmitAsync(apdu, response)
                If transmitResult <> SCardError.Success Then
                    Return
                End If

                If response.HasData Then
                    Dim uid = BitConverter.ToString(response.GetData()).Replace("-", "")
                    If uid <> _lastReadUid Then
                        _lastReadUid = uid
                        Dispatcher.Invoke(Sub()
                                              UidTextBlock.Text = $"UID: {uid}"
                                              ' 添加业务逻辑
                                          End Sub)
                    End If
                End If

                Await reader.DisconnectAsync(SCardReaderDisposition.Leave)
            End Using
        Catch ex As Exception
            Dispatcher.Invoke(Sub() UidTextBlock.Text = $"读取错误: {ex.Message}")
        End Try
    End Function

    Protected Overrides Sub OnClosed(e As EventArgs)
        MyBase.OnClosed(e)
        ' 释放资源
        _cardContext?.CancelMonitoring()
        _cardContext?.Release()
        _cardContext?.Dispose()
    End Sub
End Class

关键说明

  1. 避免UI冻结:通过事件驱动+异步API,所有IO操作在后台线程执行,UI线程保持响应
  2. 自动检测卡片:利用SCardContext.StatusChanged事件实时监听阅读器状态变化,无需手动触发
  3. 简化APDU命令:FF CA 00 00 00是ACR122U读取Mifare UID的标准命令,比原代码更简洁高效
  4. 资源管理:窗口关闭时释放SCardContext资源,防止内存泄漏
  5. UI线程安全:使用Dispatcher.Invoke在UI线程更新控件,避免跨线程操作异常

内容的提问来源于stack exchange,提问作者Jake Wynn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 14:07:04