Windows程序设计之简单窗口的实现
要实现一个窗口的显示要经过以下步骤:
(1)创建窗口类
(2)注册窗口类
(3)创建窗口
(4)显示窗口
下面依次展示每个步骤的实现过程。
创建窗口类
Windows编程中主函数原型如下:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, PSTR szCmdLine, int iCmdShow)
在C语言中,程序的入口是main函数。相似的,在Windows程序中,WinMain是程序的入口。WINAPI是在头文件WINDEF.H中定义的标识符(这里不是很懂,等以后懂了再来补充)
#define WINAPI __stdcall
WinMain的第一个函数的参数叫做实例句柄。句柄其实就是一个数值,它是一些东西的标识。在这个程序中,作为第一个参数的实例句柄唯一标识。在Windows程序设计中,我们经常将句柄当做参数传递。第二个参数hPreInstance是标识了其他实例的句柄。第三个参数是用来运行程序的命令行,Windows程序在启动时用它把文件装入内存。第四个参数用来表示程序在一开始如何显示,正常、最大或最小化显示。
在结束这一节之前,我想先介绍一下匈牙利命名法。匈牙利命名法用位于变量名最前面的小写字母表示变量类型,之后采用驼峰命名法表示一个变量。比如hInstance,h表示该变量是一个句柄,Instance表示该变量是一个实例,二者合起来就表示这是一个实力句柄。Windows程序中的变量名前缀如下表:
前缀 | 数据类型 |
---|---|
c | char or WCHAR or TCHAR |
by | 无符号字符 |
n | short |
i | int |
x,y | int x,y坐标 |
cx,cy | int x,y的长度 |
B,f | BOOL(int),f表示flag |
w | WORD无符号短整型 |
l | LONG长整型 |
dw | DWOED无符号长整型 |
fn | 函数 |
s | 字符串 |
sz | 以0结束的字符串 |
h | 句柄 |
p | 指针 |
cb | 字节数 |
br | handle to a brush(画刷的句柄) |
注册窗口类
再创建完窗口类之后,我们还需要注册窗口类之后才能创建真正的串口。就像我们在做一些事的时候要得到许可才可以做这件事,比如进入电影院看电影,我们需要电影票来得到进入电影院的许可。注册窗口类的函数调用方式如下:
RegisterClass(&winclass)
成功返回1,失败则返回0。
注意,在进行窗口注册前,我们只是定义了winclass但是还没有对其进行初始化。在注册窗口类之前,我们先对其进行初始化。
WNDCLASS是Windows操作系统维护的一个数据结构,它的原型如下:
typedef struct tagWNDCLASS{
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hIstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
}WNDCLASS, * PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA;
还记得之前说过的匈牙利命名法吗?我们来看第二个成员,lpfn就表示“指向函数的长整型指针”,cb表示字节数,常常用在表示字节数的变量中。
下面我们依次对每个成员初始化。
winclass.style表示窗口的风格,我们将其做如下初始化;
wndclass.style = CS_VREDRAW|CS_HREDRAW;
CS_VREDRAW代表窗口的水平尺寸,CS_HREDRAW代表了窗口的垂直尺寸。他们在头文件WINUSER.H中定义如下:
#define CS_VREDRAW 0x0001
#define CS_HREDRAW 0x0002
(补充说明一下,CS前缀代表类风格选项)
winclass.lpfnWndProc = WndProc;
对该类的第二个成员初始化。WndProc是窗口过程处理函数,我们对窗口进行的一系列操作,比如在窗口中绘图、点击鼠标和点击键盘等操作都在这个处理函数中进行。
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
cbClsExtra 和 cbWndExtra表示提供给Windows系统所维护的窗口额外空间(具体的使用方法等我学到再说【手动狗头】)
winclass.hInstance = hInstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
该语句为所有基于该窗口类的窗口设置图标,这里我们使用的是默认图标。后期学到加载图标时再详细讨论这个函数的用法。
winclass.hCursor = LoadCursor(NULL, IDI_ARROW);
与上一个函数类似,该语句为所有基于该窗口类的窗口设置鼠标图标,我们依然采用默认的图标。
winclass.hbrBackGround = GetStockObject(BLACK_BRUSH);
hbr表示画刷的句柄(handel to a brush),我们将背景填充为黑色。
winclass.lpszMenuName = NULL;
我们不需要菜单,直接NULL。
winclass.lpszAppName = szAppName;
最后我们为窗口赋予一个名称,szAppName是一个数组,里面保存表示窗口名称的字符串。
终于,我们完成了窗口类的初始化,可以注册窗口类了。不过在这之前,还有一点要说明。窗口类注册函数由两个,一个是RegisterA,另一个是RegisterB。RegisterA函数接受的参数是指向WNDCLASS类的指针,而RegisterB接受的参数是指向WNDCLASSN的指针。用哪个函数注册窗口,决定了之后传递给窗口的消息是ASCII文本还是UNICODE文本。下面我们开始注册窗口类函数
if(!RegisterClass(&winclass)){
/*do something*/
return 0;
}
当注册失败时则执行do something
创建窗口
接下来我们需要创建窗口,创建窗口函数返回该窗口的句柄。
HWND hWnd = CreateWindow(
szAppName, //窗口类名称
TEXT("hello windows"), //窗口名称
WS_OVERLAPPEDWINDOW, //窗口格式,层叠型
CW_USEDEFAULT, //初始x坐标
CW_USEDEFAULT, //初始y坐标
CW_USEDEFAULT, //x方向长度
CW_USEDEFAULT, //y方向长度
NULL, //父窗口句柄
NULL, //窗口菜单句柄
hInstance, //该程序实例句柄
NULL //创建参数
);
同样存在CreateWindowA和CreateWindowW函数,他们分别将头两个参数当做ASCII字符串和UNICODE字符串处理。当窗口创建函数成功返回时,内存中已经开辟出了一块区域用来保存建立一个窗口所用的信息,我们用一个HWND型的句柄来获取这些信息。
显示窗口
ShowWindow(hWnd, iCmdShow)
iCmdShow参数即是WinMain接收到的参数,他决定了一开始窗口的显示方式,SW_SHOWNORMAL代表正常显示,SW_SHOWMAIMIZED表示最大化显示,SW_SHOWMINIMIZED表示最小化显示。
UpdateWindow(hWnd);
该函数被用来重绘窗口区,它通过向窗口处理函数发送WM_PAINT消息来实现。经过以上步骤,我们就可以画出一个简单的窗口了。