YoonSeok OH
YoonSeok OH

Reputation: 735

Multiple background threads calling SetForegroundWindow() act as if they are all foreground threads at the same time

Why does calling SetForegroundWindow() in multiple background threads act as if they are all foreground threads at the same time?

In my understanding, calling SetForegroundWindow() after creating a window in a background thread will set the calling thread as the foreground thread. However, whenever another thread calls SetForegroundWindow(), then that thread becomes the foreground thread, and the previous foreground thread returns to the background.

I've tested 3 cases by clicking the title bar of a window and dragging it.

The results of each cases were:

The reason why I express "as if they are all foreground threads" in my question is that case 3 should act the same as case 2 unless the clicked window is already in the foreground.

References I looked at are as follows:

[EDIT]

Here's the sample code that reproduces Case 2. If SetForegroundWindow is called for each windows in each thread, Case 3 is reproduced. Case 1 can be reproduced if SINGLETHREAD is defined.

#include <Windows.h>

HINSTANCE g_hInstance = nullptr;
HANDLE g_hEvent0 = nullptr;
HANDLE g_hEvent1 = nullptr;

//#define SINGLETHREAD

LRESULT WINAPI WndProc(HWND _hWnd, UINT _unMessage, WPARAM _wParam, LPARAM _lParam)
{
    switch (_unMessage)
    {
    case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
    default:
        break;
    }
    
    return DefWindowProc(_hWnd, _unMessage, _wParam, _lParam);
}

DWORD WINAPI Thread0(LPVOID _lpParam)
{
    WNDCLASSEXW stWndClassExW0
    {
        sizeof(WNDCLASSEXW),
        CS_VREDRAW | CS_HREDRAW,
        &WndProc,
        0,
        0,
        g_hInstance,
        nullptr,
        static_cast<HICON__*>(LoadImageW(nullptr, static_cast<const wchar_t*>(IDC_ARROW), static_cast<unsigned int>(IMAGE_CURSOR), 0, 0, static_cast<unsigned int>(LR_SHARED))),
        static_cast<HBRUSH__*>(GetStockObject(WHITE_BRUSH)),
        L"MenuName",
        L"ClassName0",
        nullptr
    };

    ATOM usWindowClass0 = RegisterClassExW(&stWndClassExW0);

    HWND hWnd0 = CreateWindowExW(0,
                                 reinterpret_cast<const wchar_t*>(usWindowClass0),
                                 L"0",
                                 WS_OVERLAPPEDWINDOW,
                                 0,
                                 0,
                                 1200,
                                 800,
                                 nullptr,
                                 nullptr,
                                 g_hInstance,
                                 nullptr);

    ShowWindow(hWnd0, SW_SHOW);

    //SetForegroundWindow(hWnd0);

    MSG msg{};

    while (true)
    {
        if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE) != 0)
        {
            if (msg.message == WM_QUIT)
            {
                break;
            }

            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

    SetEvent(g_hEvent0);

    return 0ul;
}

DWORD WINAPI Thread1(LPVOID _lpParam)
{
    WNDCLASSEXW stWndClassExW1
    {
        sizeof(WNDCLASSEXW),
        CS_VREDRAW | CS_HREDRAW,
        &WndProc,
        0,
        0,
        g_hInstance,
        nullptr,
        static_cast<HICON__*>(LoadImageW(nullptr, static_cast<const wchar_t*>(IDC_ARROW), static_cast<unsigned int>(IMAGE_CURSOR), 0, 0, static_cast<unsigned int>(LR_SHARED))),
        static_cast<HBRUSH__*>(GetStockObject(WHITE_BRUSH)),
        L"MenuName",
        L"ClassName1",
        nullptr
    };

    ATOM usWindowClass1 = RegisterClassExW(&stWndClassExW1);

    HWND hWnd1 = CreateWindowExW(0,
                                 reinterpret_cast<const wchar_t*>(usWindowClass1),
                                 L"1",
                                 WS_OVERLAPPEDWINDOW,
                                 200,
                                 200,
                                 1200,
                                 800,
                                 nullptr,
                                 nullptr,
                                 g_hInstance,
                                 nullptr);

    ShowWindow(hWnd1, SW_SHOW);

    //SetForegroundWindow(hWnd1);

    MSG msg{};

    while (true)
    {
        if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE) != 0)
        {
            if (msg.message == WM_QUIT)
            {
                break;
            }

            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

    SetEvent(g_hEvent1);

    return 0ul;
}

