查看: 655|回复: 0
打印 上一主题 下一主题

从零开始学VC系列教程 五.消息机制与自定义消息【恢复】

[复制链接]
跳转到指定楼层
沙发
发表于 2016-5-29 18:56:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
从零开始学VC系列教程 五.消息机制与自定义消息 

        题外话:我们一再重复的讲Windows的消息,因为这的确是一个关键,会用,并且用好是非常必要的。这一章我们主要介绍一下消息机制及消息传输,做几个小小的实验,让大家在概念上明白怎么用。

        本节内容:学会消息发送接收及自定义消息 

        学习目的: 撑握消息机制的概念,学会基于消息的操作. 



这一章我们要做的例子看起来很简单,只是实现的方式不一样。按[开始]时,在文本框内显示当前的鼠标坐标,按[结束]后,显示一个结束信息,按[关闭]则退出程序.



1.新建工程.首先建一个基于MFC对话框的工程,名字为Eg05。前面四章我们已经介绍过很多次建立工程的方法了,这里不再重复了,如果还有不明白的,可以看一下以前的记录,或者给我留言。

2.在对话框上放一个EditBox,改ID称为IDC_EDIT_SHOW然后再添加三个按钮,分别设置标题为[开始],[结束],[关闭]。设置ID分别为IDC_BTN_STAR,IDC_BTN_OVER,IDC_BTN_CLOSE。通过前面几章的学习,我们知道ID号大家可以自己随便设定,当然最好是接近控件的作用简称或接近控件名的简称。完成后如下图所示。

 

图片01 (原文件名:01.JPG) 

首先我们来为[关闭]按钮添加响应,双击[关闭]按钮,添加响应程序。只有一行,呵呵。完成后如下图所示:

 



图片02 (原文件名:02.JPG) 



void CEg05Dlg::OnBtnClose() 

{

        // TODO: Add your control notification handler code here

       &nbspostMessage(WM_CLOSE);

}

编译运行,按[关闭]就可以看到程序退出了,晕死,是不是太简单了?下面我们来说一下为什么是这样。

在这里,我们用PostMessage函数发送了一个名为WM_CLOSE的消息,这是一个系统消息,用于关闭程序。查一查MSDN,可以看到这个函数的一些说明。原文放在下面,方便没有下载MSDN的朋友看看.

CWnd:ostMessage

BOOL&nbspostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );

Return Value

Nonzero if the message is posted; otherwise 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.

Remarks

Places a message in the window’s message queue and then returns without waiting for the corresponding window to process the message. Messages in a message

从原型我们可以看到,其实这个函数还有两个参数,分别为wParam和lParam,分别默认为0了。这两个参数是什么意思呢? 

wParam和lParam 这两个是Win16系统遗留下来的产物,在Win16API中WndProc有两个参数: 

一个是WORD类型的16位整型变量;另一个是LONG类型的32位整型变量。因此根据匈牙利命名法,16位的变量就被命名为wParam, 32位的变量就被命名为lParam。 



到了Win32API中,原来的16位变量也被扩展为32位,因此此时wParam和lParam的大小完全相同。 

在Win32API的早期,为了保证和Win16API的代码可移植性MS定义了WPARAM和LPARAM两个宏。 

当时保留了w前缀的原因一方面是由于WPARAM宏也已W开头,还有也因为要提醒程序员注意到可移植性,当然到了现在Win16早已退出历史舞台,这个前缀也就约定俗成的沿用下来了。WPARAM 和 LPARAM 本质上没有什么区别:都是32位数, 但是区别也还是有的:除了上面说的关于16位的的历史问题外,MICROSOFT在使用时两种参数分别代表不同的含义和内容,WPARAM常常代表一些控件的ID或者高位低位组合起来分别表示鼠标的位置,如果消息的发送者需要将某种结构的指针或者是某种类型的句柄时,习惯上用LPARAM来传递,可以参考各种控件的通知消息。

在WM_CLSOE中,我们使用了不带参数的调用,其实这个消息本来也没有参数,所以后面的两个参数就默认了。

