//===================================================================== //TITLE: // 父子窗口分属不同消息循环在WinXP和WinCE的差异 //AUTHOR: // norains //DATE: // Monday 19- April-2010 //ENVIRONMENT: // WINDOWS CE 5.0 // WINDOWS XP SP3 //=====================================================================
老实说,这题目起的有点拗口,读起来不太滑溜;但更有意思的是,本文所说的情况比较特殊,并不一定大家都能碰上。不过,如果碰上了,估计找起来还特别费劲,特别是对于代码是从WinCE迁移到WinXP上的朋友而言。
在开始进行本文的讨论之前,先确定如下特殊条件:
1. 主线程创建父窗口。 2. 创建一个线程,并在该线程中创建子窗口,且该线程有子窗口的消息循环。 3. 进入到父窗口的消息循环。
可能用文字描述有点抽象,我们来看看具体的代码:
- #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;
- }
我承认,为了突出文章的主题,这代码写得有点峥嵘,实在不好理解。所以,就以流程图说一下流程:
如果你是WinCE环境下,刚刚的那段代码跑的非常顺畅,一点问题都没有;但如果是在WinXP,那么一切都会改变,你会发现,程序没有响应,被卡死了。仔细追踪,你会发现出问题的是在流程图中的"创建子窗口"这一项,具体来说,是CreateWindowEx函数根本没有返回!
解决方式也非常简单,在调用CreateWindowEx函数的时候,传入一个WinCE所不具备的WS_EX_NOPARENTNOTIFY即可。当你传入该数值时,CreateWindowEx就会如你所愿,顺顺当当返回。
由此我们或多或少可以知道WinXP和WinCE在消息处理上的小小差异:如果没有WS_EX_NOPARENTNOTIFY,那么子窗口创建时,需要等待父窗口的回应。而在我们示例的代码中,父窗口还没有进入消息循环,无法正常响应子窗口的动作,于是便造成了死锁。而WinCE则没有这方面的问题,子窗口根本就不必等待父窗口的回应,相应的创建完毕后,直接返回。从这个意义上来说,我们一刀切地认为(可能实际底层代码并不一定如此),虽然WinCE不具备WS_EX_NOPARENTNOTIFY这个数值,但实际上却默认具备了该数值的属性。
|