19

While trying to figure out why an app I'm working on wouldn't close I realized it was throwing an exception in its WM_CLOSE handler. However instead of the app crashing (as it should), the exception gets silently ignored.

To make sure there wasn't something else going on, I created a new C++ Win32 app in Visual Studio and added this:

case WM_CLOSE:
    (*(int*)NULL) = 0;
    break;

Same thing: no crash, just a first chance exception in the debug log. If I add the same code to a WM_COMMAND handler it crashes as expected.

So I'm curious: what's so special about WM_CLOSE that Windows deems exceptions thrown by it should be swallowed?

(BTW: this is on Windows 7 x64, running x86 program)

wallyk
  • 56,922
  • 16
  • 83
  • 148
Brad Robinson
  • 44,114
  • 19
  • 59
  • 88
  • 3
    There's nothing inherently special about `WM_CLOSE`. Unhandled exceptions in your `WndProc` callback always get silently ignored, by calling `WndProc` under a `__try`/`__except` handler. This is the only safe implementation, since [when you transfer control across stack frames, all the frames in between need to be in on the joke](http://blogs.msdn.com/b/oldnewthing/archive/2012/09/10/10347674.aspx). – IInspectable May 12 '14 at 07:52
  • 2
    By default WindoWProcedure calls destroy window to destroy the window. This could be a reason, that any exceptions are then redirected to "/dev/null": http://msdn.microsoft.com/en-us/library/windows/desktop/ms632617(v=vs.85).aspx and http://msdn.microsoft.com/en-us/library/windows/desktop/ms632682(v=vs.85).aspx – icbytes May 12 '14 at 07:52
  • 2
    @IInspectable, I don't think Raymond Chen says the `WndProc` callback is protected in his article. It actually looks like he's saying the opposite: `As a result of this overall mindset, Win32 code doesn't worry too much about recovering from exceptions. If an exception happens, then it means your process is already toast and there's no point trying to fix it`. In addition, if the callback was protected then a NULL pointer dereference would not crash in a `WM_COMMAND` handler either. – Frédéric Hamidi May 12 '14 at 08:08
  • I read it as "you can't throw exceptions across stack frames that are not in your control" - wndproc is one of them as its a call back, so they get "lost" as it can't propagate back to your message loop function. – paulm May 12 '14 at 08:10
  • @IInspectable I'm not aware that calls to WndProc are called in __try/__except. Certainly unhandled exceptions don't always get silently ignored - they should cause the app to terminate. Where's the transfer across stack frames? – Brad Robinson May 12 '14 at 12:49
  • @icbytes it's not related to the window being destroyed - the exception occurs before DefWindowProc is called, the window isn't destroyed and the app continues to run. – Brad Robinson May 12 '14 at 12:50
  • Did You execute it ONLY in visual studio or also as standalone application ? – icbytes May 12 '14 at 12:51
  • @paulm the exception doesn't need to be propagated back to the message loop. There's no apparent reason for it to be lost/swallowed though. – Brad Robinson May 12 '14 at 12:52
  • The message pump is your code, it calls MS code, which calls your code (window proc) if window proc throws you expect it to end up back at message pump. It won't, because MS code won't pass it on. – paulm May 12 '14 at 12:54
  • 4
    @Brad If you would rather not have your exceptions swallowed you need to provide a Win7-compatible manifest with your application, and compile for 64-bit. Otherwise the 'old' Win2k3 behavior is in effect, which swallows exceptions. – IInspectable May 12 '14 at 12:54
  • Just to be clear this isn't really a problem for my app. I'm just curious why the behavior would be different for WM_CLOSE compared to most other messages eg: WM_COMMAND. – Brad Robinson May 12 '14 at 13:12
  • @icbytes both - same behavior – Brad Robinson May 12 '14 at 13:18
  • @paulm I don't expect it to be thrown back to the message loop. I expect it to terminate the app like any other exception normally does. – Brad Robinson May 12 '14 at 13:18
  • Then You should stick to paulm's answer. The "transfer" between stackframes sounds logical in this case, because the wndproc in Your program and the windows message pump define both two stack frames, though, if You except a try-catch error, code it down and throw another windows messsage EITHER IN try, then in catch and inspect the behaviour. – icbytes May 12 '14 at 13:20
  • @IInspectable Unless there's a manifest setting that controls the behavior for different window messages, not sure how the manifest is related to this. Exceptions in WM_CLOSE are swallowed, exceptions in WM_COMMAND terminate the app. – Brad Robinson May 12 '14 at 13:21
  • What I also could think of is, that WM_CLOSE is harder defined then WM_COMMAND. The WM_CLOSE message could be of a type, which is not that much checked by the usual coder, as WM_COMMAND might be, and therefore handles WM_CLOSE a little bit more "loose". But now my ideas reached the end. I do not have other explanations. – icbytes May 12 '14 at 13:29
  • 1
    @paulm "It won't, because MS code won't pass it on." Not sure what you mean by this. In fact if I wrap DispatchMessage in a try-except, exceptions thrown by wndproc _are_ propagated up to the message loop function. (unless they're thrown in a handler for WM_CLOSE). – Brad Robinson May 12 '14 at 13:31
  • 1
    This is documented well in the MSDN article for [WindowProc](http://msdn.microsoft.com/en-us/library/windows/desktop/ms633573%28v=vs.85%29.aspx). You are seeing #1 behavior. Be careful with the PCA, that OK button looks *way* too tempting. Removing the appcompat shim requires editing the registry. Or renaming your EXE :) – Hans Passant May 12 '14 at 15:03
  • 4
    @Brad Having a valid Windows 7 manifest is enough to change the behavior. This is detailed in [The case of the disappearing OnLoad exception](http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/). I should have been more precise: Not all exceptions are swallowed by default, but only those where the `WndProc` is the user-mode callback of a kernel function (e.g. `WM_CREATE`/`WM_NCCREATE` as a result of calling `CreateWindow`). – IInspectable May 12 '14 at 18:45
  • 1
    Yep, it was behaviour #1 in the doco for WindowProc. Interestingly just rebuilding for x64 didn't fix it - I needed to also rename the executable as mentioned by @HansPassant. – Brad Robinson May 13 '14 at 21:45
  • I couldn't reproduce it, WM_CLOSE crashes for me. I'm on 32-bit Win7, though. – sashoalm May 23 '14 at 13:26
  • Tried it on XP 32-bit, it crashes there, too. – sashoalm May 23 '14 at 14:12
  • It is visual studio which eats exceptions in the window proc, not windows. I normally use Borland C and are used to the "luxury" the application crashes if an exception happens anywhere in the application. Now to make a x64 build I compile the code in Visual Studio and it is annoying the message processing is simply canceled when there is a problem instead of a crash of the application. I am trying to figure out how to configure VC so it crashes. UPDATE: oh, it is windows x64, not VC.... – Arnoud Mulder Feb 01 '20 at 16:41
  • UPDATE 2: and only in XP, in Windows 10 the application is exited – Arnoud Mulder Feb 01 '20 at 17:29

1 Answers1

2

That exceptions be swallowed by a WindowProc on Win 64 is expected behavior. (See docs.)

Also see the better explanations on Paul Betts' blog, especially his note

How come this doesn’t happen all the time?

Here’s why this seems to happen only on certain window messages – remember that window messages can originate from different sources, anyone(*) can queue a message to a window. However, certain window messages are directly sent via win32k.sys (the most notable one being WM_CREATE) as a direct synchronous result of a user-mode call.

seems to shed some light on this question.

Random ASCII also has some good explanation on all this kernel-mode-ex-swallowing behavior:

Failure to stop at all

An equally disturbing problem was introduced some years ago with 64-bit Windows and it causes some crashes to be silently ignored.

Structured exception ... relies on being able to unwind the stack (without or without calling destructors) in order to transfer execution from where an exception occurs to a catch/__except block.

The introduction of 64-bit Windows complicated this. On 64-bit Windows it is impossible to unwind the stack across the kernel boundary. That is, if ... an exception is thrown in the callback that is supposed to be handled on the other side of the kernel boundary, then Windows cannot handle this.

This may seem a bit esoteric and unlikely – writing kernel callbacks seems like a rare activity – but it’s actually quite common. In particular, a WindowProc is a callback, and it is often called by the kernel, ...

And as a bonus: See here on SO on the why.

Community
  • 1
  • 1
Martin Ba
  • 37,187
  • 33
  • 183
  • 337
  • I think it is horrible and shocking that Windows silently swallows exceptions - that Windows silently ignores crashes. The Raymond Chen article points out the danger of having exceptions handled without everybody being in on the joke which makes it weirder yet that Windows does that. BTW, I'm the author of the Random ASCII article linked in this answer. – Bruce Dawson Jun 06 '15 at 16:05
  • @BruceDawson - I do agree about horrible, whereas after all I've been through wrt. Windows crash handling (from XP onwards), I'm not so sure I find much shocking anymore. :-) ... And thanks for Random ASCII - always great resources there! – Martin Ba Jun 12 '15 at 20:00