在非主執行緒中建立視窗

峻峰飛陽發表於2019-04-15

   很多朋友都會有過這樣的經歷,為什麼在主執行緒中建立視窗且視窗工作很正常,但一移到非主執行緒(有的朋友喜歡叫它為工作執行緒),卻無法正常工作.本文就這個問題和各位探討,可能無法做到盡善盡美,但能拋磚引玉也算是欣慰了.
   
   在主執行緒中建立一個能夠正常工作的視窗,估計地球人都知道.
   
   這是一段工作正常的程式碼: 

#include "windows.h"

HWND g_hWnd = NULL;
HINSTANCE g_hInst;


LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
{
    return DefWindowProc(hWnd,wMsg,wParam,lParam);
}

void CreateWnd(void)
{    

    WNDCLASS wc = {0};
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = g_hInst;
    wc.hIcon         = NULL; 
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = TEXT("SimpleWindow");

    RegisterClass(&wc);

    g_hWnd = CreateWindowEx(0,
                TEXT("SimpleWindow"),
                TEXT("SimpleWindow"),
                WS_VISIBLE,
                0,
                0,
                200,
                200,
                NULL, 
                NULL, 
                g_hInst, 
                0);
}



int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
     // TODO: Place code here.
    
    g_hInst = hInstance;

    CreateWnd();

    //The message loop    
    MSG msg;
    while(GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

 如果我們建立一個執行緒,然後在這個執行緒中建立視窗,看看帶給我們的是什麼:

#include "windows.h"

HWND g_hWnd = NULL;
HINSTANCE g_hInst;


LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
{
    return DefWindowProc(hWnd,wMsg,wParam,lParam);
}

void CreateWnd(void)
{    

    WNDCLASS wc = {0};
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = g_hInst;
    wc.hIcon         = NULL; 
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = TEXT("SimpleWindow");

    RegisterClass(&wc);

    g_hWnd = CreateWindowEx(0,
                TEXT("SimpleWindow"),
                TEXT("SimpleWindow"),
                WS_VISIBLE,
                0,
                0,
                200,
                200,
                NULL, 
                NULL, 
                g_hInst, 
                0);
}


DWORD CreateThread(PVOID pArg)
{
    CreateWnd();
    return 0;
}


int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
     // TODO: Place code here.
    
    g_hInst = hInstance;

    HANDLE hThrd = CreateThread(NULL,0,CreateThread,NULL,0,NULL);
    CloseHandle(hThrd);

    //The message loop    
    MSG msg;
    while(GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

    我們似乎什麼都沒見到,只是視窗一閃,啥都沒了.因為g_hWnd為全域性變數,我們的理智告訴我們,在主執行緒沒有退出之前,g_hWnd是不會銷燬的.而用斷點除錯,將會發現在WndProc函式中只能接收WM_CREATE及以後一些訊息,之後的再也收不到了,特別是WM_PAINT似乎就憑空消失了!那麼,程式碼什麼都沒變更,只是移動到了分執行緒中,為何會出現這個問題呢?
    
    一切似乎很簡單,在MSDN中我們找到了答案(原文見:http://support.microsoft.com/kb/90975/en-us):
    
    In a multithreaded application, any thread can call the CreateWindow() API to create a window. There are no restrictions on which thread(s) can create windows.

It is important to note that the message loop and window procedure for the window must be in the thread that created the window. If a different thread creates the window, the window won't get messages from DispatchMessage(), but will get messages from other sources. Therefore, the window will appear but won't show activation or repaint, cannot be moved, won't receive mouse messages, and so on.

    該段話大意是:視窗在任何執行緒中都可以建立,但訊息迴圈必須要和建立視窗在同一執行緒,否則視窗將無法從DispatchMessage()獲取任何訊息!
    
    原來如此,最重要是這麼一句:It is important to note that the message loop and window procedure for the window must be in the thread that created the window.
    
    好吧,那麼我們在支執行緒中放置訊息迴圈程式碼,看看是什麼結果吧:

#include "windows.h"

HWND g_hWnd = NULL;
HINSTANCE g_hInst;


LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
{
    return DefWindowProc(hWnd,wMsg,wParam,lParam);
}

void CreateWnd(void)
{    

    WNDCLASS wc = {0};
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = g_hInst;
    wc.hIcon         = NULL; 
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = TEXT("SimpleWindow");

    RegisterClass(&wc);

    g_hWnd = CreateWindowEx(0,
                TEXT("SimpleWindow"),
                TEXT("SimpleWindow"),
                WS_VISIBLE,
                0,
                0,
                200,
                200,
                NULL, 
                NULL, 
                g_hInst, 
                0);
}


DWORD CreateThread(PVOID pArg)
{
    CreateWnd();

    //The message loop    
    MSG msg;
    while(GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }


    return 0;
}


int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
     // TODO: Place code here.
    
    g_hInst = hInstance;

    HANDLE hThrd = CreateThread(NULL,0,CreateThread,NULL,0,NULL);
    CloseHandle(hThrd);


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

    return 0;
}

    一切正常,如同在主執行緒建立一樣!
    
    當然了,還有點需要注意的,在這個例子中,由於訊息迴圈在主執行緒和分執行緒都分別存在,如果在WndProc()呼叫PostQuitMessage(),那麼退出的也僅僅是分執行緒,而主執行緒還是會不停地在等待訊息,從而導致程式無法正常退出.不過倒不用過分擔心,和這個示例程式碼不同,在實際程式碼編寫中,在主執行緒往往都會建立主視窗,而在這個主視窗訊息處理函式呼叫PostQuitMessage()則完全可以讓主執行緒正常退出.
    
    事實告訴我們,非主執行緒建立視窗也能工作正常,只要我們注意一點:訊息迴圈必須要和建立視窗在同一執行緒!

原文地址: http://blog.csdn.net/norains/article/details/2023957

相關文章