消息(Message)就是用于描述某个事件所发生的信息,而事件(Event)则是用户操作应用程序产生的动作或Windows系统自身所产生的动作。事件和消息两者密切相关,事件是原因,消息是结果,事件产生消息,消息对应事件。
首先要明确VC++中使用消息映射技术处理“消息”和“事件” ,通过DECLARE_MESSAGE_MAP()等宏通杀
事件就是“当....的时候”,事件总是和一个ID有关
消息就是“嗨!你该干....了” ,VS2005以上的“消息”标签中就是VC6 Windows Messages Handler中的内容
我们一般都是在 "当...的时候" 干 XXX0 或者 XXX1 XXX2 .... 也可以 “这件事情我不想理会”(让Windows去干)。 我们也可能在正干XXXn中突然想起: “嗨!我该晚餐了!” 再附上不经典的描述:事件是一个动作——用户触发的动作。 消息是一个信息——传递给系统的信息。 事件与消息的概念在计算机中较易混淆,但本质不同: 事件由用户(操作电脑的人)触发且只能由用户触发,操作系统能够感觉到由用户触发的事件,并将此事件转换为一个(特定的)消息发送到程序的消息队列中。 这里强调的是: 可以说“用户触发了一个事件”,而不能说“用户触发了一个消息”。 用户只能触发事件,而事件只能由用户触发。 一个事件产生后,将被操作系统转换为一个消息,所以一个消息可能是由一个事件转换而来(或者由操作系统产生)。 一个消息可能会产生另一个消息,但一个消息决不能产生一个事件——事件只能由用户触发。
总结(事件,消息的来源):
事件:只能由用户通过外设的输入产生。
消息:(产生消息的来源有三个) (1) 由操作系统产生。 (2) 由用户触发的事件转换而来。 (3) 由另一个消息产生。
(一) 利用用户定义的消息通信
在Windows程序设计中,应用程序的每一个线程都拥有自己的消息队列,甚至工作线程也不例外,这样一来,就使得线程之间利用消息来传递信息就变的非常简单。首先用户要定义一个用户消息,如下所示:
#define WM_USERMSG WMUSER+100;在需要的时候,在一个线程中调用::PostMessage((HWND)param,WM_USERMSG,0,0)或CwinThread::PostThradMessage()来向另外一个线程发送这个消息,上述函数的四个参数分别是消息将要发送到的目的窗口的句柄、要发送的消息标志符、消息的参数WPARAM和LPARAM。
下面的代码的结果是在线程结束时显示一个对话框,提示线程结束:
UINT ThreadFunction(LPVOID pParam)
{
while(!bend)
{
Beep(100,100);
Sleep(1000);
}
::PostMessage(hWnd,WM_USERMSG,0,0);
return 0;
}
WM_USERMSG消息的响应函数为OnThreadended(WPARAM wParam,LPARAM lParam)
LONG CTestView::OnThreadended(WPARAM wParam,LPARAM lParam)
{AfxMessageBox("Thread ended.");Retrun 0;}
上面的例子是工作者线程向用户界面线程发送消息,对于工作者线程,如果它的设计模式也是消息驱动的,那么调用者可以向它发送初始化、退出、执行某种特定的处理等消息,让它在后台完成。在控制函数中可以直接使用::GetMessage()这个SDK函数进行消息分检和处理,自己实现一个消息循环。GetMessage()函数在判断该线程的消息队列为空时,线程将系统分配给它的时间片让给其它线程,不无效的占用CPU的时间,如果消息队列不为空,就获取这个消息,判断这个消息的内容并进行相应的处理。
(二)用事件对象实现通信
在线程之间传递信号进行通信比较复杂的方法是使用事件对象,用MFC的Cevent类的对象来表示。事件对象处于两种状态之一:有信号和无信号,线程可以监视处于有信号状态的事件,以便在适当的时候执行对事件的操作。上述例子代码修改如下:
Cevent threadStart ,threadEnd;
UINT ThreadFunction(LPVOID pParam)
{
::WaitForSingleObject(threadStart.m_hObject,INFINITE);
AfxMessageBox("Thread start.");
while(!bend)
{
Beep(100,100);
Sleep(1000);
Int result=::WaitforSingleObject(threadEnd.m_hObject,0);//等待threadEnd事件有信号,无信号时线程在这里悬停
If(result==Wait_OBJECT_0)
Bend=TRUE;
}
::PostMessage(hWnd,WM_USERMSG,0,0);
return 0;
}
///
/Void CtestView::OninitialUpdate()
{ hWnd=GetSafeHwnd();
threadStart.SetEvent();//threadStart事件有信号
pThread=AfxBeginThread(ThreadFunction,hWnd);//启动线程
pThread->m_bAutoDelete=FALSE;
Cview::OnInitialUpdate);
}
Void CtestView::OnDestroy()
{threadEnd.SetEvent();
WaitForSingleObject(pThread->m_hThread,INFINITE);
d elete pThread;
Cview::OnDestroy();
}
(二)
消息(Message)就是用于描述某个事件所发生的信息,而事件(Event)则是用户操作应用程序产生的动作或Windows系统自身所产生的动作。事件和消息两者密切相关,事件是原因,消息是结果,事件产生消息,消息对应事件。
Windows系统中的消息可以分为两大类:系统定义的消息和应用程序自定义的消息。
1.系统定义的消息
操作系统可以向应用程序发送或投递系统定义的消息控制应用程序的行为并提供输入。例如,对每一个输入事件,用户按键、移动鼠标、单击都会产生一个消息。应用程序通过相应这些消息处理用户的动作,实现和用户的交互。应用程序也可以向系统发送或投递系统定义的消息以控制预先注册的类型的空间窗口的行为。
每一个系统定义的消息都有唯一的标识符常量。在SDK(Soft Development Kit,软件开发包)头文件中这些常量被定义为32为整数,标识符常量的字符分为两部分,前缀和后缀。前缀表示处理该消息的窗口的类别,后缀描述了该消息的目的。
表2-2 消息的全部前缀和说明
BM 按钮(Button Control)
CB 组合框(Combo Box Control)
CDM 通用对话框(Common Dialog Box)
DBT 设备消息(Device Control)
DL 下拉列表(Drag List Box)
EM 编辑框(Edit Control)
HKM 热键(Hot Key Control)
IPM IP控件(IP Address Control)
LB 列表框(List Box Control)
LVM 列表视图(List View Control)
MCM 日历控件(Month Calendar Control)
PBM 进度条(Progess Bar)
PSM 属性(Property Bar)
RB 伸缩条 (Rebar Control)
SB 状态栏(Status Bar Control)
STM 静态条(Static Control)
TB 工具条(Toolbar)
TBM 跟踪条(Tackbar)
TCM 标签控件(Tab Control)
TVM 树视图(Tree-view Control)
UDM 微调按钮控件(Up-down Control)
WM 普通窗口(General Window)
Windows中大概有400多种消息,这些消息可以进一步细分为3类:
1> 窗口消息(Windows Message)
与窗口的内部运作有关,如创建窗口,绘制窗口,销毁窗口等。可以是一般的窗口,也可以是Dialog,控件等。
如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL...
2> 命令消息(Command Message)
与处理用户请求有关, 如单击菜单项或工具栏或控件时, 就会产生命令消息。
WM_COMMAND, LOWORD(wParam)表示菜单项,工具栏按钮或控件的ID。如果是控件, HIWORD(wParam)表示控件消息类型
3> 控件通知(Notify Message)
控件通知消息, 这是最灵活的消息格式, 其Message, wParam, lParam分别为:WM_NOTIFY, 控件ID,指向NMHDR的指针。NMHDR包含控件通知的内容, 可以任意扩展。
控件通知消息同窗口消息一样,可以由从CWnd类派生的任意的MFC类进行处理。但是控件通知消息同窗口消息有一个重要的区别:控件通知消息不会出现在消息映射中,而且用户不需要使用向导将他们加入到应用程序中去。因此向导对话框中,也不会发现任何控件通知消息。
消息队列
Windows中有两种类型的消息队列
1) 系统消息队列(System Message Queue)
这是一个系统唯一的Queue,设备驱动(mouse, keyboard)会把操作输入转化成消息存在系统队列中,然后系统会把此消息放到目标窗口所在的线程的消息队列(thread-specific message queue)中等待处理
2) 线程消息队列(Thread-specific Message Queue)
每一个GUI线程都会维护这样一个线程消息队列。(这个队列只有在线程调用GDI函数时才会创建,默认不创建)。然后线程消息队列中的消息会被送到相应的窗口过程(WndProc)处理.
注意: 线程消息队列中WM_PAINT,WM_TIMER只有在Queue中没有其他消息的时候才会被处理,WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理