金猴舞棒 / 待分类 / MFC 消息详解

分享

   

MFC 消息详解

2016-06-13  金猴舞棒


消息类别

  1. Windows 消息

    此类消息主要包括以前缀 WM_ 开头的消息,WM_COMMAND 除外。Windows 消息由窗口和视图处理。此类消息往往带有用于确定如何处理消息的参数。

  2. 控件通知

    此类消息包括从控件和其他子窗口发送到其父窗口的 WM_COMMAND 通知消息。例如,当用户在编辑控件 (Edit Control) 中执行可能更改文本的操作后,该编辑控件 (Edit Control) 将向其父级发送包含 EN_CHANGE 控件通知代码的 WM_COMMAND 消息。该消息的窗口处理程序以某种适当的方式响应此通知消息,例如在控件中检索该文本。

    框架像传送其他 WM_ 消息一样传送控件通知消息。但是有一个例外的情况,即当用户单击按钮时由按钮发送的 BN_CLICKED 控件通知消息。该消息被作为命令消息特别处理,并像其他命令一样传送。

  3. 命令消息

    此类消息包括用户界面对象(菜单、工具栏按钮和快捷键)发出的 WM_COMMAND 通知消息。框架处理命令的方式与处理其他消息不同,可以使用更多种类的对象处理命令,详见命令目标中的解释。

    Windows 消息和控件通知消息

    类别 1 和 2 中的消息(Windows 消息和控件通知)由窗口来处理(窗口是从 CWnd 类派生的类的对象)。包括CFrameWnd、CMDIFrameWnd、CMDIChildWnd、CView、CDialog 以及从这些基类派生的您自己的类。这些对象封装了 HWND(Windows 窗口的句柄)

    类别 3 中的消息(命令)可以由范围更广的对象(文档、文档模板以及应用程序对象本身)处理,而不仅仅由窗口和视图处理。当某一命令直接影响到某个特定对象时,应当让该对象处理此命令。例如,“文件”菜单中的“打开”命令在逻辑上与应用程序相关联:该应用程序接收到此命令时会打开指定的文档。因此“打开”命令的处理程序是应用程序类的成员函数。有关命令及其如何传送到对象的更多信息,请参见框架调用处理程序的方式

主要的 MFC 编程领域

MFC 无法为您做的工作

作为一种通用的编程框架,MFC 不能预见每位程序员的每一个需求。例如,MFC 使生成电子表格应用程序的界面变得容易,但您必须提供所有重要的显示和计算逻辑。

注意   MFC 不像 C 运行时库,它不是通用函数库。不能在另外的非 MFC 上下文中调用 MFC 类成员函数。从 MFC 内部仍然可以直接调用 Win32 API 函数,尤其是 MFC 未选择封装的那些函数。但大部分 MFC 函数都是类的成员,而且您必须具有该类的对象才能调用它的任何成员函数。有关相关信息,请参见 Win32 编程

MFC 被设计成一个用于生成 Windows 操作系统应用程序的类库。MFC 的目标和设计是针对日常使用的传统桌面生产应用程序的。由于使用 MFC 后生产效率非常高,因此尝试并将 MFC 用于不打算用 MFC 生成的应用程序类型是很诱人的。其中的一种应用程序类型就是 Windows 服务。尽管可以使用 MFC 的某些部分生成 Windows 服务,但对于使用哪些部分需要非常谨慎。Microsoft 知识库包括有关可能遇到的问题的一些信息;不过仍有许多其他问题没有记录在内。Microsoft 不支持使用 MFC 生成 Windows 服务。

MFC 可以为您做的工作

尽管 MFC 具有通用性,但它确实提供许多专业化支持:

支持请参见
OLE 可视化编辑OLE
自动化自动化
ActiveX 控件MFC ActiveX 控件
Internet 编程概述:Internet
Windows 公共控件控件
DAO 数据库编程数据访问对象 (DAO)
ODBC 数据库编程开放式数据库连接 (ODBC)
多线程编程多线程编程
网络编程Windows Sockets
可移植性移植程序

1. 怎样使用MFC发送一个消息用MFC发送一个消息的方法是,

    首先,应获取接收消息的CWnd类对象的指针;
    然后,调用CWnd的成员函数SendMessage( )。
        LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam);
        pWnd指针指向目标CWnd类对象。变量Msg是消息,wParam和lParam变量包含消息的参数,如鼠标单击哪里或选择了什么菜单项。目标窗口返回的消息结果放在变量Res中。
        发送消息到一个没有CWnd类对象的窗口,可以用下列目标窗口的句柄直接调用Windows API:
        LRESULT Res=::SendMessage(HWND hWnd, UINT Msg,  WPARAM wParam, LPARAM lParam);
        这里的hWnd是目标窗口的句柄。
