//=====================================================================
//TITLE:
// 父子窗口分屬不同消息循環(huán)在WinXP和WinCE的差異
//AUTHOR:
// norains
//DATE:
// Monday 19- April-2010
//ENVIRONMENT:
// WINDOWS CE 5.0
// WINDOWS XP SP3
//=====================================================================
老實(shí)說(shuō),這題目起的有點(diǎn)拗口,讀起來(lái)不太滑溜;但更有意思的是,本文所說(shuō)的情況比較特殊,并不一定大家都能碰上。不過(guò),如果碰上了,估計(jì)找起來(lái)還特別費(fèi)勁,特別是對(duì)于代碼是從WinCE遷移到WinXP上的朋友而言。
在開始進(jìn)行本文的討論之前,先確定如下特殊條件:
1. 主線程創(chuàng)建父窗口。
2. 創(chuàng)建一個(gè)線程,并在該線程中創(chuàng)建子窗口,且該線程有子窗口的消息循環(huán)。
3. 進(jìn)入到父窗口的消息循環(huán)。
可能用文字描述有點(diǎn)抽象,我們來(lái)看看具體的代碼:
- #include <string>
-
- #ifdef UNICODE
- #ifndef TSTRING
- #define TSTRING std::wstring
- #endif
- #else
- #ifndef TSTRING
- #define TSTRING std::string
- #endif
- #endif //#ifdef UNICODE
-
- HWND g_hWndParent = NULL;
- HANDLE hEventNotify = NULL;
- LRESULT CALLBACK WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
- {
- return DefWindowProc(hWnd,wMsg,wParam,lParam);
- }
- BOOL MyRegisterClass(const TSTRING &strClassName)
- {
- WNDCLASS wc;
- wc.style = 0;
- wc.lpfnWndProc = WndProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- wc.hInstance = GetModuleHandle(NULL);
- wc.hIcon = NULL;
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
- wc.lpszMenuName = NULL;
- wc.lpszClassName = strClassName.c_str();
- wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
- return RegisterClass(&wc);
- }
- HWND MyCreateWindow(const TSTRING &strClassName,const TSTRING &strWndName,HWND hWndParent,DWORD dwStyle,DWORD dwExStyle)
- {
- RECT rcArea = {0};
- SystemParametersInfo(SPI_GETWORKAREA, 0, &rcArea, 0);
- return CreateWindowEx(dwExStyle,
- strClassName.c_str(),
- strWndName.c_str(),
- dwStyle,
- rcArea.left,
- rcArea.top,
- rcArea.right - rcArea.left,
- rcArea.bottom - rcArea.top,
- hWndParent,
- NULL,
- GetModuleHandle(NULL),
- 0);
-
- }
- DWORD WINAPI ThreadCreateWnd(LPVOID pArg)
- {
- if(MyRegisterClass(TEXT("CHILD_CLASS")) == FALSE)
- {
- return 0x10;
- }
- if(MyCreateWindow(TEXT("CHILD_CLASS"),TEXT("CHILD_NAME"),g_hWndParent,WS_CHILD|WS_VISIBLE,0) == NULL)
- {
- return 0x20;
- }
-
- OutputDebugString(TEXT("Finish Create child window/r/n"));
- SetEvent(hEventNotify);
-
- MSG msg;
- while(GetMessage(&msg,NULL,0,0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- return 0;
- }
-
-
- #ifdef _WIN32_WCE
- int WINAPI WinMain( HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPTSTR lpCmdLine,
- int nCmdShow)
- #else
- int APIENTRY _tWinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPTSTR lpCmdLine,
- int nCmdShow)
- #endif //#ifdef _WIN32_WCE
- {
-
- if(MyRegisterClass(TEXT("PARENT_CLASS")) == FALSE)
- {
- return 0x10;
- }
- g_hWndParent = MyCreateWindow(TEXT("PARENT_CLASS"),TEXT("PARENT_NAME"),NULL,WS_POPUP|WS_VISIBLE,0);
- if(g_hWndParent == NULL)
- {
- return 0x20;
- }
-
- HANDLE hEventNotify = CreateEvent(NULL,FALSE,FALSE,NULL);
-
- CreateThread(NULL,NULL,ThreadCreateWnd,FALSE,FALSE,NULL);
- WaitForSingleObject(hEventNotify,INFINITE);
-
- MSG msg;
- while(GetMessage(&msg,NULL,0,0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- return 0;
- }
我承認(rèn),為了突出文章的主題,這代碼寫得有點(diǎn)崢嶸,實(shí)在不好理解。所以,就以流程圖說(shuō)一下流程:
如果你是WinCE環(huán)境下,剛剛的那段代碼跑的非常順暢,一點(diǎn)問(wèn)題都沒有;但如果是在WinXP,那么一切都會(huì)改變,你會(huì)發(fā)現(xiàn),程序沒有響應(yīng),被卡死了。仔細(xì)追蹤,你會(huì)發(fā)現(xiàn)出問(wèn)題的是在流程圖中的"創(chuàng)建子窗口"這一項(xiàng),具體來(lái)說(shuō),是CreateWindowEx函數(shù)根本沒有返回!
解決方式也非常簡(jiǎn)單,在調(diào)用CreateWindowEx函數(shù)的時(shí)候,傳入一個(gè)WinCE所不具備的WS_EX_NOPARENTNOTIFY即可。當(dāng)你傳入該數(shù)值時(shí),CreateWindowEx就會(huì)如你所愿,順順當(dāng)當(dāng)返回。
由此我們或多或少可以知道WinXP和WinCE在消息處理上的小小差異:如果沒有WS_EX_NOPARENTNOTIFY,那么子窗口創(chuàng)建時(shí),需要等待父窗口的回應(yīng)。而在我們示例的代碼中,父窗口還沒有進(jìn)入消息循環(huán),無(wú)法正常響應(yīng)子窗口的動(dòng)作,于是便造成了死鎖。而WinCE則沒有這方面的問(wèn)題,子窗口根本就不必等待父窗口的回應(yīng),相應(yīng)的創(chuàng)建完畢后,直接返回。從這個(gè)意義上來(lái)說(shuō),我們一刀切地認(rèn)為(可能實(shí)際底層代碼并不一定如此),雖然WinCE不具備WS_EX_NOPARENTNOTIFY這個(gè)數(shù)值,但實(shí)際上卻默認(rèn)具備了該數(shù)值的屬性。