为了更好的说明这两个参数,下面我们来添加本例程的另一个功能,显示鼠标的坐标。其实显示坐标不用这么麻烦,在这里是为了更好的展示消息的参数。首先添加一个类变量,从类管理器中双击[CEg05Dlg]这个类,会打开这个类的头文件,在类定义中添加一个布尔变量,名称为m_bMouseMoveDisp完成后如下图所示

 

图片03 (原文件名:03.JPG) 

如上图,我们添加了两行代码。

public:

        BOOL m_bMouseMoveDisp;

然后要对这个变量初始化,双击类管理器中的[OnInitDialog]函数,可以打开对话框的初始化函数,我们在后面添加一行代码:m_bMouseMoveDisp=false; //这里就是变量初始化



图片04 (原文件名:04.JPG) 

下面要添加一个函数,选择[CEg05Dlg],点右键会弹出一个菜单,如图所示

 

图片05 (原文件名:05.JPG) 

选择[Add Virtual Function…]弹出一个对话框

 

图片06 (原文件名:06.JPG) 

然后在左边的列表中双击[PreTranslateMessage],可以看到这个函数添加到右边了,然后选择这个函数,双击或点最后一个按钮[编辑存在]就可以添加到类中了。完成后如图:

 

图片07 (原文件名:07.JPG) 

当然上面的图中,我们还添加了一些响应代码,

BOOL CEg05Dlg:reTranslateMessage(MSG* pMsg) 

{

        // TODO: Add your specialized code here and/or call the base class

        if(pMsg->message==WM_MOUSEMOVE && m_bMouseMoveDisp==TRUE)

        {

                CString a;

                a.Format("X坐标:%d,Y坐标:%d",pMsg->lParam&0x0000ffff,(pMsg->lParam&0xffff0000)>>16);

                SetDlgItemText(IDC_EDIT_SHOW,a);

        }

        return CDialog:reTranslateMessage(pMsg);

}

重载这个函数可以捕获本窗体的所有经过消息队列的消息,在win32程序中,关于消息有两种传递方式:

a.MFC消息,MFC会把所有的消息一条条放到一个AFX_MSGMAP_ENTRY结构中,形成一个数组,该数组存放了所有的消息和与它们相关的参数。也可以说是放到消息队列里去。

b. 采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息。

这两种方式中只有第一种(穿过消息队列的消息)才受PreTranslateMessage()影响,

第二种消息并不会理睬PreTranslateMessage()的存在。

通过函数参数可以看到,传进来的是一个消息结构,我们查MSDN可以得到消息结构的原型:

typedef struct tagMSG {     // msg  

    HWND   hwnd;      

    UINT   message;

    WPARAM wParam;

    LPARAM lParam;

    DWORD  time;

   &nbspOINT  pt;

} MSG;

很明显,可以从message得到消息名称,所以我们写成if(pMsg->message==WM_MOUSEMOVE && m_bMouseMoveDisp==TRUE)在这里,m_bMouseMoveDisp是前面一步我们定义的类变量。当我们移动鼠标时,系统会发出一个名为WM_ MOUSEMOVE的消息到所有窗口,这个消息会经过消息队列,所以可以在这里捕获到,这个消息带两个参数,分别为 

fwKeys = wParam;        // key flags 

xPos = LOWORD(lParam);  // horizontal position of cursor 

yPos = HIWORD(lParam);  // vertical position of cursor 

可以看到,lParam这个参数包括了光标所在处的坐标,而wParam则包含了鼠标键的事件,包括

MK_CONTROL        Set if the ctrl key is down.

MK_LBUTTON        Set if the left mouse button is down.

MK_MBUTTON        Set if the middle mouse button is down.

MK_RBUTTON        Set if the right mouse button is down.

MK_SHIFT        Set if the shift key is down.

所在我们用pMsg->lParam&0x0000ffff取得参数lParam的低字节16位为X坐标,用,pMsg->lParam&0xffff0000)>>16的高16位数据,并移至低位作为Y坐标,然后嘛,大家都看得懂了,显示在IDC_EDIT_SHOW中。当然,这里还有一个条件,就是m_bMouseMoveDisp要为TRUE,这个就是按钮的事了,我们双击[开始]按钮,添加一行代码。bMouseMoveDisp=TRUE;完成后编译运行,点[开始]就可以看到框内显示了鼠标在窗体中的坐标了。实时效果如下:

 

