`

MFC全接触(二)

阅读更多
        上一次和MFC邂逅,我和她谈得很投机。[1]分别的时候,她还依依不舍地对我说:“别忘了给我消息哦。”忙了一个月了,直到这几天,我才想起她告诉我的那句话。今天终于忍不住要给她发条消息了,但是我该怎么发呢?完了,她都没有告诉我怎么发呢?看来又得自己努力了。
        我们都知道Windows应用程序是消息驱动(Message-Driven)的。而我们在编写应用程序的时候也是建立起消息映射表,并通过实现消息响应函数去处理用户操作产生的事件消息。关于消息驱动,有一个基本的概念要明确的,就是应用程序并不能直接得到来自用户操作的信息,而这些信息则是被操作系统拦截并封装成MSG类,然后再将信息发往相应的应用程序。        
        既然要发消息,那么就得先了解一下MSG的结构了。在WINUSER.H中,我们找到了MSG的定义:
        
typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
#ifdef _MAC
    DWORD       lPrivate;
#endif
}
 MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;


        在结构体tagMSG当中,hwnd是消息要发送到的窗口句柄(window handle);message是消息的标识符(message identifier),它是一个16位的无符号整数,用以表示消息的类型;而wParam和lParam则是消息本身的参数。
        有了对消息的初步了解后,接下去需要了解的是消息的发送或者称为消息路由(Message Routing)。操作系统会有两种方式发送消息,一种是将消息放入消息队列中,另外一种就是直接将消息发往相应的应用程序。
        首先分析第一种发送方式:操作系统维护着两种不同的消息队列:一个是系统消息队列,一个则是线程特定(thread-specific)消息队列(简称线程消息队列)。系统信息队列是各个GUI线程共享的,而线程特定信息队列则是每个GUI线程拥有一个。消息就由输入设备产生->系统消息队列->线程消息队列->相应的消息处理程序。而第二种方式则是越过了中间两个队列,直接传到了相应的消息处理程序。像窗口获得焦点(WM_SETFOCUS)这一类的信息都是以第二种方式发送的。
        当消息发送至线程消息队列以后,任务的重点就转到消息处理程序上了。消息的处理通常都是通过消息循环来进行的。以下是很简单的消息循环的例子,源自MSDN:

MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 00 )) != 0)

    
if (bRet == -1)
    
{
        
// handle the error and possibly exit
    }

    
else
    
{
        TranslateMessage(
&msg); 
        DispatchMessage(
&msg); 
    }

}

        其中GetMessage方法的作用是从消息队列取得消息,并将消息复制到变量msg中,最后会将消息从队列中移走。GetMessage方法会有返回值,如果返回值为0(当取得的消息为WM_QUIT的时候),退出循环;如果返回值为-1,则说明取消息这一动作发生了异常,然后进行相应的处理;如果返回值为其他数值,则执行TranslateMessage和DispatchMessage方法。
        而在MFC当中,我们可以从CWinThread的Run方法中找到MFC使用消息循环处理消息的过程:

for (;;)
{
    
// phase1: check to see if we can do idle work
    while (bIdle &&!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
    
{
        
// call OnIdle while in bIdle state
    if (!OnIdle(lIdleCount++))
        bIdle 
= FALSE; // assume "no idle" state
    }


    
// phase2: pump messages while available
    do
    
{
        
// pump message, but quit on WM_QUIT
    if (!PumpMessage())
        
return ExitInstance();

    
// reset "no idle" state after pumping "normal" message
    if (IsIdleMessage(&m_msgCur))
    
{
        bIdle 
= TRUE;
        lIdleCount 
= 0;
    }


    }
 while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
    

        从代码中,我们可以看到跟前面简单例子有两个不同的地方:第一是MFC使用了PeekMessage方法从消息队列中获得消息的方法,而不是直接使用GetMessage方法;第二就是使用了PumpMessage方法进行消息的处理。PeekMessage方法与GetMessage不同的地方就是:它从消息队列中得到消息,却不会将消息从队列中移走。使用PeekMessage方法也是因为它是一个具有异步行为的方法,如果消息队列中没有消息,它会立即返回,而GetMessage则不行,因为它的任务除了获得消息之外,还要从消息队列中将获得的消息移走,因此,一旦消息队列中没有消息,GetMessage方法就会被阻塞了,使得线程处于睡眠状态,这样就不会出现所谓的Idle状态,也就不会去执行OnIdle方法了。接下来,我们将注意力转到PumpMessage方法中:

BOOL CWinThread::PumpMessage()
{
    ASSERT_VALID(
this);
    
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
    
{
        #ifdef _DEBUG
        
if (afxTraceFlags & traceAppMsg)
        TRACE0(
"CWinThread::PumpMessage - Received WM_QUIT. ");
        m_nDisablePumpCount
++// application must die
        
// Note: prevents calling message loop things in
        
//       'ExitInstance'will never be decremented
        #endif
    
return FALSE;
    }


    #ifdef _DEBUG
    
if (m_nDisablePumpCount != 0)
    
{
        TRACE0(
"Error: CWinThread::PumpMessage called when not permitted. ");
    ASSERT(FALSE);
    }

    
#endif

    #ifdef _DEBUG
    
if (afxTraceFlags & traceAppMsg)
        _AfxTraceMsg(_T(
"PumpMessage"), &m_msgCur);
    
#endif

    
// process this message

    
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
    
{
    ::TranslateMessage(
&m_msgCur);
    ::DispatchMessage(
&m_msgCur);
    }

    
return TRUE;
}

         从代码中,我们可以知道,在PumpMessage方法中,通过调用GetMessage方法从消息队列中取走消息,然后根据其返回值来决定是否返回,这跟前面的简单例子是很类似的。如果GetMessage方法返回的是0或者-1都会返回FALSE,方法结束;如果返回的是其他正整数,那么将会执行最后的一个条件语句,在那里我们又见到了熟悉的TranslateMessage和DispatchMessage方法。通过这样一层一层的阅读源代码,我们可以发现MFC消息处理流程的主体与前面的简单例子是很类似的,只是增加了一些相应的扩展,使得消息的处理更灵活。
        折腾了半天,终于对消息有了初步的了解。虽然MFC消息处理的流程并没有很特别的地方,但是其OnIdle方法以及PreTranslateMessage方法都是需要仔细研究的,让我们下次再续吧。
   
        [1] MFC全接触(一)

分享到:
评论

相关推荐

    MFC读取二进制文件并保存为文本文件

    1:本程序读取二进制文件,并把读到的二进制文件保存为文本数据 2:二进制文件内容一系列的三维点云数据,由扫描仪器扫描获得 3:本程序采用了多线程技术,读取二进制文件时,界面不会卡顿 4:实例二进制文件为data....

    一元二次方程求解MFC实现

    在MFC中实现一元二次方程的求解,我们可以利用C++的编程技术和MFC的框架来创建用户界面,并处理用户的输入以计算方程的解。 一元二次方程的解可以通过韦达定理或者公式法获得,公式法也称为求根公式,具体如下: x...

    深入浅出MFC第二版高清晰书签

    深入浅出MFC第二版高清晰书签深入浅出MFC第二版高清晰书签深入浅出MFC第二版高清晰书签深入浅出MFC第二版高清晰书签深入浅出MFC第二版高清晰书签深入浅出MFC第二版高清晰书签深入浅出MFC第二版高清晰书签深入浅出MFC...

    《深入浅出MFC》高清第二版(书签)_深入浅出MFC_c++mfc_

    在压缩包内,除了主教材《深入浅出MFC》高清第二版(书签).pdf外,还有两个辅助文件:VC驿站-C C++ VC 编程 教程 培训 论坛.lnk可能是一个链接,提供了更多C++和MFC相关的学习资源和讨论平台;VC 驿站.txt可能包含...

    深入浅出.MFC 第二版配套光盘

    深入浅出.MFC 第二版配套光盘, 买书之前,本人也很需要

    MFC画二维动态图表

    在本文中,我们将深入探讨如何使用Microsoft Foundation Class (MFC) 库来创建二维动态图表。MFC 是一个C++库,它为Windows应用程序开发提供了一种结构化的框架,简化了Windows API的使用。在MFC中,我们通常利用GDI...

    深入浅出MFC第二版光盘

    《深入浅出MFC第二版》是一本专为Windows应用程序开发设计的图书,主要围绕Microsoft Foundation Classes (MFC) 库展开,该库是微软提供的一套C++类库,用于简化Windows API的使用。MFC是基于面向对象编程的概念,它...

    MFC实现二维柱状图、散点图、饼图等

    本篇文章将详细讲解如何利用MFC实现二维柱状图、散点图和饼图,以及如何结合实例创建数据的实时点云图。 首先,我们从柱状图开始。柱状图是一种常用的数据可视化方式,它通过垂直或水平的柱子长度来表示数值大小。...

    深入浅出MFC第二版(简体中文)

    **深入浅出MFC第二版(简体中文)——MFC技术详解** MFC,全称为Microsoft Foundation Classes,是微软公司开发的一种C++类库,它为Windows应用程序开发提供了丰富的接口,极大地简化了Windows API的使用。MFC是...

    深入浅出MFC第二版 PDF

    6. **文件I/O**:MFC提供了对文件操作的支持,包括读写文本文件、二进制文件以及利用流对象进行高级I/O操作。 7. **数据库编程**:MFC集成了ODBC(Open Database Connectivity),使得与各种数据库的连接变得简单。...

    深入浅出MFC第二版随书源码

    《深入浅出MFC第二版》是一本专为Windows应用程序开发设计的图书,重点讲解了Microsoft Foundation Classes(MFC)库的使用。MFC是微软提供的一个C++类库,它封装了Windows API,使得开发者可以更加高效地构建基于...

    深入浅出MFC第二版例题集

    《深入浅出MFC第二版例题集》是侯俊杰教授编著的一本专为MFC学习者设计的实践教程。MFC,全称Microsoft Foundation Classes,是微软提供的一套面向对象的类库,用于简化Windows应用程序的开发。这本书通过丰富的例题...

    深入浅出mfc(第二版)光盘镜像

    通过《深入浅出MFC》第二版,读者不仅可以学习到MFC的基本用法,还能了解到许多实用技巧和最佳实践,从而提升Windows应用程序的开发能力。该书的光盘镜像文件包含书中所有实例代码,供读者实际操作和学习。

    MFC灰度化以及二值化

    可以读取打开一般格式的图像,显示,并进行最基础的灰度化算法以及二值化算法。聚类还有待添加,。

    mfc绘制二维坐标系和波形图

    在MFC(Microsoft Foundation Classes)框架下开发图形用户界面时,绘制二维坐标系和波形图是一项常见的任务,尤其在数据分析、信号处理等应用中。VS2008是常用的开发环境,它提供了丰富的库支持来实现这类功能。...

    深入浅出MFC第二版+源码

    深入浅出MFC第二版+源码

    深入浅出MFC第二版

    《深入浅出MFC第二版》是著名计算机技术专家侯俊杰先生撰写的一部关于Microsoft Foundation Classes (MFC) 的经典著作。MFC是微软公司为Windows应用程序开发提供的一种类库,它基于C++,封装了Windows API,使得...

    (C++经典)深入浅出MFC第二版随书源码

    《深入浅出MFC第二版》是一本深受C++开发者喜爱的经典著作,专注于Microsoft Foundation Classes (MFC) 库的讲解。MFC是微软提供的一个C++类库,它为Windows应用程序开发提供了一种结构化的框架,使得开发者可以更...

    MFC读取资源中的文本及二进制内容 文件打包进exe文件

    把文件资源打包进MFC的exe文件中,运行时进行加载,然后读取文件中的文本内容或者二进制内容 内含应用例程:1.把一个对象存为二进制文件,加载文件后恢复原对象 2.生成文本,加载资源中的文本文件,获取文本中的字符...

    深入浅出MFC 第二版 源码

    《深入浅出MFC 第二版 源码》是一个针对Microsoft Foundation Classes (MFC) 框架的深度学习资源。MFC是微软提供的一种C++类库,用于简化Windows应用程序开发,它将Windows API封装成易于使用的C++类。这本书的第二...

Global site tag (gtag.js) - Google Analytics