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

.NET 7 WinForms应用中BluetoothLEAdvertisementWatcher触发ObjectDisposedException

.NET 7 WinForms BLE广播对话框关闭后间歇性ObjectDisposedException异常分析与解决

异常现象

在Win11系统的.NET 7 WinForms应用中,关闭显示BLE广播的模态对话框后,会间歇性抛出System.ObjectDisposedException,且广播设备的频率越高,异常触发概率越大。已将对话框设为应用生命周期内持久化(不随关闭销毁对象),但问题仍存在。

核心原因

问题本质是线程竞态条件:

  1. BluetoothLEAdvertisementWatcher的Received事件在后台线程触发,事件处理逻辑会通过this.Invoke切换到UI线程更新控件。
  2. 关闭对话框时,在FormClosing中调用deviceWatcher.Stop()并移除事件,但此时系统可能还有已触发但尚未处理的Received事件在后台队列中等待执行。
  3. 模态对话框关闭后,即使对象本身未被Dispose,其窗口句柄会被WinForms销毁(模态对话框的生命周期特性),此时后台线程中的this.Invoke尝试访问已失去句柄的窗体,就会抛出ObjectDisposedException。

合理解决方案

方案一:同步等待后台事件处理完成

通过线程安全的计数和同步信号量,确保在窗体关闭前,所有正在处理的Received事件都执行完毕,避免后续无效的UI调用。

修改后的对话框代码:

using System.Diagnostics;
using Windows.Devices.Bluetooth.Advertisement;
using System.Threading;

namespace TestBle
{
    public partial class WatcherCrashTest : Form
    {
        private BluetoothLEAdvertisementWatcher deviceWatcher;
        private ManualResetEventSlim _eventProcessingComplete = new ManualResetEventSlim(true);
        private int _activeEventCount = 0;

        public WatcherCrashTest()
        {
            Debug.WriteLine("Dialog constructor");
            InitializeComponent();
            deviceWatcher = new BluetoothLEAdvertisementWatcher();
        }

        private void WatcherCrashTest_Shown(object sender, EventArgs e)
        {
            Debug.WriteLine("Dialog Shown");
            deviceWatcher.Received += deviceWatcher_Received;
            deviceWatcher.Start();
        }

        private void WatcherCrashTest_FormClosing(object sender, FormClosingEventArgs e)
        {
            Debug.WriteLine("Dialog FormClosing");
            if (deviceWatcher != null)
            {
                deviceWatcher.Stop();
                deviceWatcher.Received -= deviceWatcher_Received;
                // 等待所有正在处理的后台事件完成
                _eventProcessingComplete.Wait();
            }
        }

        private void deviceWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
        {
            // 线程安全标记事件开始处理
            Interlocked.Increment(ref _activeEventCount);
            _eventProcessingComplete.Reset();

            try
            {
                String BtDeviceDetails = "0x" + args.BluetoothAddress.ToString("X8") + ": " + args.Advertisement.LocalName;
                Debug.WriteLine(BtDeviceDetails);

                // 提前检查窗体状态,避免无效操作
                if (!this.IsHandleCreated || this.IsDisposed)
                {
                    return;
                }

                this.Invoke(new Action(() =>
                {
                    // 再次检查控件状态
                    if (!listBox1.IsDisposed)
                    {
                        listBox1.Items.Add(BtDeviceDetails);
                    }
                }));
            }
            finally
            {
                // 线程安全标记事件处理完成
                if (Interlocked.Decrement(ref _activeEventCount) == 0)
                {
                    _eventProcessingComplete.Set();
                }
            }
        }
    }
}

方案二:前置检查窗体句柄状态

在调用Invoke前,先验证窗体的句柄是否存在,直接跳过无效的UI更新操作,避免触发异常。这种方案更轻量,适合不需要等待所有事件完成的场景:

修改deviceWatcher_Received方法:

private void deviceWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
    String BtDeviceDetails = "0x" + args.BluetoothAddress.ToString("X8") + ": " + args.Advertisement.LocalName;
    Debug.WriteLine(BtDeviceDetails);

    // 检查窗体是否还有有效句柄
    if (!this.IsHandleCreated || this.IsDisposed)
    {
        return;
    }

    // 使用BeginInvoke避免阻塞后台线程,同时在委托内再次检查控件状态
    this.BeginInvoke(new Action(() =>
    {
        if (!listBox1.IsDisposed)
        {
            listBox1.Items.Add(BtDeviceDetails);
        }
    }));
}

临时方案的弊端说明

你之前找到的两种临时方案存在潜在问题:

  • Application.DoEvents():强制处理消息队列中的所有未处理消息,可能导致意外的重入逻辑(比如重复触发窗体关闭事件),违背WinForms的消息循环设计原则。
  • 使用主窗体Invoke:虽然能绕过对话框的异常,但逻辑上不合理(更新对话框控件却依赖主窗体),且如果主窗体后续也被关闭,同样会触发类似异常。

内容的提问来源于stack exchange,提问作者Stephen Done

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 21:30:50