2. 怎样用MFC寄送一个消息
    用MFC寄送一个消息与发送一个消息几乎相同,但寄送时用PostMessage( ) ,而不是用SendMessage( );返回值Res也不一样,Res不是一个由目标窗口返回的值,而是一个布尔值,用来表示消息是否成功地放到消息队列中。
3. 检索一个寄送消息
    正常情况下,一旦消息被寄送后,应用程序在后台发送它。但是在特殊情况下,需要你自己去删除一个消息,例如想在应用程序接收到某种消息之前停止应用程序。有两种方法可以从应用程序消息队列中删除一个消息,但这两种方法都没有涉及MFC。
■ 第一种方法:在不干扰任何事情之下窥视消息队列,看看一个消息是否在那里。
    BOOL res=::PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ) ;
■ 第二种方法:实际上是等待,一直等到一个新的消息到达队列为止,然后删除并返回该消息。
    BOOL res=::GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
    在这两种方法中,变量hWnd指定要截获消息的窗口,如果该变量设为NULL,所有窗口消息将被截获。wMsgFilterMin和wMsgFilterMax变量与SendMessage( )中的变量Msg相对应,指定查看消息的范围。如果用'0,0',则所有的消息都将被截获。如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELAST,则所有键盘或鼠标的消息将被截获。wRemoveMsg变量指定PeekMessage( )是否应该真正地从队列中删除该消息。(GetMessage( )总是删除消息)。该变量可以取两个值:
    ■ PM_REMOVE,PeekMessage( )将删除消息。
    ■ PM_NOREMOVE,PeekMessage( )将把消息留在队列里,并返回它的一个拷贝。
    当然,如果把消息留在消息队列中,然后再次调用PeekMessage( )查看相同类型的消息,则将返回完全相同的消息。
    lpMsg变量是一个指向MSG结构的指针,MSG包含检索到的消息。
    typedef struct tagMSG {
                        HWND hwnd; // window handle message is intended for
                        UINT message;
                        WPARAM wParam;
                        LPARAM lParam;
                        DWORD time; // the time the message was put in the queue
                        POINT pt; // the location of the mouse cursor when the
                                       // message was put in the queue
                        } MSG;
4. MFC怎样接收一个寄送的消息
    MFC处理一个寄送和发送消息的唯一明显不同是寄送的消息要在应用程序的消息队列中花费一些时间。在消息泵(message pump)弹出它之前,它要一直在队列中。
    消息泵
    MFC应用程序中的消息泵在CWinApp的成员函数Run()中。应用程序开始运行时,Run()就被调用,Run()把时间分割成两部分。一部分用来执行后台处理,如取消临时CWnd对象;另一部分用来检查消息队列。当一个新的消息进来时,Run()抽取它—即用GetMessage( )从队列中取出该消息,运行两个消息翻译函数,然后用DispatchMessage( )函数调用该消息预期的目标窗口进程。
    消息泵调用的两个翻译函数是PreTranslateMessage( )和::TranslateMessage( )。目标窗口的MFC类可调用reTranslateMessage在发送消息给它之前进行消息翻译,例如,CFrameWnd用PreTranslateMessage( )将加速键(如,Ctrl+S存储文件)转换为命令消息。翻译前的消息通常被处理掉,而翻译后的消息(如果有的话)将被重新寄送到队列里。::TranslateMessage是一个窗口函数,将原始键码转换为键字符。消息一旦被DispatchMessage()发送,MFC处理它就像处理SendMessage()发送的消息一样。