int WINAPI wWinMain(_In_ HINSTANCE _hInstance,
                    _In_opt_ HINSTANCE _hPrevInstance,
                    _In_ LPWSTR _lpCmdLine,
                    _In_ int _nCmdShow)
{
    g_hInstance = _hInstance;

#ifdef SINGLETHREAD

    WNDCLASSEXW stWndClassExW0
    {
        sizeof(WNDCLASSEXW),
        CS_VREDRAW | CS_HREDRAW,
        &WndProc,
        0,
        0,
        g_hInstance,
        nullptr,
        static_cast<HICON__*>(LoadImageW(nullptr, static_cast<const wchar_t*>(IDC_ARROW), static_cast<unsigned int>(IMAGE_CURSOR), 0, 0, static_cast<unsigned int>(LR_SHARED))),
        static_cast<HBRUSH__*>(GetStockObject(WHITE_BRUSH)),
        L"MenuName",
        L"ClassName0",
        nullptr
    };

    ATOM usWindowClass0 = RegisterClassExW(&stWndClassExW0);

    HWND hWnd0 = CreateWindowExW(0,
                                 reinterpret_cast<const wchar_t*>(usWindowClass0),
                                 L"0",
                                 WS_OVERLAPPEDWINDOW,
                                 0,
                                 0,
                                 1200,
                                 800,
                                 nullptr,
                                 nullptr,
                                 g_hInstance,
                                 nullptr);

    ShowWindow(hWnd0, SW_SHOW);

    WNDCLASSEXW stWndClassExW1
    {
        sizeof(WNDCLASSEXW),
        CS_VREDRAW | CS_HREDRAW,
        &WndProc,
        0,
        0,
        g_hInstance,
        nullptr,
        static_cast<HICON__*>(LoadImageW(nullptr, static_cast<const wchar_t*>(IDC_ARROW), static_cast<unsigned int>(IMAGE_CURSOR), 0, 0, static_cast<unsigned int>(LR_SHARED))),
        static_cast<HBRUSH__*>(GetStockObject(WHITE_BRUSH)),
        L"MenuName",
        L"ClassName1",
        nullptr
    };

    ATOM usWindowClass1 = RegisterClassExW(&stWndClassExW1);

    HWND hWnd1 = CreateWindowExW(0,
                                 reinterpret_cast<const wchar_t*>(usWindowClass1),
                                 L"1",
                                 WS_OVERLAPPEDWINDOW,
                                 200,
                                 200,
                                 1200,
                                 800,
                                 nullptr,
                                 nullptr,
                                 g_hInstance,
                                 nullptr);

    ShowWindow(hWnd1, SW_SHOW);

    tagMSG stMSG{};

    while (true)
    {
        if (PeekMessageW(&stMSG, nullptr, 0, 0, static_cast<unsigned int>(PM_REMOVE)) != 0)
        {
            if (stMSG.message == static_cast<unsigned int>(WM_QUIT))
            {
                break;
            }

            TranslateMessage(&stMSG);
            DispatchMessageW(&stMSG);
        }
    }
#else

    g_hEvent0 = CreateEvent(nullptr, 0, 0, nullptr);
    g_hEvent1 = CreateEvent(nullptr, 0, 0, nullptr);

    DWORD dwThreadID0 = 0ul;
    DWORD dwThreadID1 = 0ul;

    HANDLE hThread0 = CreateThread(nullptr, 0, &Thread0, nullptr, 0, &dwThreadID0);
    HANDLE hThread1 = CreateThread(nullptr, 0, &Thread1, nullptr, 0, &dwThreadID1);

    WaitForSingleObject(g_hEvent0, INFINITE);
    WaitForSingleObject(g_hEvent1, INFINITE);

#endif // SINGLETHREAD

    return 0;
}

Upvotes: 0

Views: 159

Answers (0)

Related Questions