0

In one of my projects, I need to create a window in a non-main thread. I have never done that so I don't much experience on that.

According to the MSDN documentation and the SO question, I should be able to create a window in other thread, but I cannot succeed. Even though, in thread start routine, I register a window class, create a window and provide a message loop, the thread starts and exits immediately. In addition, I cannot debug the thread start routine so I cannot hit the break points inside it.

Is there something I am missing? I hope I don't miss anything silly.

Please consider the following demo. Thank you for taking your time.

#include <Windows.h>
#include <tchar.h>

HANDLE hThread;
DWORD WINAPI OtherUIThreadFunc(LPVOID args);

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

HWND m_hwnd;
MSG msg;
WNDCLASSEX m_wcx;
const int MESSAGE_PROCESSED = 0;
const TCHAR* m_szClassName = _T("DemoWndCls");
const TCHAR* m_szWindowTitle = _T("Demo Window");

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int nCmdShow)
{
    hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)OtherUIThreadFunc, hInstance, 0, NULL);
    /*MSG msg;

    ZeroMemory(&m_wcx, sizeof(m_wcx));
    m_wcx.cbSize = sizeof(m_wcx);
    m_wcx.style = CS_VREDRAW | CS_HREDRAW;
    m_wcx.hInstance = hInstance;
    m_wcx.lpszClassName = m_szClassName;
    m_wcx.lpfnWndProc = WndProc;
    m_wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    m_wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    m_wcx.hbrBackground = (HBRUSH)COLOR_WINDOW;

    if (!RegisterClassEx(&m_wcx))
        return false;

    m_hwnd = CreateWindowEx(0, m_wcx.lpszClassName, m_szWindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 480, 360, NULL, NULL, hInstance, NULL);

    if (!m_hwnd)
        return false;
    ShowWindow(m_hwnd, SW_NORMAL);
    UpdateWindow(m_hwnd);
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;*/
}

DWORD WINAPI OtherUIThreadFunc(LPVOID args)
{
    HINSTANCE hInstance = (HINSTANCE)args;

    ZeroMemory(&m_wcx, sizeof(m_wcx));
    m_wcx.cbSize = sizeof(m_wcx);
    m_wcx.style = CS_VREDRAW | CS_HREDRAW;
    m_wcx.hInstance = hInstance;
    m_wcx.lpszClassName = m_szClassName;
    m_wcx.lpfnWndProc = WndProc;
    m_wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    m_wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    m_wcx.hbrBackground = (HBRUSH)COLOR_WINDOW;
    
    if (!RegisterClassEx(&m_wcx))
        return false;
    m_hwnd = CreateWindowEx(0, m_wcx.lpszClassName, m_szWindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 480, 360, NULL, NULL, hInstance, NULL);
    if (!m_hwnd)
        return false;
    ShowWindow(m_hwnd, SW_NORMAL);
    UpdateWindow(m_hwnd);
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
LRESULT WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        return MESSAGE_PROCESSED;
    case WM_DESTROY:
        PostQuitMessage(0);
        return MESSAGE_PROCESSED;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    
}
jtxkopt
  • 916
  • 1
  • 8
  • 21
  • 1
    What do you think happened in WinMain *after* you created that child thread ? After gelling on that for moment, what do you think happened to your *program* after WinMain returned ? – WhozCraig Feb 05 '22 at 11:33
  • @WhozCraig This is just demo program. I would expect that the process doesn't kill and becomes alive. – jtxkopt Feb 05 '22 at 11:42
  • The thread isn't a separate process. It is part of the first process (and shares the global variables too). – Weather Vane Feb 05 '22 at 11:44
  • @RbMm I stated that I cannot hit the bps as expected. I see your point but I really cannot do that in a way I cannot understand why. – jtxkopt Feb 05 '22 at 11:44
  • @WeatherVane *The thread isn't a separate process. It is part of the first process (and shares the global variables too).* - I know that. – jtxkopt Feb 05 '22 at 11:45
  • So why do you think it will be left running when the first thread immediately exits (it has no other code except the `CreateThread` call)? – Weather Vane Feb 05 '22 at 11:46
  • @WeatherVane I may be wrong but there is alive thread so the process should be alive, isn't that right? – jtxkopt Feb 05 '22 at 11:49
  • @RbMm I changed the class name, but the result remained the same. – jtxkopt Feb 05 '22 at 11:53
  • 1
    No, that isn't right. What happens when you put a `WaitForSingleObjet(hThread, INFINITE);` after your thread creation. You might be surprised. – WhozCraig Feb 05 '22 at 11:53
  • you need debug your code, what api fail ? with what error ? etc – RbMm Feb 05 '22 at 11:56
  • 3
    Yeah, not for me. Sry. I used your exact code from your question, built the application as subsystem windows, added the WFSO call, and up pops your window from your child thread. As soon as the window is closed, the child thread finishes and exits, the WFSO returns because the thread handled is now signaled, and the main thread exits, just as I suspected it would. – WhozCraig Feb 05 '22 at 11:57
  • @WhozCraig Yeah, sorry, I just saw that it was alive and running in background. – jtxkopt Feb 05 '22 at 11:59
  • 3
    One thing pretty important, btw. Stop casting all your function callback sets. The function prototypes are explicit, and should be followed to the letter. – WhozCraig Feb 05 '22 at 11:59
  • A call `WaitForSingleObject(hThread, INFINITE)` solved the problem. That was the reason why I cannot the debug the code because it exited immediately. Thank you all. I learned something really important. – jtxkopt Feb 05 '22 at 12:03

1 Answers1

1

Window creation succeeds (in theory, anyway). The issue is that the primary thread moves one to return, which causes the runtime to terminate the process.

To solve the issue you will have to keep the primary thread alive. A call to WaitForSingleObject, or a message loop are possible options.


This is mostly a result of following the conventions of C and C++. In either case returning from the main function is equivalent to calling the exit() function. This explains why returning from the primary thread tears down the entire process.

Bonus reading: If you return from the main thread, does the process exit?

IInspectable
  • 46,945
  • 8
  • 85
  • 181