5. MFC怎样处理一个接收到的消息
    处理接收到的消息的目的非常简单:将消息指向一个函数,该函数通过消息中的消息标识符处理它。非MFC窗口用简单的case语句来实现该目标,每个case语句执行一些函数,或调用其他一些函数。
    MainWndProc(HWND hWnd, UINT message, W PARAM wParam,LPARAM lParam)
    {
        switch(message)
        {
        case WM_CREATE:
            : : :
        break;
        case WM_PAINT:
            : : :
        break;
        default:
        return(DefWindowProc(hWnd,message,wParam,lParam));
        }
        return(NULL);
    }
    任何遗漏的消息将被传输到一个默认的消息处理函数,但是,case语句不能很好地适应C++和封装技术。在C++环境中,要求消息被一个专门处理该类型消息的类的成员函数处理。因此,MFC不采用case语句,而采用更加复杂和回旋的方法。但它允许用私有类处理消息,而只需做下面三件事情:
    ■ 从将要接收消息的CWnd类对象派生类(对于命令消息是CCmdTarget)。
    ■ 在派生类中写一个处理消息的成员函数。
    ■ 在类中定义一个查找表(叫做消息映像),该表具有成员函数的条目和它要处理的消息的标识符。
    然后,MFC依次调用下面的函数,指引输入消息到处理函数。
    1) AfxWndProc( )接收消息,寻找消息所属的CWnd对象,然后调用AfxCallWndProc( )。
    2) AfxCallWndProc( )存储消息(消息标识符和参数)供未来参考,然后调用WindowProc( )。
    3) WindowProc( ) 发送消息给OnWndMsg( ) ,然后,如果消息未被处理,则发送给DefWindowproc( )。
    4) OnWndMsg( )要么为WM_COMMAND消息调用OnCommand( ),要么为WM_NOTIFY消息调用OnNotify( )。任何被遗漏的消息都将是一个窗口消息。OnWndMsg( )搜索类的消息映像,以找到一个能处理任何窗口消息的处理函数。如果OnWndMsg( )不能找到这样的处理函数,则把消息返回到WindowProc( ),由它将消息发送给DefWindowProc( )。
    5) OnCommand()查看这是不是一个控件通知(lParam不是NULL);如果它是,OnCommand( )就试图将消息映射到制造通知的控件;如果它不是一个控件通知,或者控件拒绝映射的消息,OnCommand( )就调用OnCmdMsg( )。
    6) OnNotify( )也试图将消息映射到制造通知的控件;如果映射不成功, OnNotify( )就调用相同的OnCmdMsg( )函数。
    7) 根据接收消息的类,OnCmdMsg( )将在一个称为命令传递(Command Routing)的过程中潜在地传递命令消息和控件通知。例如,如果拥有该窗口的类是一个框架类,则命令和通知消息也被传递到视图和文档类,并为该类寻找一个消息处理函数。
为什么要消息映像?
    这毕竟是C++语言;为什么OnWndMsg( )不为每个窗口消息调用一个预定义的虚拟函数?因为它太占CPU。若是那样,当扫描一个消息映像以加速该过程时,OnWndMsg( )可能会做出意想不到的事情,并陷入汇编器。注意通过重载WindowProc( )、OnWndMsg( )、OnCommand( )、OnNotify( ) 或OnCmdMsg( )可以修改这一过程。重载OnWndMsg( )可以在窗口消息被排序之前插入该过程。重载OnCommand( )或OnNotify( )可以在消息被反射之前插入该过程。

 

MFC 中资源标识符前缀ID号意义


IDB_BITMAP

IDC_CONTRAL/CURSOR

IDD_DIALOG

IDI_ICON

IDM_MENU

IDR_

IDR_MAINERAME

IDS_STRING

IDP_

ID_



afx_msg

Defining a Message Map

The DECLARE_MESSAGE_MAP macro declares three members for a class.

  • A private array of AFX_MSGMAP_ENTRY entries called _messageEntries.
  • A protected AFX_MSGMAP structure called messageMap that points to the_messageEntries array.
  • A protected virtual function called GetMessageMap that returns the address ofmessageMap.

This macro should be placed in the declaration of any class using message maps. By convention, it is at the end of the class declaration. For example:

class CMyWnd : public CMyParentWndClass{ // my stuff...protected: //{{AFX_MSG(CMyWnd) afx_msg void OnPaint(); //}}AFX_MSG DECLARE_MESSAGE_MAP()};

This is the format generated by AppWizard and ClassWizard when they create new classes. The //{{ and //}} brackets are needed for ClassWizard.


应用程序框架产生的消息映射函数例如:afx_msg void OnBnClickedButton1(); 其中 afx_msg为消息标志,它向系统声明:有消息映射到函数实现体;而在map宏定义中,就有具体消息和此函数的映射定义(可以是自定义,也可以是系统自动完成的)afx是 application framework如定义一个Edit的Change处理函数:一、在类的头文件(*.h)中增加 : //{{AFX_MSG(CDialogDemo) afx_msg void OnChangeEdit1(); //{{AFX_MSG 二、在类的实现文件(*.cpp)中增加: 1.消息定义( ON_EN_CHANGE):BEGIN_MESSAGE_MAP(CDialogDemo, CDialog)//{{AFX_MSG_MAP() ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1) //}}AFX_MSG_MAP END_MESSAGE_MAP() 2.执行函数:void CDialogDemo::OnChangeEdit1() { // TODO: Add your control notification handler code here……} 在afxwin.h中afx_msg的解释: #ifndef afx_msg #define afx_msg // intentional placeholder #endif 没什么意思.只是定义了这个符号而已. 这个对编译器来说,相当于什么都没有,对于人来说,我们可以看到这样的符号. 对于类向导来说.这个符号才是有意义的.它是一个消息处理函数的前缀. 类向导生成的消息函数,分发函数,事件响应函数都以这个为前缀. 如果去掉了,向导将不能识别

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>