使用消息和消息队列

摘要:
如果线程创建了一个或多个窗口则必须提供消息循环来接收线程消息队列中接收消息并分派到适当的窗口过程。一个典型的应用程序其WinMain函数中应包括为主窗口注册窗口类,创建和显示主窗口,然后开始其消息循环。TranslateMessage将虚拟键消息转换为字符消息。这是因为消息循环中的DispatchMessage函数在窗口函数完成消息处理之前不会返回。下面的示例演示如何使用PeekMessage函数在冗长的操作期间检查消息队列中的鼠标点击和键盘输入。

参考:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644928(v=vs.85).aspx

下面的代码示例演示如何执行与窗口消息和消息队列有关的任务:

一 创建消息循环

系统不会自动为每个线程创建消息队列,而是只在线程执行那些需要消息队列的操作时才会为其创建消息队列。如果线程创建了一个或多个窗口则必须提供消息循环来接收线程消息队列中接收消息并分派到适当的窗口过程。由于系统会直接送消息到个别窗口,所以线程在开始其消息循环之前至少要创建一个窗口,多数应用程序都包含一个创建窗口的简单线程。一个典型的应用程序其WinMain 函数中应包括为主窗口注册窗口类,创建和显示主窗口,然后开始其消息循环。

你可以通过GetMessageDispatchMessage函数创建(译:此处应该说“开始”更合适)消息循环,如果你的应用程序必须包含用户的字符输入,循环中要包括TranslateMessage函数。TranslateMessage 将虚拟键消息转换为字符消息。下面的示例展示位于简单窗口应用程序的WinMain函数中的消息循环:

HINSTANCE hinst; 
HWND hwndMain; 

intPASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpszCmdLine, intnCmdShow) 
{ 
    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)) 
            returnFALSE; 
    } 

    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) 
        returnFALSE; 

    //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. 

    returnmsg.wParam; 
}

以下示例展示一个使用快捷键和显示非模式对话框的线程的消息循环,当TranslateAcceleratorIsDialogMessage函数返回TRUE (表示消息已处理)时,则不调用TranslateMessageDispatchMessage,原因在于TranslateAcceleratorIsDialogMessage 执行了消息所有必要的转换和调度。

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函数检查消息队列,该函数与 GetMessage 函数类似:都检查消息队列中符合过滤器规则的消息然后复制到一个 MSG 结构,主要的区别是后者直到在消息队列中找到符合过滤器规则的消息才返回,而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) 
        { 
            caseWM_LBUTTONDOWN: 
            caseWM_RBUTTONDOWN: 
            caseWM_KEYDOWN: 
                // 
                //Perform any required cleanup. 
                // 
                fDone =TRUE; 
        } 
    } 
} 

其他函数,包括 GetQueueStatusGetInputState, 也可以检查线程消息队列的内容。 GetQueueStatus 返回一个标志位的数组以指示队列中消息的类型,这是检查队列是否包含任意类型消息最快的方法。GetInputState 在队列包含鼠标或键盘消息时返回 TRUE 。它们都可以用于检查队列是否需要处理的消息。

三 寄送消息

调用PostMessage函数可以寄送消息到(线程)消息队列, PostMessage 放置一个消息到线程消息队列的末尾并立即返回,不会等待线程处理该消息。函数的参数包括一个窗口句柄,一个消息标识和两个消息参数,系统考贝这些参数到一个MSG结构,设置结构的 timept 成员后放置到消息队列。系统根据随 PostMessage 一起传递的窗口句柄来判断哪个线程消息队列来接收消息,如果句柄为HWND_TOPMOST ,系统会寄送消息到所有顶级窗口的线程消息队列。

可以用 PostThreadMessage 函数寄送消息到指定的线程消息队列,PostThreadMessagePostMessage 类似, 只是第一个参数是线程标识而非窗口句柄,线程标识可以通过GetCurrentThreadId函数获得。

PostQuitMessage 函数可以结束一个消息循环,PostQuitMessage 寄送WM_QUIT 消息到当前正在执行的线程,消息的消息循环遇到WM_QUIT 消息后会结束并返回控制权给系统,通常用来响应 WM_DESTROY消息,像下面的示例:

