yidabu 2007-5-10 14:41
D语言GUI入门6 GetMessage消息循环的错误分析
D语言GUI入门6 GetMessage消息循环的错误分析知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070510点击下面网址查看原文:http://www.d-programming-language-china.org by: 孙鑫(VC++深入详解) D语言论坛 http://www.d-programming-language-china.org 增加D语言相关说明tag:GetMessage函数,windows sdk编程 消息循环的错误分析 有不少初学者学完前面内容后,编写了下面的代码: QUOTE: … HWND hwnd; hwnd=CreateWindow(…); … MSG msg; while(GetMessage(&msg,hwnd,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } … 注意代码中以粗体显示的部分。这段代码基于这样一个想法:第1章的程序只有一个窗口,而我们前面说了GetMessage函数的hWnd参数是用于指定接收属于哪一个窗口的消息,于是不少人就在消息循环中为GetMessage函数的hWnd参数指定了CreateWindow函数返回的窗口句柄。 读者可以用上述代码中的消息循环部分替换1.5节代码中的消息循环部分,然后运行程序,关闭程序。你会发现你的机器变慢了,同时按下键盘上的Ctrl + Alt + Delete键,启动Windows的任务管理器,切换到“进程”选项卡,单击“CPU”项进行排序,你会发现WinMain.exe的CPU占用率接近100,难怪机器“变慢了”。那么这是什么原因呢?实际上这个问题的答案在MSDN中就可以找到,并且就在GetMessage函数的说明文档中。不少初学者在遇到问题时,首先是头脑一片空白,接着就去找人求助,这种思想用在程序开发的学习中,没有什么好处。笔者经常遇到学员问问题,结果有不少问题的答案在MSDN关于某个函数的解释中就可看到(由于显示器的限制,有的答案需要滚动窗口才能看到 J)。所以在这里,笔者也建议读者遇到问题一定要记得查看MSDN,学会使用MSDN并从中汲取知识,将使你受用无穷。 回到正题,在前面介绍GetMessage函数时,曾说过如果hWnd参数是无效的窗口句柄或lpMsg参数是无效的指针时,GetMessage函数将返回-1。当我们关闭窗口时,调用了DestroyWindow来销毁窗口,由于窗口被销毁了,窗口的句柄当然也就是无效的句柄了,那么GetMessage将返回-1。在C/C++语言中,非0即为真,由于窗口被销毁,句柄变为无效,GetMessage总是返回-1,循环条件总是为真,于是形成了一个死循环,机器当然就“变慢了”。J 在MSDN关于GetMessage函数的说明文档中给出了下面的代码: http://msdn2.microsoft.com/en-us/library/ms644936.aspx( 本文出处: http://www.d-programming-language-china.org ) QUOTE: BOOL bRet; while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0) { if (bRet == -1) { // handle the error and possibly exit } else { TranslateMessage(&msg); DispatchMessage(&msg); } } 针对我们这个问题,可以修改上述代码如下: QUOTE: … HWND hwnd; hwnd=CreateWindow(…); … MSG msg; BOOL bRet; while( (bRet = GetMessage( &msg, hwnd, 0, 0 )) != 0) { if (bRet == -1) { // handle the error and possibly exit return -1; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } … D语言论坛 http://www.d-programming-language-china.org 按: 上面孙鑫确实讲得很细致。很多人没有看msdn的习惯,往往会忽略这点。yidabu也实验过了,窗体虽然销毁了,程序却没有退出,陷入死循环,占用CPU近100%。下面是D语言版本代码: [Copy to clipboard] [ - ]CODE: int nRet; while ( (nRet = GetMessage(&msg, hwnd, 0,0)) !=0 ) { if (nRet==-1) { return -1; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } 读者可以再次运行修改后的程序,看看运行的结果。 现在全部D语言代码是这样: [Copy to clipboard] [ - ]CODE: import std.utf; //for toUTF16z import win32.api; //for windows api import std.stdio; //for writefln extern(C) void gc_init(); extern(C) void gc_term(); extern(C) void _minit(); extern(C) void _moduleCtor(); extern(C) void _moduleDtor(); extern(C) void _moduleUnitTests(); //TextOut函数 pragma(lib,"gdi32.lib"); const char[] ClassName = "http://www.d-programming-language-china.org"; extern(Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow) { int result; gc_init(); _minit(); try { _moduleCtor(); _moduleUnitTests(); result = myWinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow); _moduleDtor(); } catch(Exception e) { printf("catch %.*s\n",e.msg); result = 0; } gc_term(); return result; } int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow) { WNDCLASSEX wc; MSG msg; HWND hWnd; wc.cbSize = WNDCLASSEX.sizeof; wc.style = CS_HREDRAW | CS_VREDRAW ; wc.lpfnWndProc = &WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; //bbs.d-programming-language-china.org注: D语言标准库的windows.d主要服务于ascii字符集的windows api,IDI_APPLICATION被定义为CHAR*,所以这里要转换成wchar* //如果IDI_APPLICATION来定义是CHAR,就可以用toUTF16z来转换 wc.hIcon = LoadIcon (cast(HINSTANCE)null,cast(wchar*)IDI_APPLICATION); wc.hIcon = LoadIcon (hInstance,toUTF16z("IDI_MYICON")); wc.hCursor = LoadCursor(cast(HINSTANCE)null,cast(wchar*)IDC_ARROW); wc.hbrBackground = cast(HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = null; //转换类名到以0结尾的宽字符集 wc.lpszClassName = toUTF16z(ClassName); wc.hIconSm = LoadIcon(hInstance,std.utf.toUTF16z("IDI_MYICON")); if ( !RegisterClassEx (&wc) ) return 0; hWnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE | WS_EX_TOPMOST, toUTF16z(ClassName), std.utf.toUTF16z("D语言论坛 http://www.d-programming-language-china.org "), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT,800,600, HWND_DESKTOP, cast(HMENU) null, hInstance, null); if (hWnd == null) { MessageBoxEx(null,toUTF16z("欢迎来到 http://bbs.d-programming-language-china.org "),toUTF16z("error"),MB_ICONERROR | MB_HELP,0); return -2; } //显示和更新窗体 ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); int nRet; //防止GetMessage=-1时陷入死循环 while ( (nRet = GetMessage(&msg, cast(HWND)null, 0, 0)) != 0 ) { if (nRet == -1 ) { return -1; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } extern(Windows) LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CHAR: writefln("你按了键盘键 %d",wParam); break; case WM_LBUTTONDOWN: HDC hDc; hDc = GetDC(hwnd); wchar[] s = "你左键单击了"; //用TextOut函数,必须加上gid32.lib库编译 TextOut(hDc,50,50, s.ptr, s.length); ReleaseDC(hwnd,hDc); break; case WM_PAINT: HDC hDc; PAINTSTRUCT ps; hDc = BeginPaint(hwnd, &ps); wchar[] s = "你现在还好吗,D语言学得还好吧 http://www.d-programming-language-china.org"; TextOut(hDc,100,200, s.ptr, s.length); EndPaint(hwnd,&ps); break; case WM_CLOSE: if ( IDYES== MessageBoxEx(hwnd, toUTF16z("真的要退出吗?"), toUTF16z("请选择"), MB_YESNO,0) ) DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } 至此,我们完整建立了一个windows GUI应用程序。建议上面所有的代码都手动输入而不是copy paste,以加深理解。理解了程序流程,windows api参数的意义,api函数的参数也就记住了。( lastupdate:20070510 最新文章请访问http://www.d-programming-language-china.org )关于一大步成功社区:yidabu提倡在交流中学习,在分享中提高收集感兴趣的知识,写下心得,通过网络与别人一起分享理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台