第一章—— 窗口与消息
一、HelloWin程序如下:
/*------------------------------------------------------------
HELLOWIN.C -- Displays "Hello, Windows 98!" in client area
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, 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.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires 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) ;
}
二、Windows与消息机制
1、进队与不进队的消息
Windows是一种面向对象的体系结构,Windows环境和应用程序都是通过消息来交互的。Windows应用程序开始执行后,Windows为该程序创建一个"消息队列(message queue)",用以存放邮寄给该程序可能创建的各种不同窗口的消息。消息队列中消息的结构(MSG)为:
typedef struct tagMSG{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;
其中第一个成员变量是用来标识接收消息的窗口句柄;第二个参数便是消息标识号,如WM_PAINT;第三个和第四个参数的具体意义同message值有关,均为消息参数。前四个参数是非常重要和经常用到的,至于后两个参数则分别表示邮寄消息的时间和光标位置(屏幕坐标)。
把消息传送到应用程序有两种方法:一种是由系统将消息"邮寄(post)"到应用程序的"消息队列"。这是"进队消息",Win32 API有对应的函数: PostMessage(),此函数不等待该消息处理完就返回;而另一种则是由系统在直接调用窗口函数时将消息"发送(send)"给应用程序的特定窗口的函数,属于"不进队消息"。对应的函数是SendMessage(),其必须等待该消息处理完后方可返回。
进队的消息基本上是用户输入的结果,如击键(WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标击键(WM_LBUTTONDOWN)形式给出。进队消息还包含时钟消息(WM_TIMER)、刷新消息(WM_PAINT)和退出消息(WM_QUIT)等等。
不进队的消息往往来自调用特定的WINDOWS函数。例如WinMain调用CreateWindows函数时,Windows将创建窗体并在处理中给窗口过程发送一个WM_CREATE消息。当WinMain调用ShowWindow时,Windows将给窗口过程发送WM_SIZE和WM_SHOWWINDOW消息。当WinMain调用UpdateWindow时Windows将给窗口过程发送WM_PAINT。
Windows调用窗口过程,意味着Windows本身也有一个消息循环,它调用GetMessage从消息队列中取出消息,并且用DispatchMessage将消息发送给窗口过程。除了具有操作系统本身的消息循环外,对于每一个正在执行的Windows应用程序,系统为其建立一个"消息队列",即应用程序队列,用来存放该程序可能创建的各种窗口的消息。应用程序中含有一段称作"消息循环"的代码,用来从消息队列中检索这些消息并把它们分发到相应的窗口函数中。
消息循环代码是应用程序中主函数winmain ( )中类似如下的程序段:
while(GetMessage(&&msg,NULL,NULL,NULL))
{ file://从消息队列中取得消息
TranslateMessage(&&msg);
file://检索并生成字符消息WM_CHAR
DispatchMessage(&&msg);
file://将消息发送给相应的窗口函数
}
由此可见,所谓"消息循环",实际是程序循环。
2、窗口过程的可重入
虽然Windows程序可以多线程执行,但是每个线程的消息只为窗口过程在该线程中执行的窗口处理消息。也就是说,消息循环和窗口过程不是并发执行的。当消息循环从消息队列中接受到一个消息时,然后调用DispatchMessage消息发送给窗口过程时,直到窗口过程将控制返回给Windows,DispatchMessage才能返回。
窗口过程能调用给Windows窗口过程发送另一个消息的函数,这时窗口过程必须在函数调用返回之前完成对第二个消息的处理。如窗口过程调用UpdateWindow产生WM_PAINT消息时,窗口过程必须先处理完WM_PAINT消息后,UpdateWindow调用才能恢复控制权给窗口过程。
三、详细说明:
1、windows.h
这个头文件包含了其他的windows头文件:
WINDEF.H——基本类型定义
WINNT.H——支持Unicode的类型定义
WINBASE.H——内核函数
WINUSER.H——用户接口函数
WINGDI.H——图形设备借口函数
2、WinMain函数的说明:
WinMain函数是windows程序的入口点,声明如下:
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE hPreInstance,
LPSTR lpCmdLine,
int nShowCmd
);
WINAPI——许多Windows的函数调用声明为WINAPI
实际情况如下:
#define WINAPI _stdcall
而另一种是CALLBACK类型
四个形参声明如下:
第一个参数:一个实例句柄,句柄是一个32位数,代表一个对象。
第二个参数:16位windows中程序通过检查hPreInstance参数就能够确定自身的其他实例是否正在运行。但是在32位windows中被废弃,全部为NULL
第三个参数:运行程序的命令行。
第四个参数:指出程序最初显示的方式:最大化最小化等等。
3、关于匈牙利命名
句柄声明如下:
类型 说明
HANDLE 通用句柄类型
HWND 标识一个窗口对象
HDC 标识一个设备对象
HMENU 标识一个菜单对象
HICON 标识一个图标对象
HCURSOR 标识一个光标对象
HBRUSH 标识一个刷子对象
HPEN 标识一个笔对象
HFONT 标识一个字体对象
HINSTANCE 标识一个应用程序模块的一个实例
HLOCAL 标识一个局部内存对象
HGLOBAL 标识一个全局内存对象
下面是Windows中常使用的一些字母前缀和它们所代表的数据类型:
类型 说明
b BOOL,布尔类型
by BYTE类型
c char类型
dw DWORD类型
fn 函数类型
i 整型
l LONG类型
lp 远(长)指针(long pointer)
n 短整型
np 近(短)指针(near pointer)
p 指针
s 字符串
sz 以'\0'结尾的字符串
w WORD类型
x short,用于表示X坐标时
y short,用于表示Y坐标时
pstr Char *
UINT Unsigned int 32位
以下是几个前缀:
前缀 类别
CS 类风格选项
CW 创建窗口选项
DT 绘制文本选项
IDI 图表ID号
IDC 光标ID号
MB 消息框选项
SND 声音选项
WM 窗口消息
WS 窗口风格
4、关于几个重要的结构:
MSG——消息结构
WNDCLASS——窗口类结构
PAINTSTRUCT——绘图结构
RECT——矩形结构
本章说明MSG与WNDCLASS结构如下:
MSG结构包含了一条Windows消息的完整信息,
在WINUSER.H中定义如下:
typedef struct tagMSG {
HWND hwnd; //接受消息的窗口句柄
UINT message; //主消息值,具体消息内容
WPARAM wParam; //副消息值,其具体含义依赖于主消息值
LPARAM lParam; //副消息值,其具体含义依赖于主消息值
DWORD time; //消息放入消息队列中的时间
POINT pt; //消息放入消息队列中鼠标的位置
} MSG;
顺便对POINT如下声明
typedefstructtagPOINT {
int x; /* X坐标 */
int y;/* Y坐标 */
} POINT;
WNDCLASS在WINUSER.H中定义如下:
typedefstructtagWNDCLASS{
DWORD style; /* 窗口风格 */
WNDPROC*lpfnWndProc; /* 窗口函数 */
intcbClsExtra; /* 类变量占用的存储空间 */
intcbWndExtra; /* 实例变量占用的存储空间 */
HINSTANCEhinstance;/* 定义该类的应用程序实例的句柄
HICONhicon;/* 图标对象的句柄 */
HCURSORhCursor;/* 光标对象的句柄 */
HBRUSHhbrBackground;/* 用于擦除用户区的刷子对象的句柄 */
LPCSTRlpszMenuName;/* 标识菜单对象的字符串 */
LPCSTRlpszClassName;/* 标识窗口类的名字的字符串*/}
WNDCLASS;
5、RegisterClass函数
该函数只有一个参数,,通过调用后返回0表示错误。
6、创建窗口过程中的几个函数
CreateWindows
HWND CreateWindow(
LPCSTR lpClassName, 类名,指定该窗口所属的窗体类。
LPCSTR lpWindowName, 窗口的名字,即在标题栏中显示的文本。
DWORD dwStyle, 该窗口的风格,在后面详细介绍。
int x, 窗口左上角相对于屏幕左上角的初始X坐标。
int y, 窗口左上角相对于屏幕左上角的初始Y坐标。
int nWidth, 窗口的宽度。
int nHeight, 窗口的高度。
HWND hWndParent, 一个子窗口的父窗口的句柄,或隶属窗口的拥有者窗口的句柄,若没有拥有或者父窗口,则为NULL。
HMENU hMenu, 选单句柄,如果为NULL,则使用类中定义的选单。如果建立的是一个子窗口,该参数是一个子窗口标识符,使用此标识符来区分多个窗口。
HINSTANCE hInstance, 创建窗口对象的应用程序的实例句柄。
VOID FAR * lpParam 创建窗口时指定的额外参数,一般设为NULL);
窗口风格类型说明 :
WS_BORDER 创建一个有边框的窗口
WS_CAPTION 创建一个有标题栏的窗口
WS_CHILDWINDOW(or WS_CHILD)
创建一个子窗口(不能与WS_POPUP一起使用)
WS_CLIPCHILDREN 当在父窗口内绘制时,把子窗口占据的区域剪切在外,即不在该区域内绘图
WS_CLIPSIBLINGS 裁剪相互有关系的子窗口,不在被其它子窗口覆盖的区域内绘图,仅与WS_CHILD一起使用
WS_DISABLED 创建一个初始被禁止的窗口
WS_DLGFRAME 创建一个有双边框但无标题的窗口
WS_HSCROLL 创建一个带水平滚动杠的窗口
WS_VSCROLL 创建一个带垂直滚动杠的窗口
WS_ICONIC 创建一个初始为图标的窗口,仅可以与WS_OVERLAPPEDWINDOWS一起使用
WS_MAXIMIZE 创建一个最大尺寸的窗口
WS_MINIMIZE 创建一个最小尺寸的窗口(即图标)
WS_MAXIMIZEBOX 创建一个带有极大框的窗口
WS_MINIMIZEBOX 创建一个带有极小框的窗口
WS_OVERLAPPED 创建一个重叠式窗口,重叠式窗口带有标题和边框
WS_POPUP 创建一个弹出式窗口,不能与WS_CHILD一起使用
WS_SYSMENU 窗口带有系统选单框,仅用于带标题栏的窗口
WS_THICKFRAME 创建一个边框的窗口,使用户可以直接缩放窗口
WS_VISIBLE 创建一个初始可见的窗口
ShowWindow函数
声明如下:
ShowWindows(hwnd,iCmdShow)
iCmdShow是WinMain接受并传递的,其中包含着窗体的现实方式
如:SW_SHOWNORMAL或SW_SHOWMAXIMIZED
或SW_SHOWMINOACTIVE(显示在任务栏上)
当第二个参数是SW_SHOWNORMAL时,就调用UpdateWindow(hwmd)来重画客户区,通过给窗体过程发出WM_PAINT消息来做到这一点。
7、关于消息循环的函数
BOOL GetMessage(
LPMSG lpMsg, 指向MSG类型的变量的远指针,它包含有从应用程序消息队列中检索到的一条消息的数据。
HWND hWnd, 指定为哪个窗口检索消息,如果hWnd为NULL,则检索调用该函数的应用程序的所有的消息(不检索属于其它应用程序的消息)。
UINT wMin,
UINT wMax 以下两个基本参数指定检索在wMin和wMax范围内的消息。如果这两个参数都为零,该函数检索所有的可用的消息。
);
返回值 在检索出WM_QUIT消息时,返回零值,在其它情况下返回非零值。
一个例子如下
GetMessage(&msg,NULL,0,0)
Windows就是从消息队列中取出消息来填充msg中的各个域。
如下:
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ; //将msg结构传给Windows进行一些键盘转换
DispatchMessage (&msg) ; //将msg结构回传给windows
}
在窗体过程中注意使用
DefWindowsPro(hwnd,iMsg,wParam,lParam)
来处理一些默认的消息
另外注意在窗体销毁的时候(WM_DESTORY后)使用一条
PostQuitMessage(0)命令来结束。