如何获取C++/CX中task内部抛出异常的Message信息
如何获取C++/CX中task任务内部发生的异常对应的消息内容?
问题现象
同步调用场景下可以正常获取异常消息:
IAsyncAction^ Foo() { throw ref new Platform::Exception(-1, "Foo exception …"); } … … try { Foo(); } catch (Exception^ ex) { OutputDebugStringW(ex->Message->Data()); } // Output: "Foo exception …"
但在异步调用场景中,上述获取方式无法正常生效:
IAsyncAction^ Foo_action() { return concurrency::create_async([=] { throw ref new Platform::Exception(-1, "Foo_action exception …"); }); } … … this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this] { concurrency::create_task(Foo_action()) .then([this](concurrency::task<void> previousTask) { try { previousTask.get(); } catch (Platform::Exception^ ex) { OutputDebugStringW(ex->Message->Data()); } }); })); // Output: "The text associated with this error code could not be found."
异步场景下捕获到异常时,ex->HResult属性可以正常保留原有错误码值,但无法读取到自定义的异常Message内容。
原因
Platform::Exception的自定义Message不会跨WinRT ABI(应用程序二进制接口)边界传递。create_async返回的IAsyncAction属于WinRT标准接口,异常从lambda内部抛出后跨线程、跨异步上下文传递时,只会按照COM规范封送HResult错误码,附加的自定义消息字符串属于C++/CX运行时的进程内私有数据,会在封送过程中被丢弃。最终捕获异常时,系统会尝试根据HResult查找系统内置的错误文本,如果传入的是自定义非标准HResult(比如示例中的-1),就会返回“找不到与该错误码关联的文本”的提示。
解决方法
- 不要依赖
Exception->Message跨异步/WinRT边界传递自定义信息。最稳妥的方式是提前维护自定义HResult和错误文本的映射表,捕获异常拿到HResult后,直接查本地映射表获取对应的错误消息即可。 - 如果需要传递动态错误信息,不要让异常直接穿过
create_async生成的WinRT接口边界:可以在create_async的lambda内部先捕获异常,将动态错误消息存在全局/线程本地/自定义错误管理类中,以HResult为索引存储,捕获到异常后再通过HResult取出提前存储的消息。示例逻辑:
// 简单错误消息存储实现示例 ref class ErrorManager sealed { private: static Platform::Collections::Map<int, Platform::String^>^ _hrToMsg = ref new Platform::Collections::Map<int, Platform::String^>(); public: static void SetMsg(int hr, Platform::String^ msg) { _hrToMsg->Insert(hr, msg); } static Platform::String^ GetMsg(int hr) { if (_hrToMsg->HasKey(hr)) return _hrToMsg->Lookup(hr); return "Unknown error"; } }; IAsyncAction^ Foo_action() { return concurrency::create_async([=] { try { throw ref new Platform::Exception(-1, "Foo_action exception …"); } catch (Platform::Exception^ ex) { ErrorManager::SetMsg(ex->HResult, ex->Message); throw; } }); } // 异常捕获处逻辑 catch (Platform::Exception^ ex) { auto msg = ErrorManager::GetMsg(ex->HResult); OutputDebugStringW(msg->Data()); }
- 如果不需要兼容其他WinRT语言调用,也可以全程使用concurrency::task而非
IAsyncAction/IAsyncOperation实现异步逻辑,避免异常穿过WinRT ABI边界,这种情况下PPL任务会在进程内保留完整的异常信息。
内容的提问来源于stack exchange,提问作者isrepeat
相关产品推荐
相关产品推荐

