//=====================================================================
//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ò),如果碰上了,估計找起來(lái)還特別費勁,特別是對于代碼是從WinCE遷移到WinXP上的朋友而言。
在開(kāi)始進(jìn)行本文的討論之前,先確定如下特殊條件:
1. 主線(xiàn)程創(chuàng )建父窗口。
2. 創(chuàng )建一個(gè)線(xiàn)程,并在該線(xiàn)程中創(chuàng )建子窗口,且該線(xiàn)程有子窗口的消息循環(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;
- }
我承認,為了突出文章的主題,這代碼寫(xiě)得有點(diǎn)崢嶸,實(shí)在不好理解。所以,就以流程圖說(shuō)一下流程:
如果你是WinCE環(huán)境下,剛剛的那段代碼跑的非常順暢,一點(diǎn)問(wèn)題都沒(méi)有;但如果是在WinXP,那么一切都會(huì )改變,你會(huì )發(fā)現,程序沒(méi)有響應,被卡死了。仔細追蹤,你會(huì )發(fā)現出問(wèn)題的是在流程圖中的"創(chuàng )建子窗口"這一項,具體來(lái)說(shuō),是CreateWindowEx函數根本沒(méi)有返回!
解決方式也非常簡(jiǎn)單,在調用CreateWindowEx函數的時(shí)候,傳入一個(gè)WinCE所不具備的WS_EX_NOPARENTNOTIFY即可。當你傳入該數值時(shí),CreateWindowEx就會(huì )如你所愿,順順當當返回。
由此我們或多或少可以知道WinXP和WinCE在消息處理上的小小差異:如果沒(méi)有WS_EX_NOPARENTNOTIFY,那么子窗口創(chuàng )建時(shí),需要等待父窗口的回應。而在我們示例的代碼中,父窗口還沒(méi)有進(jìn)入消息循環(huán),無(wú)法正常響應子窗口的動(dòng)作,于是便造成了死鎖。而WinCE則沒(méi)有這方面的問(wèn)題,子窗口根本就不必等待父窗口的回應,相應的創(chuàng )建完畢后,直接返回。從這個(gè)意義上來(lái)說(shuō),我們一刀切地認為(可能實(shí)際底層代碼并不一定如此),雖然WinCE不具備WS_EX_NOPARENTNOTIFY這個(gè)數值,但實(shí)際上卻默認具備了該數值的屬性。