4

In an experimental code, when creating three top level windows with hierarchical ownership I am seeing weird behavior when dismissing them in reverse order.

Code:

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    MSG msg;
    WNDCLASS wndClass;
    WCHAR className[] = L"OwnedWindowsWeirdness";

    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.lpszMenuName = NULL;
    wndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndClass.hInstance = hInstance;
    wndClass.lpfnWndProc = WndProc;
    wndClass.lpszClassName = className;
    wndClass.style = CS_HREDRAW | CS_VREDRAW;

    if(!RegisterClassW(&wndClass))
    {
        MessageBoxW(0, L"Unable to register class...Exiting!", className, MB_OK);
        return -1;
    }

    HWND hwnd1 = CreateWindowW(className, L"Main Window", WS_OVERLAPPEDWINDOW, 
                               CW_USEDEFAULT, CW_USEDEFAULT, 500, 400, 
                               NULL, 0, hInstance, 0);
    HWND hwnd2 = CreateWindowW(className, L"Main Window > Window 2", WS_OVERLAPPEDWINDOW, 
                               CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, 
                               hwnd1, 0, hInstance, 0);

    HWND hwnd3 = CreateWindowW(className, L"Main Window > Window 2 > Window 3", WS_OVERLAPPEDWINDOW,
                               CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,
                               hwnd2, 0, hInstance, 0);

    ShowWindow(hwnd1, SW_SHOWNORMAL);
    UpdateWindow(hwnd1);

    ShowWindow(hwnd2, SW_SHOWNORMAL);
    UpdateWindow(hwnd2);

    ShowWindow(hwnd3, SW_SHOWNORMAL);
    UpdateWindow(hwnd3);

    while(GetMessage(&msg, 0,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    // Subdeveloper: Purposefully not complicating the code by calling PostQuitMessage/etc!
    // In absence of which, this test application will need to be closed using 
    // task manager

    return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

The code above does this:

  1. Creates a top level window Main Window
  2. Creates another top level window Window 2 and assigns its owner as Main Window
  3. Creates yet another top level window Window 3 and assigns its owner as Window 2
  4. All are non-modal if you observe closely, but with correct ownership

Now when this application is run (Release built, run on Windows 10 x64) and we close the windows in reverse order, after closing Window 2 activation goes away to existing Notepad window.

The behavior could be seen in following screen capture:

enter image description here

I am wondering what is going on. Generally this kind of behavior occurs when we miss setting correct ownership!

Secondly, when hunting around I did see that focus goes to Default IME window sometimes (i.e. Windows Input Method Editor). I think a default window is assigned for IME to every application with UI? If so maybe as soon as I create the Main Window, an IME window is created, and then on my next calls to CreateWindowW, the other 2 owned windows are created, thus changing the siblings in top level windows list? This is just a speculation for now.

Can someone explain this, and whats the "no-hack" workaround for this?

subdeveloper
  • 1,381
  • 8
  • 21
  • There isn't much sensible to say about it, the window manager giveth and taketh. Just fix the problem with if (hwnd == hwnd2 && iMsg == WM_CLOSE) SetForegroundWindow(hwnd1); in the window procedure, now it no longer has to guess which window ought to be activated next. – Hans Passant Feb 24 '20 at 14:36
  • The `Main Window` is inactive after two other window show up. You can click any window to active it even if the one overlapped by others. There is no dependent active relationship between these three top-level windows unlike child window and parent window. If you active `Main Window` by clicking it after all three windows show up, then close `Window 2`, the `Main Window` will be the active one. – Rita Han Feb 25 '20 at 08:09
  • Although an application can activate a different top-level window at any time, to avoid confusing the user, **it should do so only in response to a user action**. So user is the best role to decide which window should be activated. The question for you is **the system** activate a different top-level window belongs a different application based on your experiment, how the system decide which one to active, right? – Rita Han Feb 25 '20 at 08:18
  • @RitaHan-MSFT and HansPassant even if we leave relationship aside for a moment, the three top-level windows are created in quick succession, won't that be enough to decide the Next/Previous order in Windows internal bookkeeping? This is not a show stopper as such, but the behavior is something astonishing to me. Are there any sources/documents around this? – subdeveloper Feb 25 '20 at 16:39
  • 1
    @rit: Users have been taught literally for decades how windows form an order in Z direction. Users *expect* windows to get activated in reverse Z order when the top-level window gets dismissed. The system never *needs* to guess. But apparently it does, and gets it wrong. – IInspectable Feb 26 '20 at 12:47
  • @subdeveloper Adding additional WM_POPUP style to Window 2 (Or use WS_CAPTION | WS_POPUPWINDOW replace WS_OVERLAPPEDWINDOW.) solves the issue for me. You can have a try. Title bars are optional for pop-up windows; otherwise, pop-up windows are the same as overlapped windows of the WS_OVERLAPPED style. – Rita Han Mar 09 '20 at 08:53
  • @RitaHan-MSFT, thanks for trying. I'd already done all this homework before posting the original question :). Though it behaves correctly after that, but still the question remains of the perceived behavior from standard overlapped windows when not doing anything crazy with ownership or parenting! – subdeveloper Mar 12 '20 at 14:59
  • @subdeveloper I was wondering what's the right direction for application design. Most applications typically use the `WS_OVERLAPPEDWINDOW` style to create the main window. Multiple-overlapped-windows is rare so I confirm with your use case. As for activate order issue I've consulted related engineer for helping on this and update to you if there is any progress. – Rita Han Mar 13 '20 at 02:07
  • @subdeveloper With `WM_POPUP`, the ownership will be picked, you will see the behavior you expected. Without `WM_POPUP`, system will find the next window to activate, this is undocumented. – Rita Han Mar 19 '20 at 05:47
  • @RitaHan-MSFT thanks for the efforts at your end! Now I too think this is undocumented and in line with what Hans also commented above, its up to Window manager's mercy. – subdeveloper Mar 25 '20 at 06:31

1 Answers1

1

Adding additional WS_POPUP style to Window 2 (Or use WS_CAPTION | WS_POPUPWINDOW replace WS_OVERLAPPEDWINDOW.) solves the issue for me.

With WS_POPUP, the ownership will be picked, you will see the behavior you expected. Without WS_POPUP, system will find the next window to activate, this is undocumented.

Rita Han
  • 9,574
  • 1
  • 11
  • 24