注意:第一部分 以及后面所有的控件里面的图标都是Windows本身自带的图标
创建窗口CreateWindow
背景知识
进行Windows程序设计,实际上是在进行一种对象导向的程序设计(OOP)。
桌面上最明显的窗口就是应用程序窗口。这些窗口含有显示程序名称的标题列、菜单甚至可能还有工具列和滚动条。另一类窗口是对话框,它可以有标题列也可以没有标题列。
装饰对话框表面的还有各式各样的按键、单选按钮、复选框、清单方块、滚动条和文字输入区域。其中每一个小的视觉对象都是一个窗口。更确切地说,这些都称为「子窗口」或「控件窗口」或「子窗口控件」。
当使用者改变窗口的大小时,Window给程序发送一个消息指出新窗口的大小。然后程序就可以调整窗口中的内容,以响应大小的变化
就是相应对应的信号 然后调整自己窗口的大小
window窗口处理消息的原理
程序建立的每一个窗口都有相关的窗口消息处理程序。这个窗口消息处理程序是一个函数,
既可以在程序中,也可以在动态链接库中。Windows通过呼叫窗口消息处理程序来给窗口
发送消息。窗口消息处理程序根据此消息进行处理,然后将控制传回给Windows。
Windows程序开始执行后,Windows为该程序建立一个「消息队列」。这个消息队列用来
存放该程序可能建立的各种不同窗口的消息。程序中有一小段程序代码,叫做「消息循环」,
用来从队列中取出消息,并且将它们发送给相应的窗口消息处理程序。有些消息直接发送给
窗口消息处理程序,不用放入消息队列中。
程序
建立一个窗口首先需要注册一个窗口类别,那需要一个窗口消息处理程序来处理窗口消息。
处理窗口消息对每个Windows程序都带来了些负担。
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPreInstance, _In_ LPSTR SzCmdLine, _In_ int iCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
//指定窗口的风格
wndclass.style = CS_HREDRAW | CS_VREDRAW;
//指定窗口消息的处理程序
wndclass.lpfnWndProc = WndProc;
//下面是类扩展字和窗口扩展字
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
// 加载图标供程序使用
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
//加载鼠标光标供程序使用
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
//取得一个图形对象
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
//类名
wndclass.lpszClassName = szAppName;
// 为程序窗口注册窗口类别
if (!RegisterClass(&wndclass))
{
//显示消息框。 没有注册成功
MessageBox(NULL, TEXT("this program require Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
//根据窗口类别建立一个窗口。
hwnd = CreateWindow(szAppName, // window class name
TEXT("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT,// initial x position
CW_USEDEFAULT,// initial y position
CW_USEDEFAULT,// initial x size
CW_USEDEFAULT,// initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
// 在屏幕上显示窗口。
ShowWindow(hwnd, iCmdShow);
//指示窗口自我更新。
UpdateWindow(hwnd);
// 从消息队列中取得消息
while (GetMessage(&msg, NULL, 0, 0))
{
//转译某些键盘消息
TranslateMessage(&msg);
//将消息发送给窗口消息处理程序。
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
// 播放一个声音文件
PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);
return 0;
case WM_PAINT:
// 开始绘制窗口
hdc = BeginPaint(hwnd, &ps);
//取得窗口显示区域的大小
GetClientRect(hwnd, &rect);
//显示字符串
DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
//结束绘制窗口
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
//在消息队列中插入一个「退出程序」消息。
PostQuitMessage(0);
return 0;
}
//执行内定的消息处理
return DefWindowProc(hwnd, message, wParam, lParam);
}
前缀 类别
CS 窗口类别样式
CW 建立窗口
DT 绘制文字
IDI 图示ID
IDC 游标ID
MB 消息框
SND 声音
WM 窗口消息
WS 窗口样式
不用特意去记忆 WinUser 每个特定的标志会注释
WndProc的第三和第四个参数分别被定义为
WPARAM和LPARAM,这些名字的来源有点历史背景:当Windows还是16位系统时,
WndProc的第三个参数被定义为一个WORD,这是一个16位的 无正负号短(unsigned
short)整数,而第四个参数被定义为一个LONG,这是一个32位有正负号长整数,从而导
致了文字「PARAM」前面加上了前置前缀「W」和「L」。当然,在32位的Windows中,
WPARAM被定义为一个UINT,而LPARAM被定义为一个LONG(这就是C中的long整数型
态),因此窗口消息处理程序的这两个参数都是32位的值。
WndProc函数传回一个型态为LRESULT的值,该值简单地被定义为一个LONG。WinMain
函数被指定了一个WINAPI型态
句柄简介
句柄是一个(通常为32位的)整数,它代表一个对象。Windows中的句柄类似传统C或者
MS-DOS程序设计中使用的文件句柄。程序几乎总是通过呼叫Windows函数取得句柄。程
序在其它Windows函数中使用这个句柄,以使用它代表的对象。代号的实际值对程序来说
是无关紧要的。但是,向您的程序提供代号的Windows模块知道如何利用它来使用相对应
的对象。
经常会用句柄来表示一个对象
注册窗口类别
窗口依照某一窗口类别建立,窗口类别用以标识处理窗口消息的窗口消息处理程序。 不同窗口可以依照同一种窗口类别建立。
窗口类别定义了窗口消息处理程序和依据此类别建立的窗口的其它特征
其实就是一个功能定义,定义了消息的处理方式和有哪些工具集,可以多个窗口指定一个窗口类型类别。其余窗口按照这样消息处理方式和工具集去处理或者安排自身的事件循环
在WNDCLASS结构中最重要的两个字段是第二个和最后一个,第二个字段(lpfnWndProc) 是依据这个类别来建立的所有窗口所使用的窗口消息处理程序的地址。在HELLOWIN.C中,这个是WndProc函数。最后一个字段是窗口类别的文字名称。程序写作者可以随意定义其名称。在只建立一个窗口的程序中,窗口类别名称通常设定为程序名称。
建立窗口
窗口类别定义了窗口的一般特征,因此可以使用同一窗口类别建立许多不同的窗口。实际呼叫CreateWindow建立窗口时,可能指定有关窗口的更详细的信息。
最上层的窗口 ,注释为「父窗口句柄」的参数设定为NULL
如果没有菜单 , 「窗口菜单句柄」也设定为NULL。
「程序执行实体句柄」设定为执行实体句柄,它是作为WinMain的参数传递给这个程序的。
最后,「建立参数」指标设定为NULL,可以用这个参数存取稍后程序中可能引用到的数据。
CreateWindow传回被建立的窗口的句柄,该句柄存放在变量hwnd中,后者被定义为HWND型态(「窗口句柄型态」)。Windows中的每个窗口都有一个句柄,程序用句柄来使用窗口。许多Windows函数需要使用hwnd作为参数,这样,Windows才能知道函数是针对哪个窗口的。如果一个程序建立了许多窗口,则每个窗口均有一个句柄。窗口句柄是Windows程序所处理最重要的句柄之一。
显示窗口
在CreateWindow 创建之后 window已经有这个窗口并且为其配备了对应的内存 但是并不会直接显示在界面上
需要调用两个函数才可以
ShowWindow (hwnd, iCmdShow) ;
第一个参数是刚刚用CreateWindow建立的窗口句柄。第二个参数是作为参数传给WinMain的iCmdShow。它确定最初如何在屏幕上显示窗口,是一般大小、最小化还是最大化。
UpdateWindow (hwnd) ;
会重画显示区域。它经由发送给窗口消息处理程序(即HELLOWIN.C中的WndProc函数),一个WM_PAINT消息做到这一点。
消息循环
UpdateWindow (hwnd) 之后窗口就会正常显示 , 每个程序Window都会为其维护一个消息队列 ,当用户输入的时候 ,window将其转换为对应的消息存入消息队列。
程序通过执行一块称之为「消息循环」的程序代码从消息队列中取出消息:
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
MSG对应的结构体
Point对应的结构体
消息循环以GetMessage呼叫开始,它从消息队列中取出一个消息:
GetMessage (&msg, NULL, 0, 0)
后面的参数为NUll和0 表示程序接收它自己建立的所有窗口的所有消息。
这里面各个字段包括:
- hwnd 接收消息的窗口句柄。在HELLOWIN程序中,这一参数与CreateWindow传回的hwnd值相同,因为这是该程序拥有的唯一窗口。
- message 消息标识符。这是一个数值,用以标识消息。对于每个消息,均有一个对应的标识符,这些标识符定义于Windows表头文件(其中大多数在WINUSER.H中),以前缀WM(「window message」,窗口消息)开头。例如,使用者将鼠标光标放在HELLOWIN显示区域之内,并按下鼠标左按钮,Windows就在消息队列中放入一个消息,该消息的message字段等于WM_LBUTTONDOWN。这是一个常数,其值为0x0201。
- wParam 一个32位的「message parameter(消息参数)」,其含义和数值根据消息的不同而不同。
- lParam 一个32位的消息参数,其值与消息有关。
- time 消息放入消息队列中的时间。
- pt 消息放入消息队列时的鼠标坐标。
只要从消息队列中取出消息的message字段不为WM_QUIT(其值为0x0012),GetMessage就传回一个非零值。WM_QUIT消息将导致GetMessage传回0。
DispatchMessage (&msg) ;
又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这也就是说,Windows将呼叫窗口消息处理程序。在HELLOWIN中,这个窗口消息处理程序就是WndProe函数。处理完消息之后,WndProc传回到Windows。此时,Windows还停留在DispatchMessage呼叫中。在结束DispatchMessage呼叫的处理之后,Windows回到HELLOWIN,并且接着从下一个GetMessage呼叫开始消息循环。
窗口消息处理程序
就是我们接受到对应的消息之后回触发什么样的逻辑或者动作,如何相应用户的输入
在HELLOWIN中,窗口消息处理程序是命名为WndProc的函数。窗口消息处理程序可任意命名(只要求不和其它名字发生冲突)。一个Windows程序可以包含多个窗口消息处理程序。一个窗口消息处理程序总是与呼叫RegisterClass注册的特定窗口类别相关联。CreateWindow函数根据特定窗口类别建立一个窗口。但依据一个窗口类别,可以建立多个窗口。
窗口消息处理程序总是定义为如下形式:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
注意,窗口消息处理程序的四个参数与MSG结构的前四个字段是相同的。第一个参数hwnd 是接收消息的窗口的句柄,它与CreateWindow函数的传回值相同。对于与HELLOWIN相似的程序(只建立一个窗口),这个参数是程序所知道的唯一窗口句柄。如果程序是依据同一窗口类别(同时也是同一窗口消息处理程序)建立多个窗口,则hwnd标识接收消息的特定窗口。
处理消息
Windows表头文件WINUSER.H为每个消息参数定义以「WM」(窗口消息)为前缀开头的标识符。
一般来说,Windows程序写作者使用switch和case结构来确定窗口消息处理程序接收的是什么消息,以及如何适当地处理它。窗口消息处理程序在处理消息时,必须传回0。窗口消息处理程序不予处理的所有消息应该被传给名为DefWindowProc的Windows函数。从DefWindowProc传回的值必须由窗口消息处理程序传回。呼叫DefWindowProc来为窗口消息处理程序不予处理的所有消息提供内定处理,这是很重要的。不然一般动作,如终止程序,将不会正常执行。