Even if you have code to handle a message, you’re allowed to call DefWindowProc, because you were doing that anyway after all


Just because you write case WM_SOMETHING: doesn't mean that you have to handle all possible parameters for the WM_SOMETHING message. You're still allowed to call the DefWindowProc function. After all, that's what you did when you didn't have a case WM_SOMETHING: statement in the first place.

switch (uMsg) {
case WM_CHAR:
    OnChar(...);
    return 0;

default:
    return DefWindowProc(...);
}

The above code fragment doesn't handle the WM_SOMETHING message at all. Suppose the WM_SOMETHING message uses the wParam parameter to specify what type of something occurred, and you only want to override the default processing in the case where wParam has the value of 4. What do you do with the other values?

switch (uMsg) {
case WM_CHAR:
    OnChar(...);
    return 0;

case WM_SOMETHING:
    if (wParam == 4) { DoSomething4(...); }
    else ... ????? ...
    return 0;

default:
    return DefWindowProc(...);
}

If the value is 4, then you do your special "something 4" processing, but what about all the other values? How do you handle them?

Well, think about it: How did you handle them before? The original code, before you added a WM_SOMETHING handler, was equivalent to this:

switch (uMsg) {
case WM_CHAR:
    OnChar(...);
    return 0;

case WM_SOMETHING:
    return DefWindowProc(...);

default:
    return DefWindowProc(...);
}

In the original code, since there was no explicit handler for the WM_SOMETHING message, control is transferred to the default case handler, which just calls the DefWindowProc function. If you really want to, you can expand the case out a bit more:

switch (uMsg) {
case WM_CHAR:
    OnChar(...);
    return 0;

case WM_SOMETHING:
    if (wParam == 4) return DefWindowProc(...);
    else return DefWindowProc(...);

default:
    return DefWindowProc(...);
}

Because if the wParam is 4, the original code just called DefWindowProc. And if the wParam was something other than 4, the original code still just called DefWindowProc.

Of course, I expanded the block in precisely this way so it matches up with the case we started writing when we decided to handle the WM_SOMETHING method. Written out this way, it becomes obvious what to write for the question marks.

switch (uMsg) {
case WM_CHAR:
    OnChar(...);
    return 0;

case WM_SOMETHING:
    if (wParam == 4) { DoSomething4(...); }
    else return DefWindowProc(...);
    return 0;

default:
    return DefWindowProc(...);
}

Just because you have a case WM_SOMETHING statement doesn't mean you have to handle all the cases; you can still call DefWindowProc for the cases you don't want to handle.

Armed with this information, you can help commenter Norman Diamond handle the VK_F10 key in his WM_SYSKEYDOWN message handler without having to "start handling a bunch of keys that really are system keys, that I didn't want to bother with."

Comments (16)
  1. John says:

    I thought this was obvious.  I guess I was wrong.

  2. asf says:

    But for custom messages >=WM_APP (or WM_USER if its your class) just returning 0 is fine, DefWndProc has no idea what to do with it anyway

  3. strik says:

    In most cases, I would still use "break;" instead of calling DefWindowProc() myself. This way, I do not have to check if there is some subtle difference in the call to DefWindowProc(). For example, the parameters might be changed in some non-obvious way.

    But that’s more "personal style" than anything else.

    And: Yes, I am surprised that it is not obvious, too.

  4. chrismcb says:

    @asf sure, but Raymond’s point still stands. If you weren’t processing the WM_USER message before, and passing it on to DefWindowProc, you can still pass it off to DefWindowProc

    @strik. I know what you mean, except if you use break, then you end up doing nothing. Of course Raymond only gave us the code for the switch statement, so we don’t really know what happens outside the switch. We do know in the original case we fell into the default and returned DefWindowProc. Now you want to break, and do who knows what?

    Of course personally I like to not have the default: and have the call to DefWindowProc outside the switch. Saves us from writing the call to DefWindowProc multiple times.

  5. SRS says:

    Does DefWindowProc still have the I_cant_believe_I_used_a_goto label in the source?

  6. Brisvan says:

    Obviously Raymond has nothing to do these days, so he is wasting his time (and our time) on sooo basic things.

    Better let the clueless go back to school and study the ABC of programming. Damn it!

  7. Cheong says:

    Brisvan: Only if (non MS-Exam) schools teaches you how to code on Win32… I heard most of them switched to teach on higher level programming languages like C#, VB.NET, Java, etc…

    Agreed it’s basic but for some people, you just have to spell the rules out for them.

  8. Do you really advocate using DefWindowProc as default case? IMHO exactly this increases the chance that later added handlers bypass DefWindowProc completely.

    Personally i always put the return DefWindowProc after the switch/case.

  9. Neil says:

    case WM_SOMETHING:

       if (wParam == 4) { DoSomething4(…); }

       else return DefWindowProc(…);

       return 0;

    Is it me or is that flow of control confusing?

    if (wParam != 4)

       return DefWindowProc(…);

    DoSomething4(…);

    return 0;

  10. Mike Dimmick says:

    @strik: well, you might want to break; if you know that this window procedure subclasses another window proc, and you actually want to have the class’s standard window proc do its thing rather than bypass all the way down to DefWindowProc.

    Subclassing doesn’t add much to this scenario, though.

  11. Neil –

    I’m actually surprised Raymond wrote it that way, I thought our coding guidelines said not to use early returns :)  Basically for the same reasons you’d avoid gotos.

  12. Yuhong Bao says:

    Obviously Raymond has nothing to do these days, so he is wasting his time (and our time) on sooo basic things.

    Better let the clueless go back to school and study the ABC of programming. Damn it!”

    Indeed Raymond posted that there is some basic ground rules for programming you should already know:

    http://blogs.msdn.com/oldnewthing/archive/2006/03/20/555511.aspx

    [I’ve learned that what I consider basic others consider totally non-obvious, so I don’t immediately reject topics just because they “sound basic to me”. -Raymond]
  13. zd says:

    Isn’t this obvious?

    And what’s wrong with the break statement?

  14. Dan says:

    You can also call DefWindowProc BEFORE or AFTER your own code if you want to use the default message handling alongside some custom code!  Genius!

    </sarcasm>

    But yeah, I was surprised some people couldn’t figure this out on their own.  I suppose if you don’t know exactly what DefWindowProc does and you’re just copy/pasting a template for your own WndProc you might not know you can put more DefWindowProc calls in safely.

  15. gloomyandy says:

    I assume there are also a few gotchas with doing this as well, some messages come in pairs (or more complex sequences), and only passing some part of the sequence may be asking for trouble…

  16. strik says:

    @chrismcb: Good catch regarding my error. Exactly that type of error I was doing here is the reason why I have my own style and stick to it.

Comments are closed.

Skip to main content