caseWM_DESTROY: 
 
    //Perform cleanup tasks. 
PostQuitMessage(0); 
    break; 

四 发送消息

SendMessage 函数直接发送消息到窗口函数,SendMessage 调用一个窗口函数并等待该过程处理该消息并返回一个结果。消息可以发送到系统中的任意一个窗口,仅需要一个窗口句柄。系统使用该句柄来决定将消息发送到哪个窗口函数。处理一个可能发送自另一个线程的消息之前,窗口函数应先调用InSendMessage函数。如果函数返回True,窗口函数应在调用任何其他可能导致线程失去控制权的函数前先调用ReplyMessage,就像下面这样:

case WM_USER + 5: 
    if(InSendMessage()) 
        ReplyMessage(TRUE); 
 
    DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc); 
    break; 

许多消息可以发送到对话框中的控件,这些控件消息可以设置外观、行为和内容,或接收控件的信息。例如, CB_ADDSTRING 消息可以添加一个字符串到下拉列表框,BM_SETCHECK 消息可以设置复选框或单选框的选择状态。发消息到一个控件用SendDlgItemMessage函数,然后指定该控件的标识和包含该控件的对话框的句柄 。以下来自对话框函数的示例,从下拉列表框的编辑控件中复制一个字符串到其列表中,示例使用SendDlgItemMessage函数发送一个CB_ADDSTRING消息到下拉列表框。

HWND hwndCombo; 
intcTxtLen; 
PSTR pszMem; 
 
switch(uMsg) 
{ 
    caseWM_COMMAND: 
        switch(LOWORD(wParam)) 
        { 
            caseIDD_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); 
                returnTRUE; 
            // 
            //Process other dialog box commands. 
            // 
} 
    // 
    //Process other dialog box messages. 
    // 
}

免责声明:文章转载自《使用消息和消息队列》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇geoip+php演示样例:通过ip,获取国家名称和代码Linux环境下Gitblit服务搭建及秘钥配置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

06-常见的面试题目-线程

1、线程的基本概念 线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。 2、线程的基本状态及状态之间的关系(对否?) 状态:运行、阻塞、...

C#中回滚TransactionScope的使用方法和原理

TransactionScope只要一个操作失败,它会自动回滚,Complete表示事务完成  实事上,一个错误的理解就是Complete()方法是提交事务的,这是错误的,事实上,它的作用的表示本事务完成,它一般放在try{}的结尾处,不用判断前台操作是否成功,如果不成功,它会自己回滚。 在.net 1.1的时代,还没有TransactionScope类...

Windows 服务项目

定义     在2000/XP等基于NT 的操作系统中,有一个服务管理器,它管理的后台进程被称为 service。     服务是一种应用程序类型,它在后台运行,与 UNIX 后台应用程序类似。服务应用程序通常可以在本地和通过网络为用户提供一些功能,例如客户端/服务器应用程序、WEB 服务器、数据库服务器以及其他基于服务器的应用程序。     后台服务 程...

Spring Cloud Zuul性能调整

Spring Cloud 版本: Dalston.SR5 这两天通过JMeter测了一下Spring Cloud Zuul的性能,用的是两台虚机8核8G和4核8G,宿主机是10核逻辑20核,代理的服务简单的返回字符串hello,vm堆内存1G够用 先说一下测试情况,值得一提的是测试并不严谨,因为用的是虚机,并且虚机上还跑了一些其它的东西,所以不能作为最终指...

IOCP 浅析(java代码实现)

随着计算机技术,尤其是网络技术的飞速发展,如今的程序开发人员不仅仅局限于基于单机运行或单一线程的应用程序的开发。服务器端 / 客户端模式的最显著的特点是一个服务器端应用能同时为多个客户端提供服务。而传统的服务器端 / 客户端模式通常为每一个客户端创建一个独立的线程,这种实现方式在客户端数量不多的情况下问题不大,但对于类似于铁路网络订票这样的瞬间客户数量巨大...

android中Handle类的用法

android中Handle类的用法     当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无响应,如果时间过长,程序还会挂掉。Handler就是把这些功能放到一个单独的线程里执行,与Activity互不影响。     当用户点击一个按钮时如果...