最近的课设涉及到调用系统API函数和窗体消息的发送,重载接收等。因此发表一篇浅文,希望能和大家共同探讨,学习。
首先,windows是一个基于视窗的消息驱动系统。这句话非常的有意思。重点是"视窗"和"消息驱动"这两个词,这两个词包含了windows界面的处理原理。下面解释下什么是消息驱动。所谓"消息",即"事件"。接触过C#.NET的朋友都知道,你拖一个控件,双击,添加代码,这一段即可称为"事件"。严肃点来讲,所谓"事件",即是指人们使用计算机的过程中发生的操作,如按下键盘上的某个按键、移动鼠标、单击鼠标左键、移动窗口及选择程序菜单等等等等。
那么,什么是消息驱动呢?消息驱动,也叫做事件驱动。通俗点来讲,你对窗体做一个操作,windows做出相应的响应,实现相应的功能。这就叫消息驱动。严肃点说,每当发生一个事件,即我们对程序做出一个操作,程序就会产生一个与之对应的特定消息,该消息会被Windows捕获,然后将通过此消息调用执行与之对应的程序代码,实现功能。windows对于每一个产生的消息都会建立一些"消息队列"。在操控窗口时产生的消息就放在消息队列里。每个应用程序对应有一个消息循环,该循环不断重复查看自身消息队列,如果有消息,就用GetMessage()函数从消息队列中提取出,并执行相应的消息处理代码。如果队列中没有消息就等待,当队列中有消息时就又处理它,如此循环往复,直至程序结束或遇到WIN_QUIT;
下面看这么一张图:
当运行程序->事件操作引发消息->消息先存在系统消息队列->再存入到应用程序消息队列->用消息循环提取消息->处理消息->再返回消息队列....
以上解释如果看不懂没关系。咱下面继续慢慢解释。
首先,先抛开消息驱动这个原题。我们先引入一个叫"动态链接库"的概念。什么是动态链接库?刚接触过C#的朋友可能有听过,但又没怎么了解过。不急,咱慢慢来。动态链接库,这个名字也挺有意思。分为三个部分,动态,链接和库。首先它是一个库,什么库?暂且通俗地理解为一个仓库。放着一大堆函数的仓库。然后它是动态的,什么是动态的?动态就是一开始时程序是没有的,等运行到了,需要了,就去那个库里寻找调用。那为什么是动态的?因为假如弄为静态的,程序将会动不动带上一大堆代码,用倒是不一定用得上,但是程序空间会变大,效率等都会受到影响。不知道看这篇文章的朋友写过C/C++的程序没有,C/C++中有个#include<xxx.h>的头文件对吧,没错,这就是"动态连结"。那在C#中如何用上动态链接库呢,没错,就是[DllImprot("xxx.dll")]类。C#中,用此类声明一个实例,声明用到的.DLL文件。就可以调用API函数啦(API函数封装在.DLL文件里)。但是有几个要求,就是调用的API函数名必须与原来系统的API函数名一致,参数也得一致,并且必须声明为extern ...;Sample:
1 int xxx1 = 0x520; 2 int xxx2 = 0x521; //用户自定义消息。
1 [DllImport("User32.dll",EntryPoint="SendMessage")]//EntryPoint 表示要调用的函数入口点是dll文件里的SendMessage()函数。 2 private static extern int SendMessage( 3 IntPtr hwnd, //声明一个窗口句柄,所谓句柄,就是C#里的"指针",窗口的一个引用,但此"指针"不可用于其他特殊操作。 //IntPtr是平台特定的int类型。就是在32跟64位系统上分别为32位和64位。一般用于声明一个句柄。 4 int Msg, //表明要发送的消息。 5 int wParam, //32位的附加信息。 6 int IPrarm //32位的附加信息。(随消息的改变而改变) 7 );
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 }
DefWndProc()函数的作用就是让窗体接收相应的"消息",并做出处理,功能实现。
以上说了这么多无非是,发给一个窗体消息,然后让窗体接收这个消息再处理这个消息。那么这跟我们的原目的有什么挂钩呢?
windows应用程序实际上具有相同的程序结构和执行控制流程。比如一个API调用程序的建立、执行流程如下:
程序入口点(DOS下是main() ,windows下是_tWinMain())->注册窗口类(RegisterClass)->创建窗口(CreateWindow())->显示窗口(ShowWindow(hwnd,nCmdShow)->(UpdateWindow(hwnd))->消息循环(等待用户操作窗口产生消息)->放入消息队列->消息循环往复读取并执行相应代码->窗口函数(决定在窗口那里显示些什么,或者如何响应用户输入(如上面的SendMessage()函数))->消息处理(用来确定窗口函数接收的是什么消息以及如何处理(如上面的DefWndProc()函数)。
笔者文笔不佳,知识也并不扎实,写出来只是让大家指教指教,还望能严厉批评, 增进彼此。
题外话:为什么学.NET还要学习消息驱动原理这些比较接近底层的东西?按照我们老师讲的,你就是在学校毕业前有两万行代码,连这个都不知道,就算你再有几万行,你始终也编不出怎么多好,多有用的程序来。