浅谈windows消息机制
首先来了解几个基本概念:
消息:在了解什么是消息先来了解什么是事件。事件可分为几种,由输入设备触发的,比如鼠标键盘等等。由窗体控件触发的,比如button控件,file菜单等。还有就是来自Windows内部的事件。这三种称为事件。而消息,是由事件翻译而来的。事件产生消息。
从数据结构角度来讲,消息是一种结构体。结构如下:
1 typedef struct tagMSG 2 3 { 4 5 HWND hwnd; //窗口句柄。 6 7 UINT message;//消息类型。 8 9 WPARAM wParam;//32位附加信息。 10 11 LPARAM lParam;//32位附加信息。 12 13 DWORD time;//消息发送时间。 14 15 POINT pt;//消息发送时,鼠标所在位置。 16 17 }MSG;
消息队列:消息队列有两种,分为系统消息队列和应用程序消息队列。产生的消息首先由Windows系统捕获,放在系统消息队列,再拷贝到对应的应用程序消息队列。32/64位系统为每一个应用程序维护一个消息队列。
消息循环:系统为每个应用程序维护一个消息循环,消息循环会不断检索自身的消息队列。每有一个消息,就用GetMessage()取出消息。
1 while(GetMessage (&msg, NULL, 0, 0))//Windows消息循环。 2 3 { 4 TranslateMessage (&msg) ;//翻译消息,如按键消息,翻译为WM_CHAR 5 6 DispatchMessage (&msg) ;//分发消息到对应窗口 7 8 }
GetMessage具有阻塞机制。当消息队列中没有消息时,程序非忙等,而是让权等待。当收到WM_QUIT时,GetMessage返回false,循环停止,同时应用程序终止。
消息处理:DispatchMessage()把取出来的消息分配给相应的窗口或线程,由窗口过程处理函数DefWindowProc()处理。
==============================================================================================================================================================================割割割割割
以上简要地介绍了消息队列,消息循环与消息处理的概念。
Windows的应用程序靠消息驱动来实现功能。而消息驱动靠消息机制来处理。消息机制就是由消息队列,消息循环,消息处理构成的。
那么,消息机制是如何运作的呢?
当用户运行一个应用程序,通过对鼠标的点击或键盘按键,产生一些特定事件。由于Windows一直监控着I/O设备,该事件首先会被翻译成消息,由系统捕获,存放于系统消息队列。经分析,Windows知道该消息应由那个应用程序处理,则拷贝到相应的应用程序消息队列。由于消息循环不断检索自身的消息队列,当发现应用程序消息队列里有消息,就用GetMessage()取出消息,封装成Msg()结构。如果该消息是由键盘按键产生的,用TranslateMessage()翻译为WM_CHAR消息,否则,用DisPatchMessage()将取出的消息分发到相应的应用程序窗口,交由窗口处理程序处理。Windows为每个窗体预留了过程窗口函数,该函数是一个回掉函数,由系统调用,应用程序不能调用。程序员可以通过重载该函数处理我们”感兴趣”的消息。对于不感兴趣的消息,则由系统默认的窗口过程处理程序做出处理。
下面看这么一张图:
这张图很好地解释了消息机制的运行原理。
当运行程序->事件操作引发消息->消息先存在系统消息队列->再存入到应用程序消息队列->用消息循环提取消息->处理消息->再返回消息队列....
==============================================================================================================================================================以上是系统消息的产生与处理
同样,我们可以通过自定义用户消息,用SendMessage()函数向窗口模拟发送消息,再重载窗口过程处理函数DefWndProc()来接收用户自定义消息,并作出处理。
因为SendMessage()是系统的一个API函数。因此在.NET平台中调用API函数需要,涉及到动态链接库的概念。需用动态链接库声明一个类,该类表明,用到哪个.DLL文件,以及程序入口点。
例如:
1 int xxx1 = 0x520; 2 int xxx2 = 0x521; //用户自定义消息。
1 [DllImport("User32.dll",EntryPoint="SendMessage")]//EntryPoint 表示要调用的函数入口点是dll文件里的SendMessage()函数。 2 private static extern int SendMessage( 3 ntPtr hwnd, //声明一个窗口句柄,所谓句柄,就是C#里的"指针",窗口或其他资源的一个引用,但此"指针"不可用于其他特殊操作。 4 //IntPtr是平台特定的int类型。就是在32跟64位系统上分别为32位和64位。一般用于声明一个句柄。 4 int Msg, //表明要发送的消息。 5 int wParam, //32位的附加信息。 6 int IPrarm //32位的附加信息。(随消息的改变而改变) 7 ); 8 // 此处由于SendMessage发送到本线程的窗口。所以发送的消息不会被加入到消息队列中,所以通过PeekMessage()或GetMessage()不能获取到由SendMessage发送的消息。
1 protected override void DefWndProc(ref Message m)//重载接收处理函数 2 { 3 switch (m.Msg) 4 { 5 case xxx1: label1.Text = "在吗"; break; 6 case xxx2: label1.Text = "没空"; break; //XXX1 /XXX2 用户自定义的消息。 7 default: base.DefWndProc(ref m); break; 8 } 9 }
什么是动态链接库?姑且看为”仓库”,里边放着一大堆待调用的函数。动态则表明随用随取,而不是程序用不用得上都要带上一大堆代码。C# 中用[DllImport]类声明调用API函数时,有三个条件,一是,函数名必须与原API函数名一致,二是参数也得一致,三是必须声明为”extern”。关于API函数的参数在MSDN上可以查到。这里不再赘述。
学过C/C++的朋友知道,写程序都要加个#include<xxx.h>的头文件,其实这就是动态链接。
====================================================================================================================================以上便是用户自定义消息,以及发送消息,和接收处理消息
下面由一个窗体的创建再讲一遍。Windows应用程序实际上具有相同的程序结构和执行控制流程。执行流程如下:
程序入口点(DOS下是main() ,windows下是_tWinMain())->注册窗口类(RegisterClass)->创建窗口(CreateWindow())->显示窗口(ShowWindow(hwnd,nCmdShow)->(UpdateWindow(hwnd))->消息循环(等待用户操作窗口产生消息)->放入消息队列->消息循环往复读取并执行相应代码->窗口函数(决定在窗口那里显示些什么,或者如何响应用户输入(如上面的SendMessage()函数))->消息处理(用来确定窗口函数接收的是什么消息以及如何处理(如上面的DefWndProc()函数)。
题外话:Windows和Dos编程,一个很大的区别就是,Windows编程是事件驱动,而Dos是过程驱动。所以,要学好Windows编程,起码要对消息机制有一个清楚的认识,否则很难编出多有用的程序来。
转载请注明出处:
博客园 :http://www.cnblogs.com/gu-zhan/ 老咸出品