首页 > 应用开发 > IT综合 > 消息和消息队列(三)
下面的代码展示了如何执行下列与消息和消息队列相关的操作。
系统自动为每个线程创建一个消息队列。如果线程创建了一个或多个窗口,则必须要提供一个消息循环。消息循环从线程的消息队列中取得消息并分派给相应的窗口过程。
由于系统将消息引导给应用程序中的某个窗口,所以线程在启动消息循环之前必须创建至少一个窗口。多数应用程序包含创建窗口的单个线程。典型的应用程序在WinMain函数中为主窗口注册窗口类,创建并显示主窗口,然后启动消息循环。
你通过使用GetMessage和DispatchMessage函数创建消息循环。如果你的应用程序必须接收用户输入的字符,则需要在消息循环中包含TranslateMessage函数。TranslateMessage函数转换虚拟键消息为字符消息。下面的例子展示了一个简单的基于Windows的应用程序中的消息循环。
示例代码:
HINSTANCE hinst;
HWND hwndMain; int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; BOOL bRet; WNDCLASS wc; UNREFERENCED_PARAMETER(lpszCmdLine); // Register the window class for the main window. if (!hPrevInstance) { wc.style = 0; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION); wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MainMenu"; wc.lpszClassName = "MainWndClass"; if (!RegisterClass(&wc)) return FALSE; } hinst = hInstance; // save instance handle // Create the main window. hwndMain = CreateWindow("MainWndClass", "Sample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL, hinst, (LPVOID) NULL); // If the main window cannot be created, terminate // the application. if (!hwndMain) return FALSE; // Show the window and paint its contents. ShowWindow(hwndMain, nCmdShow); UpdateWindow(hwndMain); // Start the message loop. while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if (bRet == -1) { // handle the error and possibly exit } else { TranslateMessage(&msg); DispatchMessage(&msg); } } // Return the exit code to the system. return msg.wParam; } 下面的例子展示了可以处理用户快捷键和非模态窗口的消息循环的例子。当TranslateAccelerator或IsDialogMessage返回TRUE(表明消息已经被处理),TranslateMessage和DispatchMessage不会被调用。这是因为TranslateAccelerator或IsDialogMessage函数已经做了所有必须的消息转换和分派工作。
示例代码:
HWND hwndMain; HWND hwndDlgModeless = NULL; MSG msg;BOOL bRet; HACCEL haccel; //
// Perform initialization and create a main window.
//
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0){ if (bRet == -1) { // handle the error and possibly exit } else { if (hwndDlgModeless == (HWND) NULL || !IsDialogMessage(hwndDlgModeless, &msg) && !TranslateAccelerator(hwndMain, haccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } 有时候,应用程序需要在线程消息循环之外检测消息队列的内容。例如,如果应用程序的窗口过程执行了一个较长时间的绘图操作,你也许希望用户可以中断这个操作。除非你的应用程序周期性的检查键盘和鼠标消息,否则应用程序对于用户来说就是没有响应的,除非操作执行完成。这是因为DispatchMessage函数只在窗口过程处理完消息之后才会返回。
在执行一个费时操作时,你可以使用PeekMessage函数来检测消息队列。PeekMessage和GetMessage函数有些近似,它们都在消息队列中检查与过滤标准匹配的消息,并复制消息到一个MSG结构中。主要的差别是,GetMessage在取得一个消息之前不会返回,而PeekMessage函数立即返回,不管队列中是否有消息。
下面的代码演示了在一个费时操作的过程中,如何使用PeekMessage函数在消息队列中检测键盘和鼠标输入。
示例代码:
HWND hwnd;
BOOL fDone; MSG msg; // Begin the operation and continue until it is complete
// or until the user clicks the mouse or presses a key.
fDone = FALSE; while (!fDone) { fDone = DoLengthyOperation(); // application-defined function // Remove any messages that may be in the queue. If the // queue contains any mouse or keyboard // messages, end the operation. while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) { switch(msg.message) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN: // // Perform any required cleanup. // fDone = TRUE; } } } 其它函数,包括GetQueueStatus 和 GetInputState,也允许你检查消息队列的内容。GetQueneStatus返回一个标记数组显示队列中的消息类型,使用它最快的方式是检查队列中是否含有任何消息。如果队列中包含鼠标或键盘消息,GetInputState返回TRUE 。这些函数都可以用来检测队列中是否包含需要被处理的消息。
使用PostMessage函数post消息到消息队列。PostMessage将消息放置在队列的末尾并立即返回,它不会等到线程处理完消息。函数的参数包括窗口句柄、消息标识符、两个消息参数。系统复制这些参数到MSG结构中,并填充结构的time和pt成员,放置消息到消息队列中。
系统使用PostMessage函数中传递的窗口句柄来决定哪个线程消息队列应该接收这个消息,如果句柄是HWND_TOPMOST,系统post消息给所有顶级窗口的消息队列。
可以使用PostThreadMessage函数post消息给指定的线程消息队列。PostThreadMessage与PostMessage近似,除了第一个参数是线程标识符而不是窗口句柄之外。可以使用GetCurrentThreadId函数获得线程标识符。
使用PostQuitMessage函数来退出一个消息循环。PostQuitMessage函数post WM_QUIT消息给当前执行的线程。当遇到WM_QUIT消息的时候,线程的消息循环终止并将控制交回系统。应用程序通常调用PostQuitMessage来响应WM_DESTROY消息,如下面代码所示:
case WM_DESTROY: // Perform cleanup tasks. PostQuitMessage(0); break; SendMessage函数用来直接发送消息给窗口过程。SendMessage调用窗口过程并等待窗口过程返回消息的处理结果。
消息可以被send给系统中的任何窗口,这需要的是一个窗口句柄。系统使用窗口句柄来决定哪个窗口过程应该接收这个消息。
在处理send自其它线程的消息函数之前,窗口过程应该首先使用InSendMessage函数,如果函数返回TRUE,窗口过程应该在调用任何可能导致线程失去控制权的函数之前调用ReplyMessage函数,如下面代码所示:
case WM_USER + 5: if (InSendMessage()) ReplyMessage(TRUE); DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc); break; 许多消息可以被send给对话框中的控件。这些控件消息设置控件的外观,行为和内容,或者取得控件的信息。例如,CB_ADDSTRING消息可以向组合框中添加一个字符串,BM_SETCHECK消息可以设置检查框或radio按钮的check状态。
使用SendDlgItemMessage函数并指定包含控件的窗口和控件的标识符,可以send消息给控件。下面的代码,取自一个对话框的过程,从复制组合框控件中复制字符串到列表框控件。示例代码使用SendDlgItemMessage来send CB_ADDSTRING消息给组合框。
示例代码:
HWND hwndCombo; int cTxtLen; PSTR pszMem; switch (uMsg) { case WM_COMMAND: switch (LOWORD(wParam)) { case IDD_ADDCBITEM: // Get the handle of the combo box and the // length of the string in the edit control // of the combo box. hwndCombo = GetDlgItem(hwndDlg, IDD_COMBO); cTxtLen = GetWindowTextLength(hwndCombo); // Allocate memory for the string and copy // the string into the memory. pszMem = (PSTR) VirtualAlloc((LPVOID) NULL, (DWORD) (cTxtLen + 1), MEM_COMMIT, PAGE_READWRITE); GetWindowText(hwndCombo, pszMem, cTxtLen + 1); // Add the string to the list box of the // combo box and remove the string from the // edit control of the combo box. if (pszMem != NULL) { SendDlgItemMessage(hwndDlg, IDD_COMBO, CB_ADDSTRING, 0, (DWORD) ((LPSTR) pszMem)); SetWindowText(hwndCombo, (LPSTR) NULL); } // Free the memory and return. VirtualFree(pszMem, 0, MEM_RELEASE); return TRUE; // // Process other dialog box commands. // } // // Process other dialog box messages. // } 来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/777154/viewspace-927532/ ,如需转载,请注明出处,否则将追究法律责任。