Exception in WindowProc
Asked Answered
A

3

5

Is it possible to catch error inside WindowProc callback? try / catch doesnt work. Looks like __try __except and also hardware exception (AV, for example) doesnt work also.


Update:

I figured out that indeed this is possible to throw exception in WindowProc callback and catch it with catch block outside WindowProc. Tested and works on Windows XP x86. I found releated question 64bit exceptions in WndProc silently fail The problem seems only exist on Windows 7 x64 (and according to that question on other x64 Windows versions too).

So the question is it possible somehow to throw exception in WindowProc and catch it with catch block outside WindowProc? I installed microsoft hotfix, set DisableUserModeCallbackFilter to 1 in registry and best I get is FATAL_USER_CALLBACK_EXCEPTION, not my exception.

Allonym answered 1/2, 2013 at 9:1 Comment(3)
winapi functions don't use exceptions. They were written in C.Libau
Is it possible to determine error inside WindowProc callback without exceptions?Allonym
Sure, call the functions from in there, check for failure, and use GetLastError and the like appropriately. Propagate results if calling from a function called within WindowProc.Libau
H
4

The MSDN documentation for WindowProc has detailed info on exceptions thrown/propagated from WindowProc. It seems that exceptions are only propagated in 32-bit versions of Windows.

However, your original question is different from the question in your update. The first one was about catching exceptions in WindowProc, and that will work fine always. The second one is about throwing exceptions from WindowProc.

I'm not sure about the usefulness/necessity of the second one. A window procedure is normally called as a result of:

  1. Calling DispatchMessage in the message loop. There's no need to throw an exception in this case because doing so would just cause the application to exit. If you encounter an error that should cause the application to exit, just call PostQuitMessage(0)
  2. Calling SendMessage. In this case you don't really want to throw exceptions because the window procedure will be executed in the UI thread, and if the calling thread is different from the UI thread, the calling thread won't get the exception anyway
  3. Calling the window procedure directly. Exceptions will work fine in this case.
Heurlin answered 8/3, 2013 at 11:33 Comment(4)
I want to throw exception when window procedure is called as result of calling DispatchMessage. I know that this is possible to PostQuitMessage(0); or use global variables to indicate error but I want to use exceptions to provide more information about error like error code, line number in WindowProc etc. So my question is it even possible to throw exception from WindowProc on x64 version? Maybe it is possible to make it with __try __except?Allonym
If the MSDN link is correct and I'm reading it right, it's not possible. Still, why not just do all the error reporting right inside WindowProc and then call PostQuitMessage(0);?Heurlin
Because it is inconvenient to handle errors in two different places, one in WindowProc and other in catch block. I think that one of exception advantage is that you can handle all errors in one place. But this is not really irrelevant to question. Anyway thanks for answer.Allonym
This answer is mostly opinion based. Who decides when it makes sense to throw exceptions? I think one should always feel safe to use exceptions, else exceptions should not be apart of the language! It makes no sense I am relying on exceptions to identify errors; when they are a potential error their self.Waac
A
3

With C++11, you could handle your situation by manually forwarding any exceptions like this:

#include <exception>
std::exception_ptr windowProcException = nullptr;

LRESULT windowProc(){
  try {
    yourcode();
  catch(...){
    windowProcException = std::current_exception();
  }   
}

You can then rethrow the exception in your mainloop like this:

windowProcException = nullptr;
DispatchMessage();
if (windowProcException)
  std::rethrow_exception(windowProcException);
Andro answered 22/2, 2019 at 2:34 Comment(2)
You will always be loved by me Mr. Chronial. You came here with an actual solution to a very ... fustrating problem. I tested your solution in nested window procedure calls and it works. Such as WM_SIZE and WM_CREATE of a child window inside WM_CREATE of the parent window. This should be the accepted answer.Waac
This won't work if the exception was thrown from the WndProc of a modal dialog. A better way is to install a WH_CALLWNDPROCRET hook and test windowProcException in the hook procedure.Doityourself
W
0

Chronial gave the best answer. I will give what I think is a useful refinement.

Chronial's concept was to allow use of cpp throw mechanism inside your window procedure, but dont let it propogate outside of the window procedure; which is called in a C library and leads to undefined behaviour on 64 bit windows, namely 64 bits win 7 or windows 8. Instead catch the exception within the window procedure, and save it in a global variable, which you rethrow in your cpp main function and make use of. See Chronial's answer for code example.

The concept is simple, but requires a bit of detail to get 100% right.

  • One pitfall to avoid is not destroying the window you throwed from. Your code will clean up all objects declared in the try block, but the window object you create will still be alive and handling messages. Even though you are no longer dispatching messages. If you use pointers in the window procedure, these pointers may be invalid when your code is in your catch block, while windows is still pumping messages to your window which you didn't destroy.

  • Every window procedure needs to have this try catch, save exception technique. It won't work if only your top level window is prepared, but exceptions are thrown in it's child window's procedure.

  • If the first two were super obvious, this one is slightly non-obvious. For your top level window's procedure; in addition to try catch the entire switch statement, you should also try catch the WM_CREATE message, and return -1 if you caught an exception. This will prevent the window and its children from being created; and save you from having to destroy the window once you rethrow the exception.

  • Lastly once again in your WM_CREATE message of your top level window, after running the code that created child windows, check if those child windows set the global windowProcException variable. Creation of child windows will run their own windowProcedure, and exceptions caught in those window procedures will not automatically propogate to your top level window's procedure. If exceptions happened in the child's window procedure, returning -1 from the top level window will cancel creation of all windows. Unless, you decide that a particular child window was not super important.

    void createChildWindows();

    windowProcedure(hwnd,msg,wparam,lparam) { try { try { if(msg == WM_CREATE) { createChildWindows(); return windowProcException ? -1 : 0; } } catch(...) { windowProcException = std::current_exception(); return -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
     }
    

    }

Waac answered 19/10, 2021 at 5:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.