图片08 (原文件名:08.JPG) 

要关闭坐标显示就很简单了,把m_bMouseMoveDisp置为FALSE就行了,所以大家可以为[结束]按钮添加一行代码就实现了。这里我们并不这样做,为了演示我们来绕一个大弯解决这个问题。按下这个按钮要发出一个消息WM_DISMOUSEDISP。很明显,这个消息是不存在的,我们自定义一个。双击[CEg05Dialolg],在前面添加一个定义。完成后如下图所示:

 

图片09 (原文件名:09.JPG) 

WM_USER 是windows自定义消息.一般一股是+几后再用。

如:#define WM_SHIT WM_USER+1

也就是说WM_USER是个分水岭,一般系统消息到不了这个数.在这里,我们自定义了一个消息,名为WM_DISMOUSEDISP 

下面双击[结束]按钮,添加响应代码PostMessage(WM_DISMOUSEDISP);这里我们也不传参数了。从上面的分析大家可以看到,SendMessage也可以发送消息,但消息并不经过消息队列。这两个函数有点区别的。PostMessage只负责将消息放到消息队列中,不确定何时及是否处理 

SendMessage要等到受到消息处理的返回码(DWord类型)后才继续 

PostMessage执行后马上返回 

SendMessage必须等到消息被处理后才会返回。

也就是说,我们发送的WM_DISMOUSEDISP可以通过PreTranslateMessage捕获到了。下面怎么写呢?想必大家也就明白了。以下给出响应代码。

BOOL CEg05Dlg:reTranslateMessage(MSG* pMsg) 

{

        // TODO: Add your specialized code here and/or call the base class

        if(pMsg->message==WM_MOUSEMOVE && m_bMouseMoveDisp==TRUE)

        {

                CString a;

                a.Format("X坐标:%d,Y坐标:%d",pMsg->lParam&0x0000ffff,(pMsg->lParam&0xffff0000)>>16);

                SetDlgItemText(IDC_EDIT_SHOW,a);

        }



        if(pMsg->message==WM_DISMOUSEDISP && m_bMouseMoveDisp==TRUE)

        {

                m_bMouseMoveDisp=false;

                SetDlgItemText(IDC_EDIT_SHOW,"关闭鼠标坐标显示!");

        }



        return CDialog:reTranslateMessage(pMsg);

}

编译运行看看,是不是就可以了。

这一章先到这里了,在上一章中,我们提到过自定义消息及消息影射到函数,大家可以再回头看一下,加深理解。

下面是整个工程及教程的下载:

工程及教程下载ourdev_588704.rar(文件大小:2.30M) (原文件名:Eg5.rar) 

第一章[从零开始学VC系列教程 一.信息显示实验]地址:[url]http://ouravr.com/bbs/bbs_content.jsp?bbs_sn=1455135&bbs_page_no=1&search_mode=3&search_text=bqmcu&bbs_id=9999[/url]

第二章[从零开始学VC系列教程 二. 对话框及常用控件实验]地址:[url]http://ouravr.com/bbs/bbs_content.jsp?bbs_sn=1481238&bbs_page_no=1&search_mode=3&search_text=bqmcu&bbs_id=9999[/url]

第三章[从零开始学VC系列教程 三. 串口通信及自定义消息]地址:[url]http://ouravr.com/bbs/bbs_content.jsp?bbs_sn=1551606&bbs_page_no=1&search_mode=3&search_text=bqmcu&bbs_id=9999[/url]

第四章[从零开始学VC系列教程之 四.并口控制与类的使用]地址:[url]http://ouravr.com/bbs/bbs_content.jsp?bbs_sn=1803655&bbs_page_no=1&search_mode=3&search_text=bqmcu&bbs_id=9999[/url]

非常感谢阿莫提供交流空间!!







回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入因仑

本版积分规则

快速回复 返回顶部 返回列表