No, really, you need to pass all unhandled messages to DefWindowProc


Earlier I had discussed that you have to return the special value BROADCAST_QUERY_DENY if you want to deny a device removal query because too many programs thought that they had covered "all" the Windows messages and just returned zero for the others. Since then, there have been lots of other window messages added to the system, many of which contain nontrivial processing in DefWindowProc. Yet, every so often, I run into another program that assumed that "Microsoft will never enhance the window manager" and simply returned zero for all the messages they didn't handle.

Indeed, often these programs don't even cover all the existing messages! One program had a helper window that handled just a few messages and returned zero for the rest. As a result, you couldn't shut down the computer because returning zero in response to the WM_QUERYENDSESSION message means, "No, don't shut down." I guess the people who wrote that program assumed you would shut down their program manually. (Programs are not supposed to fail a shutdown unless the decision came from the user, typically by clicking "Cancel" in response to a "Do you want to exit without saving?") Custom keyboard buttons like the volume control buttons didn't work either (if focus was on this helper window), because it neglected to pass the WM_APPCOMMAND message to the DefWindowProc function.

Therefore, once again, I implore you: If you don't handle a message in your window procedure, pass it to the DefWindowProc function. Your customer base thanks you.

(Note for people who take what I say too literally: If you are using a framework, then follow that framework's protocol for indicating that you want default message processing to occur. For example, dialog procedures do not pass unhandled messages to the DefWindowProc function; they merely return FALSE to indicate that default processing should take place.)

Comments (22)
  1. 8 says:

    Yes, ofcourse (I do that). But using DefWindowProc is always a little bit creepy because so much things that I don’t know about, so I implement more WM handlers then I’d actually need, just to ensure a proper state and nice handling instead of ignoring it and let Windows figure it out by itself, which cannot possibly know how my program would best handle it so just does a "safety measure" to ensure nothing breaks.

  2. Brian says:

    I wonder if some of the problem comes from people who confuse dialog box handling with regular window handling…

  3. 8 says:

    I mean "because it does so many things"… funny I got errors when trying to post this correction.. hmm

    <title>Error</title>

    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">

    Clever self-aware program ;*)

  4. Rowland Shaw says:

    It’s not just old applications that continue to report failiure in response to WM_QUERYENDSESSION:

    http://lab.msdn.microsoft.com/ProductFeedback/viewfeedback.aspx?feedbackid=d6dc37dc-13b6-4f88-aff8-2ec7f035d9b8

  5. Norman Diamond says:

    For example, dialog procedures do not pass

    > unhandled messages to the DefWindowProc

    > function; they merely return FALSE to

    > indicate that default processing should take

    > place.)

    100% true, but seeing it in this context an idea has popped up.  To programmers who are less skilled, and/or who copy sample code without noticing that the source and destination contexts differ in this way, it might not be obvious that dialog procs and window procs differ in this way.  Also is there some possibility that the documentation of dialog procs might have been less clear in the past than it is now?  Or maybe wizards generated bad code, somewhat like Visual Studio 2005 still generating bad code for winmain functions?  If it’s not always the programmer’s fault then I wish something could be done to help, though in this situation it seems pretty difficult to help.

    Um, by the say, why isn’t it CallWindowProc(DefWindowProc, …, …, …)?

  6. Endurion says:

    I deem it weird that anyone can return 0 to all unhandled messages and not notice that he’s having A) massive displaying problems and B) massive behaviour problems with that particular window.

    Unless a "lucky" constellation of handled messages and two blind eyes…

  7. Stu says:

    Detection of bad handling should be fairly simple though:

    1) Have a dummy message, WM_QUERYPROPERHANDLING for instance and make it clear in the docs that *only* DefWindowProc can properly handle this message. For added reliability, you could just use a random unassigned message ID.

    2) Have DefWindowProc return a pseudo-random magic value for this message. The value should be as unpredictable as possible and the algorithm to generate it should change in every single version and edition of Windows.

    Then, periodically send the dummy message to all applications. See which ones respond correctly. For applications that do not, ignore their responses to WM_QUERYENDSESSION and other  messages where returning 0 can be bad for the user.

  8. Norman Diamond says:

    I like the ideas of Stu and Luc Rooijakkeers.  In fact it would be nice for any debug version of a program (with or without a debug version of Windows) to get killed on an Assert when it misbehaves this way.  This assert won’t fix old programs but it will help fix new ones.

    My previous comment has a bug.  I asked if the documentation of dialog procs used to be less clear in the past, but the correct question would be if the documentation of window procs used to be less clear in the past.  If anyone copied code from a dialog proc into a window proc, they could be in bad shape, same as if they used window proc code generated by some automated wizards.

  9. Luc Rooijakkeers says:

    Stu: What you suggest would be possible I guess, but the problem is compounded by the fact that a program may have multiple message-handling loops (think modal dialogs, for example).

    Personally, I’d feel safer if every such "bad if not handled properly" message would be *immediately* preceded by such a "test default handling" message. It’s not like they are that frequent (i.e. many times per second) anyway…

    Reserving a certain range for the random message id would be best, I think, to discourage developers from just adding a single case WM_QUERYPROPERHANDLING: to their message handling code. And of course this *would* need to be documented properly and added to message tracing code etc. Perhaps using a pseudo-registered window message would work for this?

    Is it actually even possible to look up the name of a registered window message?

    Finally, to make this probing message really work for developers, the debug version of windows (or even better, any program being debugged?) should complain when the message ISN’T being handled properly, preferably with a "never bother me again about this particular version of this particular executable" option (where "particular version" should include version numbers and file date/time to account for continuing development).

  10. 8 says:

    What’s up with the Windows XP caption bar messages?

    http://groups.google.com/group/microsoft.public.win32.programmer.ui/browse_thread/thread/39f679322067d1f1/90cb0e2fe2b5141c?tvc=2

    My windowproc also just returns 0 on them. Iirc it goes like this (shortened):

    UINT msg = message|1;

    if (msg == 0xAF) return 0;

  11. David Walker says:

    "It’s not just old applications that continue to report failiure in response to WM_QUERYENDSESSION"

    Wow!  It’s almost funny that Rowland Shaw’s link points to a product feedback on the brand-spanking-new SQL Server 2005, which aborts a shutdown request before the user even answers the "save or abandon" dialog box.

    Almost funny, but not quite.

  12. MSDN Archive says:

    Rowland, you are evil!

  13. Mike Fried says:

    So if DefWindowProc is so important, and this issue is common, then why not have a compatability flag to change the behavior around calling the Window with specific messages. Basically, when you call the WndProc, and it returns that it didn’t handle the message, check if the DefWindowProc was called. This is simply a matter of having something like so:

    threadGlobalWasDefaultProcUsed = false;

    result = CallWndProc( … );

    if( compatFlagZZZZ && messageNeedsDefaultHandling( … ) && 0 == result && !threadGlobalWasDefaultProcUsed )

    {

      // The app didn’t process the message and didn’t care to ask Windows to process the message

      result = DefWindowProc( … );

    }

    How evil is a solution like this? How likely would it be to break existing functionality? Would adding an app compatability flag for this be a bad solution? My guess is that this kind of problem/solution has huge testing problems. I wonder if tools like App Verifier check these kinds of problems.

  14. 8 says:

    Then they could’ve better not export the DefWindowProc API at all in the first place, and use it internally whenever the window procedure returns 0. (much like the dialog procs)

  15. peterchen says:

    I like the idea to help debuggers to check for this, but IMHO a lazy programmer will always wreak havoc. You can plug a few critical holes (is this one?), but no amount of code can plug laziness. And by trying you just create a mess for the OS maintainer and a pain for other developers.

  16. all laptop users must hate apps that dont let you suspend or shutdown, its a mistaken attempt by the app to stop you doing something they feel is misguided or wrong. Office 2000, for example wont let you suspend if you are reading a network file, even if that is one in a folder you’ve marked as ‘available offline’. There’s nothing like pulling a laptop out of a bag to find it overhot, battery down to 7% and a dialog saying "you cannot suspend" to make you feel ill will to developers and general.

    Windows vista will take the veto of suspend (WM_POWER) out of apps, but not drivers, which peeves me. IMO unsigned drivers shouldnt have the right either, as it means they havent been through WHQL yet. WM_QUERYENDSESSION is the same: system shutdown is too important to let an app veto.

    If an app can only save its state on a clean close, then the app is broken. they should go read up on "crash only software" and rethink what they are doing

  17. Steve says:

    Any way to find out what window is doing this?  My laptop will occasionally refuse to shutdown.  Never a message telling me why.  I typically kill every process I can and it’ll shut down.

    I suppose one of these days when I’m not in a hurry to leave I’ll have to kill a process and try a shutdown, kill another and shutdown, etc.  It’d be a pain but at least I might find the misbehaving program that way.

    Your entry just got me thinking that maybe I could write a little console program to send that message to every window and tell me which one refused the shutdown.

  18. Neil says:

    I hope all these gems are clearly listed in a Windows Logo certification requirements document somewhere.

  19. Neil: I guess I don’t understand how this proposed "certification requirement" document would be any different from just a list of "common mistakes and how to avoid them" that applies to all programs, not just programs submitted to certification.

    In other words, why hide it in the certification requirements? Shouldn’t it be in the core documentation? Oh wait, it already is: http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/windowprocedures/aboutwindowprocedures.asp

  20. Norman Diamond says:

    I think Neil’s idea was that a listing in the requirements wouldn’t only inform (or hide as the case may be) the requirement to developers, but would also have an effect on testing.  That is, to get the logo, programs would have to pass a test that would include testing for this case.

    I do wonder how some programs get logos that they pretty obviously aren’t qualified for.  Some such programs aren’t even made by the company that issues the logos.

  21. As far back as elementary school, there may have been times when notes were being passed. The note would…

Comments are closed.

Skip to main content