第三章: 窗口与消息
创建窗口:调用函数 CreateWindow
"Windows向应用程序发送了一条消息" 意思就是说:“Windows调用了该程序内部的一个函数”,这个函数就是我们自己写的,此函数的参数描述了由Windows所发送并由我们的程序所接收的特定消息。这个函数被称为“窗口过程”;
应用程序所创建的每一个窗口都有一个与之相关联的窗口过程,这个窗口过程可以是应用程序的某一个函数,也可以位于一个动态链接库中。
Windows通过调用“窗口过程”来向窗口传递消息,“窗口过程”依据这些消息来做出相应的处理,然后将控制权返还给Windows;
窗口总是依据“窗口类”来创建,窗口类标识了用于处理传递给窗口的消息的窗口过程。窗口类的使用允许多个窗口共享同一窗口类,因而多个窗口可以使用相同的窗口过程。
当Windows程序开始执行时,Windows首先为该程序创建一个“消息队列”(message queue)。该消息队列中存放着应用程序可能创建的所有窗口的消息。Windows应用程序中一般都包含一小段称为"消息循环"(message loop)的代码,该代码用于从消息队列中检索消息,并将其分发给相应的窗口过程。
创建一个窗口,首先需要注册一个窗口类,且需要为窗口类提供一个“窗口过程函数”(即处理消息的函数);
HELLOWIN程序:
HELLOWIN.cpp
#include
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{
//MessageBox(NULL,TEXT("Hello,Windows 98!"),TEXT("HelloMsg"), MB_OKCANCEL);
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.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName; //窗口类名称,
if (!RegisterClass(&wndclass)) // 注册窗口类
{
MessageBox(NULL,TEXT("This program requires Windows NT"),szAppName,MB_ICONERROR);
return 0;
}
//CreateWindow就是通过第一个参数 szAppName (窗口类名称)与上面注册的窗口类关联起来的
// CreateWindow会创建窗体,并在创建过程中向窗口过程发送一条WM_CREATE消息
hwnd = CreateWindow(szAppName, // 窗口类名称
TEXT("The Hello Program!"), // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口风格,或称窗口格式
CW_USEDEFAULT, // 初始x坐标
CW_USEDEFAULT, // 初始y坐标
CW_USEDEFAULT, // 初始x方向尺寸
CW_USEDEFAULT, // 初始y方向尺寸
NULL, // 父窗口句柄
NULL, // 菜单窗口句柄
hInstance, // 程序实例句柄
NULL); // 创建参数
ShowWindow(hwnd,iCmdShow); // 会向窗口过程发送一个WM_SIZE消息和WM_SHOWWINDOW消息
UpdateWindow(hwnd); // 会向窗口过程发送一个WM_PAINT消息
while (GetMessage(&msg,NULL,0,0)) // 从消息队列中取消息,如果message为WM_QUIT则返回0
{
TranslateMessage(&msg); //翻译(或者说转换)一些键盘消息
DispatchMessage(&msg); // 把消息推送给窗口过程函数(就是上面给wndclass.lpfnWndProc赋值的WndProc函数)
}
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: // 下面的函数需要添加 winmm.lib 库,添加方法见(图二)
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);//将一个WM_QUIT消息插入到程序的消息队列中
return 0;
default:
break;
}
return DefWindowProc(hwnd,MESSAGE,wParam,lParam);
}
运行结果:(图一)
添加winmm.lib:(图二)
Windows函数调用:
1:LoadIcon 加载图标,以供程序使用。
2:LoadCursor 加载鼠标光标,以供程序使用。
3:GetStockObject 获取一个图形对象。在本例中是一个用来对窗口的背景进行重绘的画刷。
4:RegisterClass 为应用程序的窗口注册一个窗口类。
5:MessageBox 显示信息框
6:CreateWindow 基于窗口类创建一个窗口。
7:ShowWindow 在屏幕中显示窗口
8:UpdateWindow 指示窗口对其自身进行重绘。
9:GetMessage 从消息队列中获取消息
10:TranslateMessage 翻译一些键盘消息。
11:DispatchMessage 将消息发送给窗口过程。
12:PlaySound 播放声音文件。
13:BeginPaint 标明窗口绘制开始。
14:GetClientRect 获取窗口客户区的尺寸。
15:DrawText 显示一个文本字符串。
16:EndPaint 结束窗口绘制。
17:PostQuitMessage 将“退出”消息插入消息队列。
18:DefWindowProc 执行默认的消息处理。
以上函数大部分在winuser.h中
大写标识前缀
前缀 | 常量 |
CS | 类风格选项 |
CW | 创建窗口选项 |
DT | 文本绘制选项 |
IDI | 图标的ID号 |
IDC | 光标的ID号 |
MB | 信息框选项 |
SND | 声音选项 |
WM | 窗口消息 |
WS | 窗口风格 |
4种结构:
结构 | 含义 |
MSG | 消息结构 |
WNDCLASS | 窗口类结构 |
PAINTSTRUCT | 绘制结构 |
RECT | 矩形结构 |
句柄:
标识符 | 含义 |
HINSTANCE | 实例句柄------程序本身 |
HWND | 窗口句柄 |
HDC | 设备环境句柄 |
句柄本质上是引用某个对象的数值(通常为32位)。Windows中的句柄非常类似于传统的C或MS-DOS程序中
使用的文件句柄。应用程序通过调用Windows函数开获取句柄,应用程序通过在其他Windows函数中使用句柄
来引用相应对象。
匈牙利标记法:(本书使用的变量名前缀)
前缀 | 数据类型 |
c | char 或 WCHAR 或 TCHAR |
by | BYTE(无符号字符) |
n | short(短整型) |
i | int(整型) |
x,y | int ,表示x坐标 或 y坐标 |
cx,cy | int ,表示x或y的长度,c表示“count”(计数) |
B或f | BOOL(int) ;f表示“flag” |
w | WORD(无符号短整型) |
l | LONG(长整型) |
dw | DWORD(无符号长整型) |
fn | 函数 |
s | 字符串 |
sz | 以0结束的字符串 |
h | 句柄 |
p | 指针 |
WNDCLASS结构体:
typedef struct tagWNDCLASSA {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA;
typedef struct tagWNDCLASSW {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
} WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;
#ifdef UNICODE
typedef WNDCLASSW WNDCLASS;
typedef PWNDCLASSW PWNDCLASS;
typedef NPWNDCLASSW NPWNDCLASS;
typedef LPWNDCLASSW LPWNDCLASS;
#else
typedef WNDCLASSA WNDCLASS;
typedef PWNDCLASSA PWNDCLASS;
typedef NPWNDCLASSA NPWNDCLASS;
typedef LPWNDCLASSA LPWNDCLASS;
#endif // UNICODE
在WNDCLASS结构体中,两个最重要的字段是第二个字段合最后一个字段。第二个字段(lpfnWndProc)是用于基于该窗口类的
所有窗口的窗口过程的地址。最后一个字段是窗口类的名称,允许用户任意命名。
如果新建窗口为顶级窗口(例如应用程序窗口),注释为“父窗口句柄”的参数就应设为NULL,通常。当两个窗口之间存在父子
关系时,子窗口总是位于父窗口的前方。应用程序窗口总是位于桌面窗口的前方,但不必为了调用CreateWindow函数而设法获取桌面窗口的句柄。
ShowWindow(hwnd,iCmdShow):
iCmdShow:SW_SHOWNORMAL 正常显示
SW_SHOWMAXIMIZED 窗口最大化显示
SW_SHOWMINIMIZED 窗口最大化显示
SW_SHOWMINNOACTIVE 窗口只显示在任务栏
UpdateWindow(hwnd);
是通过向窗口过程发送一条WM_PAINT消息而完成的;
消息循环:
//msg的message字段不等于WM_QUIT(其值为0x0012),则GetMessage返回一个非0值,否则返回0,出错返回-1
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
typedef struct tagMSG {
HWND hwnd; // 消息所指向的窗口的句柄
UINT message; // 消息标识符,在WINUSER.h 中都为其定义了一个以WM为前缀的标识符
WPARAM wParam; // 消息参数,具体含义取决于具体的消息
LPARAM lParam; // 消息参数,具体含义取决于具体的消息
DWORD time; // 消息进入消息队列的时间
POINT pt; // 消息进入消息队列中时鼠标指针的位置坐标
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;