您的位置:首页 > 其它

父子窗口分属不同消息循环在WinXP和WinCE的差异

2010-04-19 17:07 344 查看
//=====================================================================
//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);
//The message loop
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这个数值,但实际上却默认具备了该数值的属性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: