1 Windows程序内部运行原理
1.1 关于API
在计算机基础课程中,我们都知道计算机有硬件和软件构成。所谓硬件就是那些铁块,比如主板、内存、硬盘、CPU、光驱、声卡、显卡、网卡、显示器等;软件就是用于控制这些硬件和为用户服务的的各类工具,比如操作系统软件就是紧挨着硬件的软件,主要管理这些硬件。
用户在使用计算机的时候,主要通过输入输出设备,比如键盘、鼠标、显示器等,对于这些硬件,当然也是操作系统来管理的。
(1)操作系统能够操纵输入输出设备
(2)操作系统能够感知输入输出设备状态的变化
(3)应用程序可以通知操作系统执行某个具体的动作——调用Windows操作系统提供给应用程序的编程接口API
(4)操作系统能够将输入输出设备的变化传给应用程序——按了一下键盘,操作系统马上感知到这一事件,并且
能够知道用户按下的是哪一个键,操作系统并不决定对这一事件如何反应,而是将这一事件转交给应用程序,由应用程序决定如何反应。
1.2 关于消息和消息队列
对事件作出反应的过程就是消息响应。
操作系统是怎样将感知到的事件传递给应用程序的呢?这是通过消息机制(Message)实现的。操作系统将每个事件都包装成一个称为消息的结构体MSG来传递给应用程序。
type struct tagMSG
{
HWND hwnd; 窗口句柄Handle——标识
UINT message; 无符号整数表示消息,难记忆,定义了一些宏,如WM_LBUTTONDOWN
WPARAM wParam; 整型,表示关于消息的附加信息
LPARAM lParam; 整型,表示关于消息的附加信息
DWORD time; 消息投递的时间
POINT pt; 消息投递时,光标在屏幕上的位置
} MSG;
如WM_CHAR消息,按下具体哪个键,如A,就放在WPARAM中
关于句柄
• 句柄(HANDLE),资源的标识。
• 操作系统要管理和操作这些资源,都是通过句柄来找到对应的资源。按资源的类型,又可将句柄细分成图标句柄(HICON),光标句柄(HCURSOR),窗口句柄(HWND),应用程序实例句柄(HINSTANCE)等等各种类型的句柄。操作系统给每一个窗口指定的一个唯一的标识号即窗口句柄。
消息队列
1.(1)每一个应用程序,操作系统都为其建立一个消息队列,先进先出,每个元素都是消息,操作系统将生成的每个消息按生成的先后顺序按入消息队列。
2.(2)应用程序总是取走队列的第一条消息,消息取走后,第二条消息成为第一条消息,剩余的消息依次前移。
3.(3)应用程序取走消息后,就能够知道用户的操作和程序状态的变化。例如应用程序从队列里边取走了WM_CHAR消息,一定是用户输入了一个字符,并且知道是哪个消息。然后就要对消息进行处理——这就是消息响应——编写消息响应代码(要用到操作系统提供的API函数)。
1.3 Windows程序的基本结构
WinMain函数
Windows程序的入口函数
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
);
窗口的创建
创建一个完整的窗口需要经过下面四个操作步骤:
1.设计一个窗口类;
2.注册窗口类;
3.创建窗口;
4.显示及更新窗口。
设计窗口类
typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS;
窗口类的类型
在我们的程序中经常要用到一类变量,这个变量里的每一位(bit)都对应某一种特性。当该变量的某位为1时,表示有该位对应的那种特性,当该位为0时,即没有该位所对应的特性。当变量中的某几位同时为1时,就表示同时具有几种特性的组合。一个变量中的哪一位代表哪种意义,不容易记忆,所以我们经常根据特征的英文拼写的大写去定义一些宏,该宏所对应的数值中仅有与该特征相对应的那一位(bit)为1,其余的bit都为0。我们使用goto definition就能发现CS_VREDRAW=0x0001,CS_HREDRAW=0x0002,CS_DBLCLKS =0x0008,CS_NOCLOSE=0x0200。他们的共同点就是只有一位为1,其余位都为0。如果我们希望某一变量的数值既有CS_VREDRAW特性,又有CS_HREDRAW特性,我们只需使用二进制OR(|)操作符将他们进行或运算相组合,如style=CS_VREDRAW | CS_HREDRAW | CS_NOCLOSE。如果我们希望在某一变量原有的几个特征上去掉其中一个特征,用取反(~)之后再进行与(&)运算,就能够实现,如在刚才的style的基础上去掉CS_NOCLOSE特征,可以用style & ~CS_NOCLOSE实现。
窗口过程函数
第二个成员变量lpfnWndProc指定了这一类型窗口的过程函数,也称回调函数。回调函数的原理是这样的,当应用程序收到给某一窗口的消息时(还记得前面讲过的消息通常与窗口相关的吗?),就应该调用某一函数来处理这条消息。这一调用过程不用应用程序自己来实施,而由操作系统来完成,但是,回调函数本身的代码必须由应用程序自己完成。对于一条消息,操作系统到底调用应用程序中的哪个函数(回调函数)来处理呢?操作系统调用的就是接受消息的窗口所属的类型中的lpfnWndProc成员指定的函数。每一种不同类型的窗口都有自己专用的回调函数,该函数就是通过lpfnWndProc成员指定的。
2 Windows图形设备接口
Windows应用程序使用图形设备接口和Windwows设备驱动程序来支持与设备无关的图形。图形设备接口(GDI)是Windows系统的重要组成部分,负责系统与用户或绘图程序之间的信息交换,并控制输出设备上显示图形或文字。
计算机输出设备和显示设备种类繁多,包括不同技术标准的显示器、打印机、绘图仪等,每类设备有包含许多不同的型号。为了适应不同的设备,Windows系统提供应用程序与具体设备分离的功能。操作系统管理并协调一系列输出设备驱动程序,将应用程序的图形输出请求转换为打印机、绘图仪、显示器或其他输出设备上的输出。GDI的设备无关性是Windows操作系统的特色之一。
对于开发人员而言,所要做的工作仅仅是在系统的帮助下建立一个与某个实际输出设备的关联,以要求系统加载相应的设备驱动程序,其他的具体输出操作则由系统实现。由此可见,Windows系统分担了应用程序的硬件设备适配器的功能。
设备描述表是一个用来确定任何设备的GDI输出位置和形象的属性的集合。应用程序并不能直接存取设备描述表,但是,应用程序可以使用设备描述表的句柄来间接存取设备描述表及其属性。当程序为设备描述表要求一个句柄时,就将创建一个设备描述表。
获取设备环境是应用程序输出图形的先决条件。
常用的两种获取设备环境的方法是调用BeginPaint或GetDC。
1 调用BeginPaint函数
应用程序响应WM_PAINT消息进行图形刷新时,主要通过BeginPaint函数获取设备环境,其形式为:
Hdc=BeginPaint(hwnd,&ps); //PAINTSTRUCT ps
EndPaint(hwnd,&ps);
2 调用GetDC函数
如果Windows应用程序的绘图工作并非由WM_PAINT消息驱动,则调用GetDC函数获取设备描述表。
Hdc=GetDC(hwnd); //函数获取DC句柄
ReleaseDC(hwnd); //函数释放DC占据的资源