MFC消息详解 消息传递

来源:互联网 发布:mac 翻墙 免费 2016 编辑:程序博客网 时间:2024/05/16 06:58

1. 怎样使用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类可调用PreTranslateMessage在发送消息给它之前进行消息翻译,例如,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( )可以在消息被反射之前插入该过程。

    ****************************************************************************************

       如何使用SendMessage自定义消息

SendMessage的基本结构如下:

SendMessage(

    HWND hWnd,  //消息传递的目标窗口或线程的句柄。

    UINT Msg, //消息类别(可以是一些系统消息,也可以是自己定义的消息)

    WPARAM wParam, //参数1 (WPARAM 其实是与UINT同类型) 

    LPARAM lParam); //参数2

其中一些参数的由来如下:

//typedef unsigned int UINT;

//typedef UINT WPARAM;

//typedef LONG LPARAM;

//typedef LONG LRESULT;

例如可以用以下语句:

::SendMessage(this->m_hWnd, WM_MY_DOSOME, (WPARAM) 0, (LPARAM) 0);

这里我发送的消息是本窗体接收的,所以句柄用:this->m_hWnd,

这里的消息类别WM_MY_DOSOME 是我自定义的。

在接收消息的窗体或线程所在的头文件里:

#define WM_MY_DOSOME WM_USER+1 // do something

表示要做一些事情。我们发了一个消息出去,那么接收方要能识别这个消息是干什么,就是通过消息类别来区分,并且开始去做这个消息对应要处理的事情。如下:

一:编写一个事情:

我们在接收窗体里定义一个这样的事情(过程),

afx_msg LRESULT DoSomeThing(WPARAM iParam1,LPARAM iParam2)

{

 MessageBox("收到消息了,我要开始做一些事情了。","收到",MB_OK);

 //在这里可以运用iParam1,iParam2 来做一些事情。

 return 0;

}

这个事情有3点大家要需要注意,非常重要:

 1:使用了afx_msg,并且要将afx_msg LRESULT DoSomeThing(WPARAM iParam1,LPARAM iParam2)

改写到头文件的 

//{{AFX_MSG 

    //。。。改写到这里,颜色会变成灰的。这一点非常重要。

//}}AFX_MSG

2:参数有2个,WPARAM iParam1,LPARAM iParam2,哪怕没有东西传进来也要写,不然会吃苦头的,vc里不会提醒你少写了一个,但一些莫名奇妙的事情会发生。

3:类型用 LRESULT,函数需要 return 0; 

二:让接收方知道什么时候做这个事情:

我们在

//{{AFX_MSG_MAP

   //。。。这里写上

   ON_MESSAGE(WM_MY_DOSOME,DoSomeThing)

//}}AFX_MSG_MAP

到这里,当你用SendMessage,发了一个WM_MY_DOSOME 类型的消息过来的时候,接收方就会去调用DoSomeThing(WPARAM iParam1,LPARAM iParam2)函数。

*************************************************************************************

如何使用PostMessage自定义消息

我们来看看这个消息的函数原型:

在CWnd::PostMessage中定义

BOOL PostMessage (

   UINT message,

   WPARAM wParam = 0,

   LPARAM lParam = 0

);

Parameters:

message

Specifies the message to be posted.

wParam

Specifies additional message information. The content of this parameter depends on the message being posted.

lParam

Specifies additional message information. The content of this parameter depends on the message being posted.

Return Value

Nonzero if the message is posted; otherwise 0. 

所以一般的当点击时,消息触发就会发送一个WM_MYMESSAGE消息,故而调用CownermessageDlg::OnMyMessage(WPARAM wParam, LPARAM lParam)这个函数。

 

如何在VC++中加入自定义消息,我的做法:这里我以一个对话框为例子,环境:VS 2008,新建一个工程,选择对话框:以编译器给出的对话框为蓝本,自己新建一个按钮如图:

把名字改成如图的,其他比如ID什么的都不改了,默认。

1 首先在ownermessageDlg.h中定义消息:例如

define WM_MYMESSAGE (WM_USER+100)

 

2 在.h文件中,加入消息响应函数,如:

afx_msg LRESULT OnMyMessage(WPARAM w,LPARAM l);

该函数有规定的格式:

afx_msg LRESULT Function_Name(WPARAM w,LPARAM l);

 

3 在ownermessageDlg.cpp中加入消息响应宏

在响应该消息的类中,在消息响应块

BEGIN_MESSAGE_MAP(CownermessageDlg, CDialog)

   ON_WM_SYSCOMMAND()

   ON_WM_PAINT()

   ON_WM_QUERYDRAGICON()

   //}}AFX_MSG_MAP

END_MESSAGE_MAP()

中加入以下语句:

ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)

成为如下样式:

BEGIN_MESSAGE_MAP(CownermessageDlg, CDialog)

   ON_WM_SYSCOMMAND()

   ON_WM_PAINT()

   ON_WM_QUERYDRAGICON()

   ON_MESSAGE(WM_MYMESSAGE,OnMyMessage)

   //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

4 在.CPP文件中,编辑消息响应函数,如下:

LRESULT CownermessageDlg::OnMyMessage(WPARAM wParam, LPARAM lParam)

{

   MessageBox(_T("My Message!"));

   return 1;

}

 

5.在对话框中为新增的按钮添加单击响应,则ownermessageDlg.cpp中的MESSAGE_MAP将变成:

BEGIN_MESSAGE_MAP(CownermessageDlg, CDialog)

   ON_WM_SYSCOMMAND()

   ON_WM_PAINT()

   ON_WM_QUERYDRAGICON()

   ON_MESSAGE(WM_MYMESSAGE,OnMyMessage)

   //}}AFX_MSG_MAP

   ON_BN_CLICKED(IDC_BUTTON1, &CownermessageDlg::OnBnClickedButton1)

END_MESSAGE_MAP()

 

void CownermessageDlg::OnBnClickedButton1()

{

   // TODO: Add your control notification handler code here

   //。。。在这里增加 PostMessage 函数投递消息

   PostMessage(WM_MYMESSAGE, IDC_BUTTON1);   

                       // PostMessage(……)就是响应WM_MYMESSAGE消息

                      // 其实这样写也是可以的:PostMessage(WM_MYMESSAGE);

}


原创粉丝点击