gpt4 book ai didi

c++ - Win32 消息处理程序错误传播

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:17:51 25 4
gpt4 key购买 nike

我正在编写一个使用单个对话框的 (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;
}

如您所见,WinMain 将处理“异常 2”和“3”,但不会“异常 1”。

我的基本问题很简单;将这些类型的错误传播到原始“调用”代码的优雅方式是什么?

我考虑过使用自定义消息并将实际的 throw 语句移出到消息泵(在 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 的情况下,您可以使用此消息将任意参数传递给对话过程(使用适当形式的对话创建函数),这可能是指向将保存存储的异常的结构的指针(如果有的话)。然后您可以调查该结构以查看对话框初始化是否失败以及如何失败。然而,这不能简单地应用于任何消息。

关于c++ - Win32 消息处理程序错误传播,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1169010/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com