GetMessage的Hwnd参数不为NULL的问题

本文探讨了在VC++应用程序中使用GetMessage函数时,若其Hwnd参数不为NULL可能导致应用程序无法正常接收到WM_QUIT消息的问题。详细解析了GetMessage函数的行为,并解释了为何当参数设置为特定窗口句柄时,会导致程序无法正常退出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GetMessage的Hwnd参数不为NULL的问题
2009年06月13日 星期六 11:16

今天在学习VC++深入详解的过程中发现当GetMessage的Hwnd参数不为NULL的时候,会导致应用程序接收不到WM_QUIT消息,此时关闭窗口时,窗口可以正常析构但是应用程序不会正常退出,必须通过任务管理器结束。原因如下:

from:http://fyca.blog.163.com/blog/static/129633842006227134350/

HWND hWnd = CreateWindowEx(...);

MSG msg;

while( TRUE ) {
   if( GetMessage( &msg, NULL, 0, 0 ) == FALSE )
      break;
   TranslateMessage( &msg );
   DispatchMessage( &msg );
}

消息循环这样写当然没问题, 但为什么GetMessage的第二个参数不能是窗口的句柄hWnd呢, 毕竟当前程序只有一个窗口啊? 如果是hWnd, 程序运行时没有任何问题, 但是当你将窗口关闭后会发现程序并未真正退出, 而且此时cpu占用为100%, Why?

MSDN中解释如下:

当GetMessage第二个参数为NULL时:
GetMessage retrieves messages for any window that belongs to the calling thread and thread messages posted to the calling thread using the PostThreadMessage function.

中文翻译:GetMessage取得 那些属于调用线程的窗口的消息 和 通过PostThreadMessage函数投递(发送)到调用线程的线程消息.

这样问题就明白了, GetMessage需要检索到WM_QUIT返回一个FALSE结束消息循环, 而WM_QUIT是通过PostQuitMessage(0){IN WM_DESTROY OF WindowProc}发送, 属于线程消息, 而非普通的窗口消息. 如果在GetMessage中用hWnd而不是NULL, 虽然WM_QUIT 消息仍会出现在程序的消息队列中,但GetMessage却无法检索到, 而且此时窗口已经被销毁了, 如果还想取得hWnd的窗口消息, 只可能发生错误( cpu占用100%, 死循环? ).

也适用于PeekMessage

在MSDN中,推荐的代码写法是这样的,

Return Values

If the function retrieves a message other than WM_QUIT, the return value is nonzero.

If the function retrieves the WM_QUIT message, the return value is zero.

If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.

Warning   Because the return value can be nonzero, zero, or -1, avoid code like this:

while (GetMessage( lpMsg, hWnd, 0, 0)) ...

The possibility of a -1 return value means that such code can lead to fatal application errors. Instead, use code like this:

BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}

#include <windows.h> #include <commctrl.h> #define ID_USERNAME 1001 #define ID_PASSWORD 1002 #define ID_LOGIN_BTN 1003 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void CreateControls(HWND); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { const char CLASS_NAME[] = "LoginWindowClass"; WNDCLASS wc = {0}; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW); RegisterClass(&wc); HWND hwnd = CreateWindow( CLASS_NAME, "登录系统", WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, NULL, NULL, hInstance, NULL ); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } void CreateControls(HWND hwnd) { CreateWindow("STATIC", "用户名:", WS_CHILD | WS_VISIBLE, 20, 20, 80, 25, hwnd, NULL, NULL, NULL); CreateWindow("EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER, 100, 20, 160, 25, hwnd, (HMENU)ID_USERNAME, NULL, NULL); CreateWindow("STATIC", "密码:", WS_CHILD | WS_VISIBLE, 20, 60, 80, 25, hwnd, NULL, NULL, NULL); CreateWindow("EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_PASSWORD, 100, 60, 160, 25, hwnd, (HMENU)ID_PASSWORD, NULL, NULL); CreateWindow("BUTTON", "登录", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 100, 100, 100, 30, hwnd, (HMENU)ID_LOGIN_BTN, NULL, NULL); } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: CreateControls(hwnd); break; case WM_COMMAND: if (LOWORD(wParam) == ID_LOGIN_BTN) { char username[50], password[50]; GetDlgItemText(hwnd, ID_USERNAME, username, 50); GetDlgItemText(hwnd, ID_PASSWORD, password, 50); // 简单验证(实际应用需加密存储) if (strcmp(username, "admin") == 0 && strcmp(password, "123456") == 0) { // 启动计算器 ShellExecute(NULL, "open", "calc.exe", NULL, NULL, SW_SHOW); DestroyWindow(hwnd); // 关闭登录窗口 } else { MessageBox(hwnd, "用户名或密码错误", "登录失败", MB_ICONERROR); } } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; }
07-06
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值