Use WM_WINDOWPOSCHANGED to react to window state changes


The documentation for the WM_SHOWWINDOW message points out that the message is not sent under certain circumstances. But what if you want to know when the window is shown, including in the cases where you don’t get WM_SHOWWINDOW?

The WM_WINDOWPOSCHANGED message is sent at the end of the window state change process. It sort of combines the other state change notifications, WM_MOVE, WM_SIZE, and WM_SHOWWINDOW. But it doesn’t suffer from the same limitations as WM_SHOWWINDOW, so you can reliably use it to react to the window being shown or hidden. The handler would go something like this:

void OnWindowPosChanged(HWND hwnd, const WINDOWPOS *pwp)
{
    if (pwp->flags & SWP_SHOWWINDOW) {
       window_was_shown();
    }
    if (pwp->flags & SWP_HIDEWINDOW) {
       window_was_hidden();
    }
    if (!(pwp->flags & SWP_NOMOVE)) {
       window_moved_to(pwp->x, pwp->y);
    }
    if (!(pwp->flags & SWP_NOSIZE)) {
       window_resized_to(pwp->cx, pwp->cy);
    }
}

HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, OnWindowPosChanged);

Note also that if you don’t pass the WM_WINDOWPOSCHANGED message to DefWindowProc, then you won’t get WM_MOVE or WM_SIZE messages, since it is DefWindowProc that converts WM_WINDOWPOSCHANGED into the WM_MOVE and WM_SIZE messages.

“If WM_WINDOWPOSCHANGED is redundant with WM_MOVE, WM_SIZE, and WM_SHOWWINDOW, then why do we have those other messages anyway?”

The WM_WINDOWPOSCHANGED message wasn’t invented until Windows 3.1. Prior to that, you had no choice but to react to those other messages. You can think of those other three messages as legacy messages now. There’s nothing wrong with them, but they’re kind of old-fashioned now.

Next time, we’ll look at the companion message WM_WINDOWPOSCHANGING.

Postscript: This entry was inspired by an actual customer question regarding the cases where WM_SHOWWINDOW message is not sent if the program is run with the SW_SHOWMAXIMIZED state. Unfortunately, one detail I missed in the customer’s question was the remark that they need to know when the window is shown because it is “critical for the application to initialize its state.” I didn’t follow up on that little remark, but I should have, because it’s very strange to do initialization work when a window is shown. What if the window is never shown? Does this mean that the program will never initialize itself? (For example, somebody might have run your program with the SW_HIDE state.) The WM_NCCREATE and WM_CREATE are the more traditional places to do window initialization.

