6 c++ winapi exception message-queue
我正在编写一个使用单个对话框的(C++)应用程序.在设置了消息泵和处理程序后,我开始想知道如何将C++异常传播到我的原始代码(CreateDialogParam例如,调用的代码).
这是我的意思的骨架示例:
BOOL CALLBACK DialogProc(HWND, UINT msg, WPARAM, LPARAM)
{
if(msg == WM_INITDIALOG) //Or some other message
{
/*
Load some critical resource(s) here. For instnace:
const HANDLE someResource = LoadImage(...);
if(someResource == NULL)
{
---> throw std::runtime_error("Exception 1"); <--- The exception handler in WinMain will never see this!
Maybe PostMessage(MY_CUSTOM_ERROR_MSG)?
}
*/
return TRUE;
}
return FALSE;
}
//======================
void RunApp()
{
const HWND dlg = CreateDialog(...); //Using DialogProc
if(dlg == NULL)
{
throw std::runtime_error("Exception 2"); //Ok, WinMain will see this.
}
MSG msg = {};
BOOL result = 0;
while((result = GetMessage(&msg, ...)) != 0)
{
if(result == -1)
{
throw std::runtime_error("Exception 3"); //Ok, WinMain will see this.
}
//Maybe check msg.message == MY_CUSTOM_ERROR_MSG and throw from here?
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//======================
int WINAPI WinMain(...)
{
try
{
RunApp();
//Some other init routines go here as well.
}
catch(const std::exception& e)
{
//log the error
return 1;
}
catch(...)
{
//log the error
return 1;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如您所见,WinMain将处理"例外2"和"3",但不处理 "例外1".
我的基本问题很简单; 将这些类型的错误传播到原始"调用"代码的优雅方法是什么?
我想过可能使用自定义消息并将实际的throw语句移到消息泵(in RunApp())中,但我不确定它是如何工作的,因为我对Windows的体验相对较少.
也许我在看这种情况都错了.如何你平时救助时,一些致命的(即关键资源的收购失败,而且也恢复没有机会)当您在消息处理程序是?
AFAIK WinAPI回调(如窗口/对话框/线程过程)不得传播异常.这是因为WinAPI内部(调用回调)不准备处理异常.它们无法准备,因为异常实现是特定于编译器的,而WinAPI DLL中的代码是固定的,因此它无法处理所有可能的异常传播实现.
在一些简单的情况下(特别是当您使用Visual Studio进行编译时),您可能会发现异常的传播似乎是正确的.然而,这是巧合.即使你的应用程序没有崩溃,你也不确定在它们之间调用的WinAPI函数是否没有分配任何由于他们没有准备好的例外而没有发布的资源.
通过不知道回调的来源(通常 - 对于某些消息可以推断出来)来添加额外的复杂性层.当且仅当它们被发布到对话框时,对话程序处理的消息才会通过消息循环.如果他们被发送,那么他们跳过循环并直接执行.此外,如果发送了一条消息,那么您不知道是谁发送了消息 - 是吗?或者Windows?或者在其他过程中尝试做某事的其他窗口?(但这样做会有风险.)您不知道调用站点是否已为异常做好准备.
Boost.Exception提供了一些解决方法.该库允许以某种方式存储捕获的异常并使用(特别是重新抛出)后者.通过这种方式,您可以将对话框过程包装throw { ... } catch(...) { ... }在catch 中的哪个位置,捕获异常并将其存储起来供以后使用.
编辑:从C++ 11 开始,存储异常对象的Boost.Exception的特性是STD的一部分.你可以用std::exception_ptr它.
但是仍然存在一些问题.您仍然需要以某种方式恢复并返回一些值(对于需要返回值的消息).而且您必须决定如何处理存储的异常.如何访问它?谁会这样做?他/她将用它做什么?
如果WM_INITDIALOG您可以使用此消息将任意参数传递给对话框过程(使用适当形式的对话框创建函数),这可能是指向将保存存储的异常(如果有)的结构的指针.然后,您可以调查该结构,以查看对话框初始化是否失败以及如何.然而,这不能简单地应用于任何消息.
小智 0
我不会出于错误处理目的而注册自定义窗口消息。我的意思是这种方法会很好用,但实际上没有必要。
顺便说一句,上面的 catch 处理程序应该捕获所有 3 个异常。您的对话框过程在调用 CreateDialog 的同一线程上运行。创建无模式对话框不会产生工作线程。无模式对话框仍然通过 GetMessage/Translate/Dispatch 循环获取消息。那里有一个堆栈帧,这意味着当您抛出时,它应该一直展开到您的 WinMain try/catch 块。
这不是您所看到的行为吗?