Windows程序内部运行机制
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Windows程序内部运行机制相关的知识,希望对你有一定的参考价值。
Windows程序内部运行机制
一、 API与SDK
Windows操作系统提供了各种各样的函数,以方便我们开发Windows应用程序,这些函数是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称为API函数。我们在编写Windows程序时所说的API函数,就是指系统提供的函数,所有主要的Windows函数都在Windows.h头文件中进行了说明。
SDK的全称是Software Development Kit,中文译为软件开发包。SDK实际上就是开发所需资源的一个集合,包括API函数库、帮助文档、使用手册、辅助工具等资源。
二、 窗口与句柄
窗口是Windows应用程序中一个非常重要的元素,一个Windows应用程序至少要有一个窗口,称为主窗口。窗口是屏幕上的一块矩形区域,是Windows应用程序与用户进行交互的接口。
一个应用程序窗口通常包括标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框,有的还有滚动条。
窗口可以分为客户区和非客户区。客户区是窗口的一部分,应用程序通常在客户区中显示文字或者绘制图形。标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框统称为窗口的非客户区,它们由Windows系统来管理,而应用程序则主要管理客户区的外观及操作。
在Windows应用程序中,窗口是通过窗口句柄(HWND)来标识的。我们要对某个窗口进行操作,首先就要得到这个窗口的句柄。在Windows程序中,有各种各样的资源(窗口,图标、光标等),系统在创建这些资源时会为它们分配内存,并返回标识这些资源的标识号,即句柄。
三、 消息与消息队列
在Windows中,不仅用户程序可以调用系统的API函数,反过来,系统也会调用用户程序,这个调用是通过消息队列来进行的
Windows程序设计是一种基于事件驱动方式的程序设计模式,主要是基于消息的。用户与应用程序交互时,操作系统感知事件,将事件包装成一个消息,投递到应用程序的消息队列中,然后应用程序从消息队列中取出消息并进行响应。在这个处理过程中,操作系统也会给应用程序“发送消息”。所谓“发送消息”,实际上是操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程。
- 消息
在Windows程序中,消息是由MSG结构体定义的。
Windows将消息对应的数值定义为WM_XXX宏的形式,WM是Windows Message的缩写,XXX对应某种消息的英文拼写的大写形式。在程序中,我们通常都是以WM_XXX宏的形式来使用消息的。
2. 消息队列
每一个Windows应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。Windows将产生的消息依次放到消息队列中,而应用程序则通过一个消息循环不断地从消息队列中取出消息,并进行响应。这种消息机制,就是Windows程序运行的机制。
3. 进队消息和不进队消息
Windows程序中的消息可以分为“进队消息”和“不进队消息”。进队的消息将由系统放入到应用程序的消息队列中,然后由应用程序取出并发送。不进队的消息在系统调用窗口过程时,直接发送给窗口。不管是进队消息还是不进队消息,最终都由系统调用窗口过程函数对消息进行处理。
4. WinMain函数
当Windows操作系统启动一个程序时,调用该程序的WinMain函数(实际是由插入到可执行文件中的启动代码调用的)。WinMain是Windows程序的入口点函数,与DOC程序的入口点函数main的作用相同,当WinMain函数结束或返回时,Windows应用程序结束。
四、 编写一个完整的Win32程序
步骤:
WinMain函数的定义
创建一个窗口
进行消息循环
编写窗口过程函数
1. 窗口的创建
创建一个完整的窗口,需要经过下面几个操作步骤:
设计一个窗口类 WNDCLASS
注册窗口类 RegisterClass
创建窗口 CreateWindow
显示及更新窗口 ShowWindow UpdateWindow
一个Windows程序可以包含多个窗口过程函数,一个窗口过程总是与某一特定的窗口类相关联,基于该窗口类创建的窗口使用同一个窗口过程。
如果窗口创建成功,CreateWindow函数将返回系统为该窗口分配的句柄,否则返回NULL。注意,在创建窗口之前应先定义一个窗口句柄变量来接受创建窗口之后返回的句柄值。
UpdateWindow函数通过发送一个WM_PAINT消息来刷新窗口,UpdateWindow将WM_PAINT消息直接发送给了窗口过程函数进行处理,而没有放到消息队列中,请注意这一点。
2. 消息循环
在创建窗口、显示窗口、更新窗口后,需要编写一个消息循环,不断地从消息队列中取出消息,并进行响应。要从消息队列中取出消息,需要调用GetMessage()函数。
参数hWnd指定接收属于哪一个窗口的消息。通常将其设置为NULL,用于接收属于调用线程的所有窗口的窗口消息。
参数wMsgFilterMin指定要获取的消息的最小值,通常设置为0。
参数wMsgFilterMax指定要获取的消息的最大值。如果wMsgFilterMin和wMsgFilterMax都设置为0,则接受所有消息。
GetMessage函数只有在接收到WM_QIUT消息时,才返回0。此时,while语句判断的条件为假,循环退出,程序才有可能结束运行。在没有接收到WM_QIUT消息时,Windows应用程序就通过这个while循环来保证程序始终处于运行状态
五、 知识点
1. 创建一个Win32应用程序的步骤:
编写WinMain函数,可以在MSDN上查找并复制
设计窗口类
注册窗口类
显示并更新窗口
编写消息循环
编写窗口过程函数
2. 在Windows.h中,以CS_开头的类样式(Class Style)标识符被定义为16位的类常量,这些常量都只有某一位为1。用这种方式定义的标识符称为“位标志”,我们可以使用位运算操作符来组合使用这些样式。
3. 回调函数的实现机制是:
(1) 定义一个回调函数;
(2) 提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
(3) 当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
4. 针对Windows的消息处理机制,窗口过程函数被调用的过程如下:
(1) 在设计窗口类的时候,将窗口过程函数的地址付给lpfnWndProc成员变量;
(2) 调用RegisterClass(&wndclass)注册窗口类,那么系统就有了我们所编写的窗口过程函数的地址;
(3) 当应用程序接收到某一窗口的消息时,调用DispatchMessage(&msg)将消息回传给系统,系统则利用先前注册窗口类时得到的函数指针,调用窗口过程函数对消息进行处理。
5. 在函数调用过程中,会使用栈。_stdcall与_cdecl是两种不同的函数调用约定,定义了函数参数入栈的顺序,由调用函数还是被调用函数将参数弹出栈,以及产生函数修饰名的方法。
6. 在VC++中,对于自定义的菜单、图标、光标、对话框等资源,都保存在资源脚本(通常扩展名为.rc)文件中。资源文件本身是文本文件格式,如果了解资源文件的编写格式,也可以直接使用文本编辑器对资源进行编辑
7. 在VC++中,资源是通过标识符(ID)来标识的,同一个ID可以标识多个不同的资源。资源的ID实质上是一个整数,在“resource.h”中定义为一个宏。
8. GetStockObject函数不仅可以用于获取画刷句柄,还可以用于获取画笔、字体和调色板的句柄。
9. 要注意,菜单并不是一个窗口。
10. Windows应用程序的消息处理机制:
(1) 操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。
(2) 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理。
(3) 应用程序调用DispatchMessage,将消息回传给操作系统。消息由MSG结构体对象来表示,其中包含了接收消息的窗口的句柄。因此,DispatchMessage函数总能进行正确的传递。
(4) 系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理(即“系统给应用程序发送了消息”)。
11. 要注意区分WNDCLASS中的style成员和CreateWindow函数的dwStyle参数,前者指定窗口类的样式,基于该窗口类创建的窗口都有这些格式,后者是指定某个具体的窗口的样式。下面是几种常用的窗口类型的说明:
WS_OVERLAPPED:产生一个层叠的窗口,一个层叠的窗口有一个标题栏和一个边框;
WS_CAPTION:创建一个有标题栏的窗
WS_SYSMENU:创建一个在标题栏上带有系统菜单的窗口
WS_THICKFRAME:创建一个具有可调边框的窗口
12. TranslateMessage函数用于将虚拟键消息转换为字符消息。字符消息被投递到调用线程的消息队列中,当下一次调用GetMessage函数时被取出。注意,TranslateMessage函数并不会修改原有的消息,它只是产生新的消息并投递到消息队列中。
13. DispatchMessage函数分派一个消息到窗口过程,由窗口过程对消息进行处理。DispatchMessage实际上是将消息回传给操作系统,由操作系统调用窗口过程对消息进行处理。
14. 消息获取与发送的函数对比:
PeekMessage会立即返回 可以将消息保存在消息队列中 。
GetMessage在有消息时返回 会将消息从消息队列中移除。
PostMessage:把消息放到指定窗口所在的线程消息队列中后立即返回。
PostThreadMessage:把消息放到指定线程的消息队列中后立即返回。对于线程消息,应该将MSG结构体的hwnd成员设为NULL。
SendMessage:直接把消息送到窗口过程处理, 处理完了才返回。
15. DC(Device context) 设备描述表,设备上下文。
包含了设备信息的结构体,在Windows平台下所有的图形相关操作都利用DC来完成。GetDC(),ReleaseDC()成对使用,防止内存泄漏。
16. WM_PAINT
WM_PAINT 消息是Windows系统产生的,用于通知应用程序重绘其窗口的消息。通常情况下Windows程序第一次产生WM_PAINT消息是由即将进入消息循环之前的UpdateWindow函数发出的。Windows利用这个机会,绘制窗口程序的窗口。
WM_PAINT消息产生的几种情况:
窗口大小改变(取决于CS_HREDRAW,CS_VREDRAW的设置)
窗口从无到有
窗口最小化后恢复
被其他窗口覆盖后再显示。
使用BeginPaint()与EndPaint()获取DC,同样的BeginPaint()与EndPaint()这两个Parint只能在WM_PAINT消息中调用。
以上是关于Windows程序内部运行机制的主要内容,如果未能解决你的问题,请参考以下文章
一. Windows程序内部运行机制--Windows编程课程学习笔记
VC++深入详解 孙鑫 第一章 Windows程序内部运行机制