Is this a really bug with CreateWindowEx or am I just confused?


Somebody sent me email pointing out strange behavior in the MessageBox function if you fail a window creation by returning −1 from the WM_CREATE message. On the other hand, returning FALSE from WM_NCCREATE seems to work just fine. "So why the difference with WM_CREATE?"

#include <windows.h>

LRESULT CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_CREATE:
            return -1;
        
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrev,
    LPSTR lpCmdLine, int nShowCmd)
{
    MSG msg;
    HWND hWnd;
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc   = WndProc;
    wc.hInstance     = hInst;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = "TestApp";
    
    if(!RegisterClass(&wc)){
        MessageBox(NULL, "Error creating class",
            "Test App", MB_ICONERROR);
        return 1;
    }
    
    hWnd = CreateWindow(wc.lpszClassName, "Test App",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT, NULL, 0, hInst, NULL);
    if(!hWnd){
        MessageBox(NULL, "Error creating window",
            "Test App", MB_ICONERROR);
        return 1;
    }
    
    ShowWindow(hWnd, nShowCmd);
    UpdateWindow(hWnd);

    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    return (int)msg.wParam;
}

You already know enough to solve this puzzle. You just need to connect the dots.

(In fact, the person who sent me this topic did so a year after I already answered it. But I'm repeating it here because the original answer was accidentally destroyed.)

Comments (10)
  1. A. Skrobov says:

    Do you mean, WM_QUIT gets posted before the control returns to WinMain, thus making the modal loop in MessageBox exit immediately?

  2. Jack Mathews says:

    As the reference docs say, WM_CREATE is called after the window is created, but before its set visible, and that failure from WM_CREATE causes the window to be destroyed.  This means that failing from WM_CREATE causes WM_DESTROY to be sent, which posts a quit message, which quits the message box.  Presumably, failing a WM_NCCREATE doesn't cause a matching WM_DESTROY (perhaps it causes a WM_NCDESTROY?!)

    Of course, this behavior is counter-intuitive to C++ programmers.  If a constructor fails (by way of an exception of course), you never get a destructor call on a partially constructed object.  I'm sure the way most people think of it is "if I fail a create, I clean up after myself first.  If a create passes, THEN call WM_DESTROY because only them am I a fully constructed window"

  3. Ben says:

    The question contains two conceptual errors, I think. (A long time since I did this for real…)

    First error is as A. Skrobov, the quit message on the queue kills the dialog. Questioner assumes it does not.

    Second half is, Questioner assumes WM_DESTROY will be sent since the window is being destroyed. But since it is only partially created it only needs to be partially destroyed. WM_NCDESTROY will still occur, since we reached WM_NCCREATE, but WM_DESTROY will not because we never reached that stage of creation, so there should be nothing to destroy. So no quit message is posted, and the messagebox shows fine.

  4. Jim says:

    Not destroyed, just well hidden:

    web.archive.org/…/blog

    (then scroll up slightly, since the panel at the top covers the start of the post)

  5. Ken Hagan says:

    I'm guessing from the answers so far that the "strange behaviour" of the MessageBox function is that the second message is never seen. However, this isn't actually stated in the original article.

  6. Killer{R} says:

    Its all obvous here… BTW I always wondered about that different return results meaning in quite same messages:

    WM_CREATE – "If an application processes this message, it should return zero to continue creation of the window. If the application returns –1, the window is destroyed"

    vs

    WM_NCCREATE – "Nonzero if the nonclient area is created. It is 0 if an error occurs; the Create function will return failure "

    WM_PAINT – "An application returns zero if it processes this message."

    vs

    WM_ERASEBKGND – "An application should return nonzero if it erases the background; otherwise, it should return zero."

    WM_SIZING, WM_MOVING – "An application should return TRUE if it processes this message"

    vs

    WM_WINDOWPOSCHANGING – "If an application processes this message, it should return zero."

    Why so much mess in quite similar functionality? Is it just a historical random or some compatibility tricks hidden here?

  7. Anonymous Coward says:

    @Killer: It's almost like Microsoft doesn't want us to get it right.

  8. Medinoc says:

    @Killer: You forgot the WM_CTLCOLORxxx messages, which ignore the DWL_MSGRESULT, and whose documentation said "cast the return value to a BOOL" long after the DLGPROC return type was changed to INT_PTR, despite being the only reason I can see for a DLGPROC to return an INT_PTR in the first place!

  9. acq says:

    Now reading the answers here and the online help, I think I've understood it:

    WM_NCCREATE Message: "If the application returns FALSE, the CreateWindow or CreateWindowEx function will return a NULL handle."

    WM_CREATE Message: "If the application returns –1, the window is destroyed and the CreateWindowEx or CreateWindow function returns a NULL handle."

    Raymonds "Modality, part 3: The WM_QUIT message": "The other important thing about modality is that a WM_QUIT message always breaks the modal loop. Remember this in your own modal loops! If ever you call  the PeekMessage function or  The [typo fixed 10:30am] GetMessage function and get a WM_QUIT message, you must not only exit your modal loop, but you must also re-generate the WM_QUIT message (via  the PostQuitMessage message) so the next outer layer will see the WM_QUIT message and do its cleanup as well."

    So I guess "the window is destroyed" in the second reference means (I think it can be more clearly written in the documentation) is "the WM_DESTROY is called." I guess it's called because CreateWindow is itself called before the application even entered message loop, and CreateWindow somehow manages to call the WndProc with WM_CREATE before. So it probably also does other calls. And the call to WM_DESTROY of given WndProc in the given application finally performs: "PostQuitMessage(0)"

    Now how is MessageBox modal loop is written? Based on the third (Raymond's) reference the modal loop should detect the WM_QUIT and finish itself. That means that the GetMessage or PeekMessage of that modal loop must be used with the second parameter NULL:

    "If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed."

    Now in Raymond's example application at the moment MessageBox modal loop runs it sees that WM_QUIT and nicely closes itself.

    Not obvious at all for uninitiated, I don't know where in the MSDN the exact "protocol" of calls to WndProc is documented (like CreateWindow calls that that and that, and all that without the queue) and where I'd otherwise find the info from "Modality, part 3: The WM_QUIT message"

    Again, it appears that Raymond provided more alone than the official documentation maintained for years (if it's not so I'd appreciate pointers, thanks).

  10. Worf says:

    It would be nice if there was a way for Windows to hint this stuff. You're in Visual Studio, you do something like this, then a tab on the output pops up like "Hinta" listing things that went strange but the dev might not notice.

    In this case, it would say "filename.c:37 – MessageBox called while quitting (WM_QUIT or PostQuitMessage called before this MessageBox). MessageBox not displayed. MessageBox contents: …"

    Because it's going to happen again and again.

    And no, it only exists if enabled in the build. Call it "Debugging Aide – helps solve common Windows problems.

Comments are closed.

Skip to main content