窗口处理中的异常

Exception in WindowProc

本文关键字:异常 处理 窗口      更新时间:2023-10-16

是否可以在回调WindowProc捕获错误? try/catch不起作用。看起来__try __except和硬件异常(例如AV)也不起作用。


更新:

我发现这确实可以在回调中抛出异常WindowProc并使用WindowProc外部的catch块捕获它。经过测试并适用于Windows XP x86.我发现 WndProc 中的相关问题 64 位异常静默失败该问题似乎只存在于Windows 7 x64上(根据其他 x64 Windows 版本的问题)。

所以问题是是否有可能以某种方式在WindowProc中抛出异常并用WindowProccatch块捕获它?我安装了微软修补程序,在注册表中将DisableUserModeCallbackFilter设置为 1,我得到的最好的是 FATAL_USER_CALLBACK_EXCEPTION ,而不是我的例外。

WindowProc

的 MSDN 文档包含有关从 WindowProc 引发/传播的异常的详细信息。似乎异常仅在 32 位版本的 Windows 中传播。

但是,您的原始问题与更新中的问题不同。第一个是关于在WindowProc中捕获异常,这将始终正常工作。第二个是关于从WindowProc抛出异常。

我不确定第二个的用处/必要性。窗口过程通常由于以下原因而被调用:

  1. 在消息循环中调用调度消息。在这种情况下,无需引发异常,因为这样做只会导致应用程序退出。如果遇到导致应用程序退出的错误,只需调用 PostQuitMessage(0)
  2. 调用发送消息。在这种情况下,您实际上并不想引发异常,因为窗口过程将在 UI 线程中执行,如果调用线程与 UI 线程不同,则调用线程无论如何都不会收到异常
  3. 直接调用窗口过程。在这种情况下,例外将正常工作。

使用 C++11,您可以通过手动转发任何异常来处理您的情况,如下所示:

#include <exception>
std::exception_ptr windowProcException = nullptr;
LRESULT windowProc(){
  try {
    yourcode();
  catch(...){
    windowProcException = std::current_exception();
  }   
}

然后,您可以在主循环中重新抛出异常,如下所示:

windowProcException = nullptr;
DispatchMessage();
if (windowProcException)
  std::rethrow_exception(windowProcException);

Chronial给出了最好的答案。我将给出我认为有用的改进。

Chronial 的概念是允许在窗口过程中使用 cpp 抛出机制,但不要让它在窗口过程之外传播;这在 C 库中调用并导致 64 位窗口(即 64 位 win 7 或 windows 8)上的未定义行为。而是在窗口过程中捕获异常,并将其保存在全局变量中,然后在 cpp main 函数中重新抛出该变量并利用该变量。有关代码示例,请参阅Chronial的答案。

这个概念很简单,但需要一些细节才能 100% 正确。

  • 要避免的一个陷阱是不要破坏你扔出的窗户。您的代码将清理 try 块中声明的所有对象,但您创建的窗口对象仍将处于活动状态并处理消息。即使您不再发送消息。如果在窗口过程中使用指针,则当您的代码位于 catch 块中时,这些指针可能无效,而窗口仍在向窗口泵送您未销毁的消息。

  • 每个窗口过程都需要有这个尝试捕获,保存异常技术。如果仅准备了顶级窗口,但它将不起作用,但在其子窗口的过程中会引发异常。

  • 如果说前两个超级明显,那么这个就有点不明显了。对于顶级窗口的程序;除了尝试捕获整个 switch 语句之外,您还应该尝试捕获WM_CREATE消息,如果捕获异常,则返回 -1。这将阻止创建窗口及其子窗口;并使您不必在重新抛出异常后销毁窗口。

  • 最后,在
  • 顶级窗口的WM_CREATE消息中,运行创建子窗口的代码后,检查这些子窗口是否设置了全局窗口ProcException变量。创建子窗口将运行其自己的 windowProcedure,在这些窗口过程中捕获的异常不会自动传播到顶级窗口的过程。如果在子窗口过程中发生异常,则从顶级窗口返回 -1 将取消创建所有窗口。除非您确定某个特定的子窗口不是超级重要。

    void createChildWindows();

    windowProcedure(hwnd,msg,wparam,lparam){尝试{尝试{if(msg == WM_CREATE){创建儿童窗口();返回窗口ProcException ?-1 : 0;}}捕获(...{windowProcException = std::current_exception();返回 -1;}

         return DefWindowProc(hwnd,msg,wparam,lparam);
     }
     catch(...)
     {
      windowProcException = std::current_exception();
      //MingGw won't complain if you don't return a value;
      //MSVC might
      //As far as I am concerned, we are throwing, 
      //so any value returned is undefined
      //We must however be careful with WM_CREATE as that return code
      //dictates whether or not window creation continues
     }
    

    }