前文复习
1、思考一下,窗口程序中为什么要注册窗口类?
你想创建什么样的窗口呢?那么,创建什么样的窗口呢?创建前,Window系统可不知道你要的是什么类型的窗口啊(比如标题栏上显示什么图标,鼠标形状是什么,窗口背景颜色等等)。这些类型信息应在你创建前事先告诉Window系统。可以采用这种方法:就是我们事先写一份要创建窗口的类型申请表,提交(注册)给Window系统。然后在创建时,可以让Windows按这个申请表来创建你所要的窗口了。也就是说我们还应该先提交一个申请表,申请成功后再根据这个表创建一个窗口。
我们在使用microsoft平台SDK或者MFC编程时,在创建窗口类后都要先用RegisterClass函数来注册窗口类,这个函数需要一个指向窗口类结构的指针。那么RegisterClass这个windows API函数到底做了什么呢,关于这个函数的源码微软是不会给出来的,因为它只是提供一个系统编程接口,网上也找不到相关说明,只是粗略介绍需要将类注册给系统,但从msdn的atom table说明中我们发现这样一段说明。The system uses atom tables that are not directly accessible to applications. However, the application uses these atoms when calling a variety of functions. For example, registered clipboard formats are stored in an internal atom table used by the system. An application adds atoms to this atom table using。答案有了,在我们构造一个窗口类结构后,我们需要将这个类结构指针加入到system atom table 即SAT中,这样系统就可以通过查找这张表来找到用户自定义的窗口类,window预定义的窗口类指针也在SAT中。SAT实际上实现了一种用于查询的映射,atom实际类型是short,即16位数据。只有系统才可直接访问这张表,但在调用某些api函数时,如Registerclass,可以告知系统来存取这张表。当然,还有本地原子表和全局原子表,这些表应用程序是可以直接访问的。
2、窗口句柄和实例句柄的区别和联系
偶理解实例是一个进程,窗口是一个进程的图形界面
实例句抦用来标识一个程序的一个具体的进程,他的值实际上是这个实例被加载到进程空间的地址。
句柄和ID有什么区别?
什么是“句柄”(handle),handle的本意是把柄,把手的意思。是你与操作系统打交道的东东。举个通俗的例子,比如你考上了大学,入学后,学校(操作系统)会给你一个学生证号。注意,这个号码是学校指定的,你无法自选。有了这个号码(学生证,假设一证多用)享受学校提供的服务:如你就可以去图书馆借书,去食堂吃饭,去教室上课等等。但你不能到食堂里买啤酒,因为学校不允许这种服务。而在计算机中系统提供的服务就是API调用,你有了HANDLE,就可以理直气壮地向系统提出调用API的服务。而指针的权力就大多了,有了指针你可以到处去喝酒,打架,学校(操作系统)管不着,所以句柄和指针的区别在于句柄指针调用系统提供的服务。而句柄虽然是一个能相互区别的号码,但与我们普通的ID号又有区别,普通的ID号是可以由程序员自己定义的,而句柄不行,它是对象生成是系统指定的,是为了区别系统中存在的各个对象,这个句柄不是由程序员符给的。句柄
1。句柄,是整个windows编程的基础,一个句柄是指使用的一个唯一的整数值,是指一个四字节长的数值,用于标志应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息。
2。句柄不是一个指针,程序不能利用它句柄来直接阅读文件中的信息。如果句柄不用在I/O文件中,它是毫无用处的。
3。句柄是windows用来标志应用程序中建立的或是使用的唯一整数,windows使用了大量的句柄来来标志很多对象。
说明:实例——在windows环境下,不但可以运行多个应用程序,还可以运行多个应用程序的多份拷贝,每个拷贝叫做一个实例,并且有不同的实例句柄。一个实例句柄是windows可以单独运行的副本,是唯一可以标志此实例的整数。
窗口消息处理
Windows窗口就是通过消息来与操作系统互动,窗口不能直接与使用者交流,使用者移动鼠标、敲键盘都是通过操作系统与窗口通信的,因此WinMain()函数中的消息循环很重要,没有它窗口便不能工作,而窗口消息处理函数更为重要,也是我们编程的主要对象。
一、消息循环
消息循环共用到3个函数,GetMessage()的返回值一直不为0,直到接受WM_QUIT时才为0(也就是false)。DispatchMessage()是将消息传给窗口消息处理函数。对初学者来说,这3个函数写法比较固定,但不要认为只能这样写。对消息的正确理解是比较关键的,学习一下消息的结构体。
typedef struct {
HWND hWnd;//接收消息的窗口的句柄
UINT message; //消息(应用程序只能用低字节,高字节留给系统用)
WPARAM wParam; //消息的第一个附加消息
LPARAM lParam; //消息的第二个附加消息
DWORD time;
POINT pt;
}; MSG, *PMSG;
注意:上面消息循环中的三个函数用的消息是这个结构体的类型,而下面要阐述的消息处理函数的参数,用的是结构体中的第2-4元素即message、wParam、lParam。
二、消息处理函数
Windows中消息的种类大致有2种,即系统消息和应用程序消息,消息的前缀也多种多样。不过日常用到的消息大都以“WM_”开头,而且许多消息的wParam和lParam都为0(也就是不使用),一般只有“WM_COMMAND”菜单命令或键盘鼠标的消息才使用wParam和lParam。
Windows中消息虽然很多,但并不是每一消息都要编程处理,我们一般把不需要特别处理的消息交给缺省消息处理函数DefWindowProc(),例子中的default部分便是。另外,有一函数必须处理,也就是关闭窗口消息WM_DESTROY,这时必须调用PostQuitMessage(0)来向操作系统发送一条终止程序的消息。
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL InitApp(HINSTANCE, LPCSTR);
BOOL InitInstance(HINSTANCE, LPCSTR, int);
//主入口函数
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
LPSTR lpsCmdLine, int nCmdShow)
{
MSG msg;
char szClassName[] ="DrawRect"; //窗口名
//注册窗口类
if (!InitApp(hCurInst, szClassName))
return FALSE;
//初始化窗口
if (!InitInstance(hCurInst, szClassName, nCmdShow)) {
return FALSE;
}
//消息循环
while (GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg); //消息解释
DispatchMessage(&msg); //消息发送
}
return (int)msg.wParam;
}
用的最多的消息可能是WM_PAINT,当窗口改变大小、或有别的窗口从这个窗口面前经过,这个窗口中的文字或图案被冲掉,这就必须重新描画。因此,窗口中要显示的文字或图像一般都是在WM_PAINT中实现的。下面我们以在窗口上显示一行字作例子,不过,这时要稍微知道一点GDI(Graphic Device Interface)的知识。
三、GDI的基础知识
要在窗口描画文字或图像,首先要先取得设备文本(Device Contexts)。取设备文本一般有2种方法,BeginPaint()函数和GetDC()函数。描画结束后必须调用EndPaint()函数和ReleaseDC来释放设备文本。后面2章将继续介绍GDI的基础知识。
要在窗口的某个位置描画文字,必须取窗口的有效区域(ClientRect),因为窗口的边框、菜单、工具条、状态条等部分是不能利用的。例如,要在窗口的正中间写一个字,用窗口宽度和高度的一半来取正中位置是不对的,除非这个窗口无任何边框、菜单、工具条、状态条等。正确的方法是先定义一个区域变量(类型为RECT),然后用GetClientRect()函数取出窗口有效区域放入这个变量中。最后,调用DrawText()函数在矩形区输出文字。DrawText()将在下一章细说,所谓区域类型RECT其实就是一个存放矩形块座标的结构体,下面是RECT的原型。
int DrawText(
HDC hDC, //设备文本的句柄
LPCTSTR lpString, //要输出的字符串
int nCount, //字符串长度
LPRECT lpRect, //要输出的区域
UINT uFormat //输出的格式
);
typedef struct _RECT {
LONG left; //左边位置
LONG top; //上边位置
LONG right; //右边位置
LONG bottom; //下边位置
} RECT, *PRECT;
重绘消息处理
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
HDC hdc;//设备文本句柄
PAINTSTRUCT ps;//窗口客户区结构体
TCHAR szHello[] = "My first GDI is so easy and cool!";//显示的文字
switch(message) {
case WM_PAINT://重绘消息
hdc = BeginPaint(hWnd, &ps);//开始描画
RECT rc;//定义客户区变量
GetClientRect(hWnd, &rc);//获取窗口有效区域
//显示文字(横向对中|不自动回行|纵向对中)
DrawText(hdc, szHello, (int)strlen(szHello),&rc,
DT_CENTER|DT_SINGLELINE|DT_VCENTER);
EndPaint(hWnd, &ps);//结束描画
break;
显示文字窗口完整程序
#include <windows.h>
TCHAR szWindowClass[] = "My first window's name";
TCHAR szTitle[] = "My first window";
ATOM MyRegisterClass(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) {
MSG msg;
MyRegisterClass(hInstance);
if ( !InitInstance(hInstance, nShowCmd) ) {
return FALSE;
}
while ( GetMessage(&msg, NULL, 0, 0) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(hInstance, IDC_SIZEALL);
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = szWindowClass;
wc.hIconSm = LoadIcon(wc.hInstance, IDI_APPLICATION);
return RegisterClassEx(&wc);
}
BOOL InitInstance(HINSTANCE hInstance, int ncmdshow) {
HWND hWnd;
hWnd = CreateWindow(szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
400,
120,
NULL,
NULL,
hInstance,
NULL);
if (!hWnd) {
return FALSE;
}
ShowWindow(hWnd, ncmdshow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
TCHAR szHello[] = "My first GDI is so easy and cool!";
switch(message) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
RECT rc;
GetClientRect(hWnd, &rc);
DrawText(hdc, szHello, (int)strlen(szHello),&rc,
DT_CENTER|DT_SINGLELINE|DT_VCENTER);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}