Comments (20)
  1. Good Point says:

    I think the really interesting part would be why WM_SHOWWINDOW is not sent under those certain cirsumstances.

  2. philq says:

    I can’t wait for tomorrow.  WM_WINDOWPOSCHANGING is hands-down my favorite message.  I would say it’s everybody’s favorite, but there is a surprising lack of discussion of favorite win32 messages on the internet.  Between WM_WINDOWPOSCHANGING and AttachThreadInput() the operating system is your oyster.

  3. Michael says:

    Good entry as usual, but don’t you mean the opposite of >>"If WM_WINDOWPOSCHANGED is redundant with WM_MOVE, WM_SIZE, and WM_SHOWWINDOW, then why do we have those other messages anyway?"<< ?

  4. Stephen Eilert says:

    @Michael

    "Good entry as usual, but don’t you mean the opposite of >>"If WM_WINDOWPOSCHANGED is redundant with WM_MOVE, WM_SIZE, and WM_SHOWWINDOW, then why do we have those other messages anyway?"<< ?"

    I don’t think so. If WM_WINDOWPOSCHANGED provides more information, the others are redundant and might as well not exist.

  5. Michael says:

    Yes I get that, maybe I’m just thick headed but the first part of the sentence makes it sounds like it’s WM_WINDOWPOSCHANGED that’s redundant, which is clearly not the case.

  6. ulric says:

    hum.. I’m an old-style charles-petzold kinda guy and I like the old messages. Especially since the WM_SIZE notification is generally all that a window needs.  Move? Never needed it.  WM_SHOWWINDOW is fine.. bizzare the client had a problem with it.  I like simple messages.

    btw, that sample code is a little funny, since it implies window could be both shown and hidden..

  7. Blech says:

    "It sort of combines the other state change notifications, WM_MOVE, WM_SIZE, and WM_SHOWWINDOW"

    I never could get my head around that. The first two I have no problem with, but WM_SHOWWINDOW? The position isn’t changing, is it? Just whether it is shown or not.

  8. Blech says:

    "What if the window is never shown? Does this mean that the program will never initialize itself?"

    Might make sense in some cases. If a program is, for example, displaying an image but is not visible why go to the expense of loading the file? It really depends what they mean by "initialize its state".

    "The WM_NCCREATE and WM_CREATE are the more traditional places to do window initialization."

    Maybe they really did mean application, and not window.

  9. John says:

    Flags like these drive me nuts, especially in the Windows API.

    Presumably you can’t both show and hide a window at the same time, so why do you have separate flags for them?  Also, why are some flags "negative" when the others are "positive"?  Wouldn’t it make more sense to check for the presence of a flag rather than the lack of a flag?  "If the window was moved" makes more sense than "if the window was not not moved".  This is the same thing you brought up a while ago about checkboxes with text like "Enable whatever" and "Disable whatever".  Very annoying.

  10. noone in particular says:

    Great stuff.

    I just now learned about the existence of WM_WINDOWPOSCHANGED, and I am doing MFC-programming for 4 years now…

  11. J says:

    "Presumably you can’t both show and hide a window at the same time, so why do you have separate flags for them?"

    What if the window is neither shown nor hidden, but instead was resized?  What value do you propose to set the SHOW flag if 1 means the window was shown and 0 means the window was hidden?  Separate flags are essential for the design of the WM_WINDOWPOSCHANGED handler.

  12. Mike says:

    I don’t think it’s strange at all to do (re-)initialization when the window is shown if you use e.g. DDraw (incl. D3D) which requires it – old contents are almost certainly lost.

  13. John says:

    @J:  Oops.  You are correct in that regard.

  14. AK says:

    SWP_NOxxx flags come from SetWindowPos, has nothing to do with new flags added over time AFAIK (but, SetWindowPos should probably been implemented the other way around, where you specify a mask of valid params instead of excluding stuff, but too late for that now ofcourse)

  15. Sarath says:

    I’ve used this message before to detect Scrollbar available or not by GetWindowLog()

  16. hexatron says:

    I suppose if I thought about this, it would explain why, under Windows 3.0, I never used ShowWindow, but instead moved the windows off- or on-screen with MoveWindow. But I won’t.

    I notice the WM_WINDOWPOSCHANGED documentation says all this good stuff I never knew (this message generates the WM_MOVE,etc, and you are better off reacting to these in the WM_WINDOWPOSCHANGED). It makes me think I should re-read all the Win32 documentation (never mind MFC and the outer darkness of non-C/C++ windows programming).  Then I remember I probably only have one human lifetime at my disposal, and I’ve already squandered most of that.

  17. hexatron says:

    As for flag, I just did a count of the 2002 Windows SDK:

    The include files together contain about 100,000 #defines.

    Of these, 625 are evaluate directly as 0 (or 0L, 0x0000, etc). Of course, many more are indirectly zero (#define ZERO 0 & #define HIDDENZERO ZERO)

    The memorable one is FILE_ATTRIBUTE_NORMAL, as in

    if(attributes & FILE_ATTRIBUTE_NORMAL){

    /// in your dreams, sucker!!!

    }

  18. Miral says:

    "I never could get my head around that. The first two I have no problem with, but WM_SHOWWINDOW? The position isn’t changing, is it? Just whether it is shown or not."

    Ah, but WM_SHOWWINDOW is related to the ShowWindow API, which can both show/hide and resize windows (minimising, maximising, and restoring are all size changes).  So they are related.

    "Also, why are some flags "negative" when the others are "positive"?  Wouldn’t it make more sense to check for the presence of a flag rather than the lack of a flag?"

    Because new flags are added over time, and existing programs won’t be providing that flag.  Therefore "flag not present" must always be "default behaviour" and "flag present" must be "modified behaviour".  Sometimes the modified behaviour is telling it to do something it wasn’t doing before, other times it’s telling it to *not* do something that it was doing before.

    So yes, it leads to non-ideal names for things, but there’s not much else you can do to maintain backwards compatibility short of defining an entire new API (which is a bit of overkill for a simple flag).

  19. Blech says:

    Ah, but WM_SHOWWINDOW is related to the ShowWindow API, which can both show/hide and resize windows (minimising, maximising, and restoring are all size changes).  So they are related.

    I’m related to my father, but it doesn’t mean he can get passed to all the functions I can.

  20. Guest post by Joe Castro, WPF product team developer This document covers the design and some implementation

Comments are closed.