//========================================================================
//TITLE:
// 在非主線程中創建窗口
//AUTHOR:
// norains
//DATE:
// Saturday 29-December-2007
//Environment:
// VS2005 + SDK-WINCE5.0-MIPSII
//========================================================================
很多朋友都會有過這樣的經歷,為什麼在主線程中創建窗口且窗口工作很正常,但一移到非主線程(有的朋友喜歡叫它為工作線程),卻無法正常工作.本文就這個問題和各位探討,可能無法做到盡善盡美,但能拋磚引玉也算是欣慰了.
在主線程中創建一個能夠正常工作的窗口,估計地球人都知道.
這是一段工作正常的代碼:
: #000000">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.
an>
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</span>)
{
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 =1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]
"> 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()則完全可以讓主線程正常退出.
事實告訴我們,非主線程創建窗口也能工作正常,
只要我們注意一點:消息循環必須要和創建窗口在同一線程!