
VC++6.0消息含义.doc
18页消息的意思1. 窗口过程 每个窗口会有一个称为窗口过程的回调函数(WndProc),它带有四个参数,分别为:窗口句柄(Window Handle),消息 ID(Message ID),和两个消息参数(wParam, lParam), 当窗口收到消息时系统就会调用此窗口过程来处理消息所以叫回调函数) 2 消息类型 1) 系统定义消息(System-Defined Messages) 在 SDK 中事先定义好的消息,非用户定义的,其范围在[0x0000, 0x03ff]之间, 可以分为以下三类: 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 包含控件通知的内容, 可以任意扩展 2) 程序定义消息(Application-Defined Messages) 用户自定义的消息, 对于其范围有如下规定: WM_USER: 0x0400-0x7FFF (ex. WM_USER+10) WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4) RegisterWindowMessage: 0xC000-0xFFFF 3 消息队列(Message Queues) 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)的方式被处理一、队列消息(Queued Messages)和非队列消息(Non-Queued Messages) 1)队列消息(Queued Messages) 消息会先保存在消息队列中,消息循环会从此队列中取消息并分发到各窗口处理 如鼠标,键盘消息 2) 非队列消息(NonQueued Messages) 消息会绕过系统消息队列和线程消息队列直接发送到窗口过程被处理 如: WM_ACTIVATE(刺激,激活 ), WM_SETFOCUS(焦点、焦距), WM_SETCURSOR(游标、指针), WM_WINDOWPOSCHANGED(), 注意: postMessage 发送的消息是队列消息,它会把消息 Post 到消息队列中;SendMessage 发送的消息是非队列消息, 被直接送到窗口过程处理 二、5 PostMessage(PostThreadMessage), SendMessage PostMessage:把消息放到指定窗口所在的线程消息队列中后立即返回。
PostThreadMessage:把消息放到指定线程的消息队列中后立即返回 SendMessage:直接把消息送到窗口过程处理, 处理完了才返回 三、GetMessage, PeekMessage PeekMessage 会立即返回 可以保留消息 GetMessage 在有消息时返回 会删除消息四、TranslateMessage, TranslateAccelerator TranslateMessage: 把一个 virtual-key 消息转化成字符消息 (character message),并放到当前线程的消息队列中,消息循环下一次取出处理 TranslateAccelerator: 将快捷键对应到相应的菜单命令它会把WM_KEYDOWN 或 WM_SYSKEYDOWN 转化成快捷键表中相应的WM_COMMAND 或 WM_SYSCOMMAND 消息, 然后把转化后的 WM_COMMAND 或 WM_SYSCOMMAND 直接发送到窗口过程处理, 处理完后才会返回五、(消息死锁 ( Message Deadlocks) 假设有线程 A 和 B, 现在有以下下步骤 1) 线程 A SendMessage 给线程 B, A 等待消息程 B 中处理后返回 2) 线程 B 收到了线程 A 发来的消息,并进行处理, 在处理过程中,B 也向线程 A SendMessgae,然后等待从 A 返回。
因为此时, 线程 A 正等待从线程 B 返回, 无法处理 B 发来的消息, 从而导致了线程 A,B 相互等待, 形成死锁多个线程也可以形成环形死锁 可以使用 SendNotifyMessage()、SendMessageTimeout()或SendMessageCallback()来避免出现死锁 六、BroadcastSystemMessage 我们一般所接触到的消息都是发送给窗口的, 其实, 消息的接收者可以是多种多样的,它可以是应用程序(applications), 可安装驱动 (installable drivers), 网络设备(network drivers), 系统级设备驱动(system-level device drivers)等, BroadcastSystemMessage 这个 API 可以对以上系统组件发送消息对 Windows 操作系统下的消息运行机制做较为深入的剖析一、 Windows 事件驱动机制DOS 和 Windows 这两种操作系统的运行机制是截然不同的,DOS 下的任何程序都是使用顺序的、过程驱动的程序设计方法这种程序都有一个明显的开始、明显的过程以及一个明显的结束,因此通过程序就能直接控制程序事件或过程的全部顺序。
即使是在处理异常时,处理过程也仍然是顺序的、过程驱动的结构而 Windows 的驱动方式则是事件驱动的,即程序的流程不是由事件的顺序来控制,而是由事件的发生来控制,所有的事件是无序的,所为一个程序员,在编写程序时,并不知道用户会先按下哪个按纽,也就不知道程序先触发哪个消息因此我们的主要任务就是对正在开发的应用程序要发出的或要接收的消息进行排序和管理事件驱动程序设计是密切围绕消息的产生与处理而展开的,一条消息是关于发生的事件的消息二、 Windows 的消息循环Windows 操作系统为每一个正在运行的应用程序保持有一个消息队列当有事件发生后,Windows 并不是将这个激发事件直接送给应用程序,而是先将其翻译成一个 Windows 消息,然后再把这个消息加入到这个应用程序的消息队列中去应用程序需要通过消息循环来接收这些消息在 MFC中使用了对 WinAPI 进行了很好封装的类库,虽然可以为编程提供一个面向对象的界面,使 Windows 程序员能够以面象对象的方式进行编程,把那些进行 SDK 编程时最繁琐的部分提供给程序员,使之专注于功能的实现,但是由于引入了很好的封装特性,使我们不能直接操纵部分核心代码。
对于消息的循环和接收也只是通过类似于下面的消息映射予以很简单的表示: BEGIN_MESSAGE_MAP(CTEMMSView, CFormView) //{ { AFX_MSG_MAP(CTEMMSView) ON_WM_LBUTTONDOWN() ON_COMMAND(ID_OPENDATA, OnOpenData) ON_WM_TIMER() ON_WM_PAINT() //} } AFX_MSG_MAP END_MESSAGE_MAP() 虽然上述消息映射在编程过程中处理消息非常简练方便,但显然是难于理解消息是如何参与循环和分发的因此有必要通过 SDK(Software Developers Kit,软件开发工具箱)代码深入到被 MFC 封装的 Windows编程的核心中来研究其具体是如何工作的在 SDK 编程中,一般是在Windows 应用程序的入口点 WinMain 函数中添加处理消息循环的代码以检索 Windows 送来的消息,然后 WinMain 再把这些消息分配给相应的窗口函数并处理它们: …… MSG msg; //定义消息名 while (GetMessage (& msg, NULL, 0, 0)) { TranslateMessage (& msg) ; //翻译消息 DispatchMessage (& msg) ; //撤去消息 } return msg.wParam ; 上述几句虽然简单但却是所有 Windows 程序的关键代码,担负着获取、解释和分发消息的任务,下面就重点对其功能和作用进行分析: MSG 结构在头文件中定义如下: typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG; 其数据成员的具体意义如下: hwnd:消息将要发送到的那个窗口的句柄,用这个参数可以决定让哪个窗口接收消息。
message:消息号,它唯一标识了一种消息类型每种消息类型都在Windows 文件进行了预定义 wParam:一个 32 位的消息参数,这个值的确切意义取决于消息本身 lParam:同上 time:消息放入消息队列中的时间,在这个域中写入的并非当时日期,而是从 Windows 启动后所测量的时间值Windows 用 这个域来使用消息保持正确的顺序 pt:消息放入消息队列时的鼠标坐标消息循环以 GetMessage 调用开始,它从消息队列中取出一个消息该函数的四个参数可以有控制地获取消息,第一个参数指定要接收消息的 MSG 结构的地址,第二个参数表示窗口句柄,一般将其设置为空,表示要获取该应用程序创建的所有窗口的消息;第三、四参数用于指定消息范围后面三个参数被设置为默认值,用于接收发送到属于这个应用程序的任何一个窗口的所有消息在接收到除 WM_QUIT 之外的任何一个消息后,GetMessage()返回 TRUE;如果 GetMessage 收到一个 WM_QUIT 消息,则返回 FALSE 以退出消息循环,终止程序运行因此,在接收到 WM_QUIT 之前,带有GetMessage()的消息循环可以一直循环下去。
当除 WM_QUIT 的消息用GetMessage 读入后,首先要经过函数 TranslateMessage()对其进行解释,但对大多数消息来说并不起什么作用这里起关键作用的是DispatchMessage()函数,把由 GetMessage 获取的 Windows 消息传送给在 MSG 结构中为窗口所指定的窗口过程在消息处理函数处理完消息之后,代码又循环到开始去接收另。












