C++/WinRT BLE扫描器调用Start()报非法状态变更错误如何解决
问题解决方案
1. 修复对象生命周期问题
你当前把BluetoothLEAdvertisementWatcher定义为bleConnect函数的局部变量,函数执行完成后对象会立刻被析构,而Start()是异步启动扫描的方法,对象提前销毁会直接触发非法状态异常。
你需要把watcher对象提升为窗口类的成员变量或者全局变量,保证扫描期间对象始终存活:
// 示例:在窗口类头文件中添加成员声明 class CYourDlg : public CDialogEx { // ... 其他原有类成员 private: winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher m_bleWatcher; };
2. 必须先调用权限请求接口再启动扫描
所有Windows蓝牙BLE API调用前,都需要先主动请求访问权限,没有权限的情况下直接调用Start()会固定抛出非法状态异常,修改启动逻辑如下:
winrt::IAsyncAction bleConnect(HWND hWnd) { // 先请求蓝牙访问权限 auto accessStatus = co_await winrt::Windows::Devices::Bluetooth::BluetoothAdapter::RequestAccessAsync(); if (accessStatus != winrt::Windows::Devices::Enumeration::DeviceAccessStatus::Allowed) { wprintf(L"蓝牙权限被拒绝\n"); co_return; } // 使用类成员的m_bleWatcher,禁止使用局部变量 m_bleWatcher.ScanningMode(winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEScanningMode::Active); // 建议至少注册Stopped事件方便排查错误 m_bleWatcher.Stopped([](auto&&, auto&& args) { wprintf(L"扫描停止,错误码: 0x%X\n", args.Error().value); }); // Received事件根据业务需要添加即可,不强制注册 if (m_bleWatcher.Status() == winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcherStatus::Created) { m_bleWatcher.Start(); wprintf(L"扫描启动成功\n"); } }
3. 确认C++/WinRT运行时已正确初始化
MFC/Win32程序使用C++/WinRT前,需要在程序入口处初始化WinRT公寓,建议用多线程公寓适配蓝牙异步API,在项目的InitInstance方法开头添加初始化代码:
BOOL CYourApp::InitInstance() { // 最先执行WinRT初始化 winrt::init_apartment(winrt::apartment_type::multi_threaded); // ... 保留原有MFC初始化逻辑 }
4. 修复manifest配置问题
Win32程序调用UWP蓝牙API需要在manifest里添加必要的声明,调试阶段无需打包也能生效:
- 首先在自定义manifest.xml中添加Windows 10/11兼容性标识,让系统识别程序支持调用WinRT API:
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> </application> </compatibility>
- 再添加蓝牙权限声明:
<Capabilities> <DeviceCapability Name="bluetooth"/> </Capabilities>
- 调试阶段加载manifest的方法:打开VS项目属性 -> 配置属性 -> 链接器 -> 清单文件 -> 附加清单文件,填入你的自定义manifest.xml路径,重新编译后调试会自动加载配置。
额外排查点
如果修改后仍报错,首先检查系统蓝牙开关是否处于开启状态,蓝牙适配器未启用时调用Start()也会抛出相同异常;另外可以打印accessStatus的返回值,确认权限是否真的获取成功。
内容的提问来源于stack exchange,提问作者Thomas T
相关产品推荐
相关产品推荐

