C++ WinRT中无法获取Windows::Foundation::IAsyncOperation异步结果求助
解决WinRT异步操作"call at unexpected time"及GetResults返回nullptr的问题
我来帮你拆解一下你遇到的两个核心问题:异步操作调用时机异常,以及回调中GetResults返回nullptr的问题,咱们一步步来解决。
一、先修正你的Completed回调代码
你当前的回调实现存在几个小问题,这可能是导致GetResults返回nullptr的原因之一:
- 错误使用捕获的
async_op而非回调参数operation
捕获的async_op是函数局部变量,函数执行完毕后可能已经被销毁,或者状态失效。正确的做法是直接用回调参数里的operation来获取结果。 - 优先检查回调传入的
status而非操作自身的Status
Completed回调的status参数就是当前异步操作的最终状态,用它判断操作是否成功完成更可靠。
修正后的回调代码如下:
void BleEnumeration::PerformConnectDevice(std::string* bleDevId) { std::wstring bleDevId_w_str((*bleDevId).begin(), (*bleDevId).end()); Platform::String^ bleDevId_refStr = ref new Platform::String(bleDevId_w_str.c_str()); auto async_op = Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(bleDevId_refStr); async_op->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<Windows::Devices::Bluetooth::BluetoothLEDevice^>( [] (Windows::Foundation::IAsyncOperation<Windows::Devices::Bluetooth::BluetoothLEDevice^>^ operation, Windows::Foundation::AsyncStatus status) { if (status == Windows::Foundation::AsyncStatus::Completed) { auto bleDevTest = operation->GetResults(); if (bleDevTest != nullptr) { // 成功获取到设备,执行后续逻辑 } else { // 获取失败,提取错误码排查原因 HRESULT hr = operation->ErrorCode.Value; // 比如hr为ERROR_ACCESS_DENIED代表权限不足,ERROR_DEVICE_NOT_AVAILABLE代表设备不可达 } } else if (status == Windows::Foundation::AsyncStatus::Error) { HRESULT hr = operation->ErrorCode.Value; // 统一处理错误逻辑,比如打印错误码或提示用户 } else if (status == Windows::Foundation::AsyncStatus::Canceled) { // 处理操作被取消的情况 } }); }
二、解决co_await/std::future调用时的"call at unexpected time"异常
你之前用co_await或者task调用GetResults时触发异常,大概率是没有在正确的线程上下文执行异步操作。WinRT的异步操作需要合适的调度器(比如UI线程调度器,或者手动初始化的调度器)来处理延续逻辑。
正确使用co_await的姿势
如果你的项目支持C++/WinRT(VS2017需要安装对应组件),可以用更简洁的co_await方式,记得确保线程已经初始化WinRT上下文:
#include <winrt/Windows.Devices.Bluetooth.h> #include <winrt/Windows.Foundation.h> using namespace winrt; using namespace Windows::Devices::Bluetooth; using namespace Windows::Foundation; IAsyncAction BleEnumeration::PerformConnectDeviceAsync(std::string bleDevId) { // 初始化WinRT上下文(如果线程未初始化过) winrt::init_apartment(); std::wstring bleDevId_w_str(bleDevId.begin(), bleDevId.end()); winrt::hstring bleDevId_hstr(bleDevId_w_str); // co_await会自动处理线程上下文切换,等待异步操作完成 auto bleDev = co_await BluetoothLEDevice::FromIdAsync(bleDevId_hstr); if (bleDev != nullptr) { // 成功获取设备,执行后续逻辑 } else { // 获取失败,提取错误码 auto error = winrt::get_last_error(); // 根据错误码做对应处理 } }
为什么之前的task/std::future方式出错?
如果用的是C++/CX,使用create_task时需要指定延续逻辑的上下文,比如用task_continuation_context::use_current()确保延续在当前支持WinRT的线程执行,避免在不兼容的原生线程中触发异常。
三、GetResults返回nullptr的常见原因排查
即使回调逻辑正确,GetResults返回nullptr通常是以下场景导致的:
- 设备ID无效:检查你传入的
bleDevId转换为宽字符后是否正确,可通过BluetoothLEDevice::GetDeviceSelector获取设备列表验证ID有效性。 - 权限不足:必须在应用清单(Package.appxmanifest)中声明蓝牙相关权限:
- 添加
<Capability Name="bluetooth"/>基础蓝牙权限 - 如果需要访问GATT服务,还要添加
<Capability Name="bluetooth.genericAttributeProfile"/>
- 添加
- 设备不可达:设备已断开连接或超出蓝牙范围。
- 设备被占用:其他应用或进程已经占用了该蓝牙设备。
四、关于async_op的生命周期问题
你尝试用shared_ptr保存async_op无效,是因为WinRT对象用^智能指针自动管理引用计数,只要回调被注册,系统会持有async_op的引用直到操作完成。所以问题不在生命周期,而是前面提到的逻辑或环境问题。
内容的提问来源于stack exchange,提问作者l.moreno
相关产品推荐
相关产品推荐

