0%

win32编程(二)

这一章讲一讲七个步骤

简介

窗口类:是包含窗口之中信息的数据结构

每个窗口都有窗口类

1
2
3
4
5
6
7
1 定义WinMain函数
2 定义窗口处理函数(处理消息)
3 注册窗口类
4 创建窗口(在内存中创建窗口)
5显示窗口(根据内存的数据将窗口绘制出来)
6消息循环(提取/翻译/派发)消息
7消息处理

接下来我们看一段代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include "windows.h"

//窗口处理函数
LRESULT WndProc(HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam)
{
return DefWindowProc(hWnd,nMsg,wParam,lParam);
}
//定义WinMain函数
int WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS wc={0};
HWND hWnd = 0;
MSG nMsg = {0};
//定义窗口处理函数
wc.style = CS_HREDRAW | CS_VREDRAW;//结构体风格
wc.lpfnWndProc = (WNDPROC)WndProc;//指向窗口过程的指针
wc.cbClsExtra = 0;//配置给这个类的额外内存
wc.cbWndExtra = 0;//配置给这个类每个窗口的额外内存
wc.hInstance = hInstance;//应用程序实例句柄
wc.hIcon = NULL;//图标资源句柄
wc.hCursor = NULL;//光标资源句柄
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//窗口颜色背景刷子
wc.lpszMenuName = NULL;//这个类窗口菜单资源名字
wc.lpszClassName = "Main";//主窗口类名
//注册窗口类
RegisterClass(&wc);
//创建窗口(在内存中创建) hWnd=CreateWindow("Main","hWnd",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hInstance,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
//消息循环
while(GetMessage(&nMsg,NULL,0,0))
{
//消息处理
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}
return 0;
}

在窗口处理函数中, DefWindowProc这个函数是默认的窗口处理函数,我们可以把不关心的消息都丢给它来处理。需要我们处理的我们再做处理。这个函数在处理关闭窗口消息WM_CLOSE时,是调用 DestroyWindow函数关闭窗口并且发 WM_DESTROY消息给应用程序;而它对WM_DESTROY这个消息是不处理的;我们在应用程序中对这个消息的处理是发出WM_QUIT消息。因此WM_CLOSE、WM_DESTROY、WM_QUIT这三个消息是先后产生的。

对于RegisterClass,我们可以看看解释

1
2
RegisterClass is basically you defining a class and including it in your program (much like a #include in C++).
CreateWindow is conceptually the same as you doing a new in C++. You're asking Windows to create a new window, and you've got to tell it the type of window. Windows includes a set of predefined windows, such as Button or Edit, but if you want to create an instance of your own window then that's fine, you just need to tell it the "class" you'd like to create. You've already registered this class by calling RegisterClass, so Windows can now go straight to the definition and create an instance of your window.

Windows系统包含一系列的预定义的窗口,例如按钮(button)、编辑框(edit)等。但如果你要创建一个属于你自己的个性窗口,你就要告诉系统这个窗口的“类”。而这个类你已经通过RegisterClass注册过了,所以系统会直接创建你定制的这个窗口类的实例。

CreateWindowEx内部实现:

根传入的名称,先在局部窗口类查找,找到了再将hinstance是否一样,如果相等,就基于它创建。若不相等。就在全局窗口类寻找,若还是找不到,就在系统窗口类查找。均找不到,则创建失败。

创建子窗口:

创建时设置父窗口句柄,创建风格加WS_CHILD|WS_VISIBLE.

消息:

消息基础:

组成:窗口句柄,消息ID,消息的两个参数,消息参数的时间,消息产生时鼠标位置

作用:当系统通知消息工作时,就采用消息的方式派发给窗口

我们可以看看MSG结构体的内容:

1
2
3
4
5
6
7
8
9
10
typedef struct tagMSG {
// msg
HWND hwnd;//窗口句柄
UINT message;//消息ID
WPARAM wParam;
LPARAM lParam;
//两个参数
DWORD time;//消息产生时间
POINT pt;//消息产生时鼠标的位置
} MSG;
1
2
3
4
5
6
DispatchMessage(&nMsg){
nMsg.hwnd-->保存窗口数据的内存-->WndProc
WndProc(nMsg.hwnd,nMsg.message,nMsg.wParam,nMsg.IParam)//最后两个不用写
//回到我们自己代码(可以处理消息)
}
//将消息派发到窗口

所以每一个窗口都有窗口处理函数

浅谈消息相关函数:

GetMessage
1
2
3
4
5
6
7
GetMessage{
IpMsg//存放获取到消息的BUFF
hWnd//窗口句柄
wMsgFilterMin//获取消息的最小ID
wMsgFiterMax//获取消息的最大ID
//两个都为0代表可以抓本进程所有消息
}

简单说说,假如我们想关闭进程,就将WM_QUIT放入本进程某个位置,然后GetMessage就会在系统中抓取这个消息

TranslateMessage

翻译消息。将按键消息,翻译成字符消息

检查消息是否时按键消息,如果是,就翻译消息,不是就return 0;

常用消息

WM_DESTROY:

窗口被摧毁时产生,摧毁之前,做相应善后处理,例如资源,消息

WM_SYSCOMMAND:

产生时间:窗口最大化,最小化,和关闭等

附带参数 wParam:具体点击的位置;IParam:鼠标光标位置(LOWORD:水平 HIWORD:垂直)

用法:常在窗口关闭时,提示用户处理

WM_CREATE:

产生时间:创建窗口但未显示时

IParam:为CREATESTRUCT类型的指针,通过它可以获取CreateWindowEx中的12个参数

用法:初始化窗口参数,资源等,包括创建子窗口

WM_SIZE:

产生时间:窗口大小发生变化时。

参数: wParam:窗口大小变化的原因 ;IParam:窗口变化后大小(LOWORD:变化后宽度 HIWORD:变化后高度)

用法:窗口大小变化时,调整窗口各个部分布局

WM_QUIT:

产生时间:程序员发送

参数:wParam:PostQuitMessage函数传递的参数 ;

用法:结束消息循环,当GetMessage收到消息时,返回false,退出循环

消息循环阻塞:

GetMessage-系统获取消息,将消息从系统中移除,阻塞函数,无消息时,等候下一条消息

PeekMessage-以查看方式从系统中获取,不将消息从系统中移除,非阻塞函数。无消息时,返回false,执行后面代码

发送消息:

SendMessage-发送消息,等候处理结果

PostMessage-投递消息,消息发出后立刻返回,不等侯消息执行结果。

系统消息ID范围(0-0x3FF)

用户自定义ID范围(0x0400-0x7FFF)

自定义消息宏:WM_USER

消息队列:

进程产生的消息先进系统队列,每隔一段时间,系统队列消息会发送到各个进程队列

postmessage消息扔到了系统队列

sendmessage消息既不进系统队列,也没进本进程队列

所以可知,sendmessage为非队列消息,他是直接将消息发送到窗口的函数,等候消息处理结果

一个消息无队列和非队列之分,看是否放入队列

WM_QUIT必须进队列(不仅队列,进程无法终止),WM-CREATE必须不能进队列(若进队列,无法抓取)

深谈GetMessage

1.在本进程查找消息,检查是否满足抓取

2.向操作系统队列抓消息(让系统分发消息,打破时间界限)

3.检查进程所有窗口需要绘制,若需要,产生WM_PAINT消息,取得消息返回处理

4.检查定时器,若某一定时器时间到,产生WM_TIMER消息

5.没有定时器,整理程序资源,内存等。

6.GetMessage等候下一条消息,PeekMessage返回false。交出程序控制权

下一节,讲讲资源