2.2.8 第15练:处理WM_CLOSE消息
/*------------------------------------------------------------------------
015 编程达人win32 API每日一练
第15个例子WM_CLOSE.C:回调函数---处理WM_CLOSE消息
WM_CLOSE消息
DestroyWindow函数
注意:关闭窗口后,程序退出。
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/*************************程序入口 ******************************************
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin"); // 窗口类名
……(略)
return msg.wParam;//msg.wParam 来自一条表示退出的消息,返回这个值给系统
}
/************************回调函数——消息处理过程****************************/
//窗口回调函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC; //设备环境——绘图的地方
PAINTSTRUCT ps; //绘图结构体变量
RECT rect; //绘图区范围
switch (message)
{
case WM_CREATE: //创建窗口消息
return 0;
case WM_PAINT://绘图函数,在窗口上画画儿!
hDC = BeginPaint(hwnd, &ps);
//获得客户区大小
GetClientRect(hwnd, &rect);
//绘制字符串
TextOut(hDC, 200, 200, TEXT("爱达人!"), lstrlen(TEXT("爱达人!")));
//绘制椭圆图形
Ellipse(hDC, 250, 250, 1200, 500);
//绘制格式化文本,客户区中间垂直居中对齐
DrawText(hDC, TEXT("我的第一个窗口程序!"), -1, &rect,DT_CENTER |
DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &ps);
return 0;
case WM_CLOSE:
/*
1、一般是响应WM_CLOSE:调用DestroyWindow(),DestroyWindow()又发送WM_DESTROY消息;响应WM_DESTROY时,PostQuitMessage()函数发送WM_QUIT标记到消息队列,GetMessage()发现WM_QUIT时,退出程序
2、一个窗口或者应用程序应该被关闭时发出WM_CLOSE消息,当接收到WM_CLOSE消息时,
如果你愿意,可以通过MessageBox向用户提出是否真的要退出。
3、用户选择退出或不退出。*/
if (IDYES == MessageBox(hwnd,TEXT("是否真的要退
出!),TEXT("OK?"),MB_YESNO))
{
DestroyWindow(hwnd);
break;
}
else
break;//不退出,并且什么都没做。
case WM_DESTROY://处理退出消息
PostQuitMessage(0);//此消息直接进入消息队列的头部!
break;
default: //可以放在最后
return DefWindowProc(hwnd, message, wParam, lParam); //调用默认窗口过程以为应用程序未处理的任何窗口消息提供默认处理
}
return 0;
//return DefWindowProc(hwnd, message, wParam, lParam); //调用默认窗口过程以为应用程序未处理的任何窗口消息提供默认处理
}
/*************************************注意*************************************
WM_CLOSE消息:作为窗口或应用程序应终止的信号发送。
#define WM_CLOSE 0x0010
返回值类型:LRESULT
如果应用程序处理此消息,则应返回零。
备注
应用程序可以在销毁窗口之前提示用户进行确认,方法是处理WM_CLOSE消息并仅在用户确认选择后才调用DestroyWindow函数。
默认情况下,DefWindowProc函数调用DestroyWindow函数来销毁窗口。
*******************************************************************************
DestroyWindow函数:销毁指定的窗口。该函数将WM_DESTROY和WM_NCDESTROY消息发送到窗口以将其停用并从中移出键盘焦点。
该函数还破坏窗口的菜单,刷新线程消息队列,破坏计时器,删除剪贴板所有权,并中断剪贴板查看器链(如果窗口位于查看器链的顶部)。
如果指定的窗口是父窗口或所有者窗口,则DestroyWindow在销毁父窗口或所有者窗口时会自动销毁关联的子窗口或所有者窗口。该函数首先销毁子窗口或所有者窗口,然后销毁父窗口或所有者窗口。DestroyWindow还会破坏由CreateDialog函数创建的无模式对话框。
BOOL DestroyWindow(
HWND hWnd //销毁窗口的句柄
);
返回值类型:布尔
如果函数成功,则返回值为非零。
如果函数失败,则返回值为零。要获取扩展的错误信息,请调用GetLastError。
*/
点击“关闭”系统菜单后,弹出对话框窗口,如图2-6所示:
图2-6 WM_CLOSE消息
注意
读者可能会有疑问,当用户点击系统菜单“关闭”窗口时,是如何产生并获取WM_CLOSE消息的呢?答案是Windows操作系统获取“关闭”系统菜单消息后,再发送一个WM_CLOSE消息。这个问题,我们将在下一节消息机制中详细讲解。
2.2.9 第16练:处理WM_SIZE消息
/*------------------------------------------------------------------------
016 编程达人win32 API每日一练
第16个例子WM_SIZE.C:窗口过程---处理WM_SIZE消息
WM_SIZE消息---调整客户区大小
InvalidateRect函数
注意:关闭窗口后,程序退出。
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/*************************程序入口 ******************************************
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin"); // 窗口类名
……(略)
return msg.wParam;//msg.wParam 来自一条表示退出的消息,返回这个值给系统
}
/*************************回调函数——消息处理过程***************************/
//窗口过程回调函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC; //设备环境——绘图的地方
PAINTSTRUCT ps; //绘图结构体变量
RECT rect; //绘图区范围
static int cxClient,cyClient;//客户区的宽度和高度,定义为静态局部变量
switch (message)
{
case WM_CREATE: //创建窗口消息
//return 0;
break; //也可以
case WM_SIZE://更改窗口客户区大小的消息
GetClientRect(hwnd, &rect);//等价于lParam参数的作用
InvalidateRect(hwnd, &rect, TRUE);//TRUE重绘窗口客户区背景
cxClient = LOWORD(lParam);//客户区的新宽度
cyClient = HIWORD(lParam);//客户区的新高度
break;
case WM_PAINT://绘图函数,在窗口上画画儿!
hDC = BeginPaint(hwnd, &ps);
//获得客户区大小
GetClientRect(hwnd, &rect);
//绘制字符串---跟随客户区尺寸的变化而变化
//TextOut(hDC, 200, 200, TEXT("爱达人!"), lstrlen(TEXT("爱达人!
")));//对比测试
TextOut(hDC, cxClient / 5, cyClient / 5, TEXT("爱达人!"),
lstrlen(TEXT("爱达人!")));
//绘制椭圆图形---跟随客户区尺寸的变化而变化
//Ellipse(hDC, 250, 250, 1200, 500);//对比测试
Ellipse(hDC, cxClient / 4 , cyClient / 4 , cxClient / 4 * 3,
cyClient / 4 * 3);
//绘制格式化文本,客户区中间垂直居中对齐
DrawText(hDC, TEXT("我的第一个窗口程序!"), -1, &rect,DT_CENTER |
DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
if (IDYES == MessageBox(hwnd,TEXT("是否真的要退出!
"),TEXT("OK?"),MB_YESNO))
{
DestroyWindow(hwnd);
break;
}
else
break;//不退出,并且什么都没做。
case WM_DESTROY://处理退出消息
PostQuitMessage(0);//此消息直接进入消息队列的头部!
break;
default: //可以放在最后
return DefWindowProc(hwnd, message, wParam, lParam);//调用默认窗口过程
}
return 0;
//return DefWindowProc(hwnd, message, wParam, lParam); //调用默认窗口过程
}
/*************************************注意*************************************
WM_SIZE消息:当主窗口的客户区部分大小改变时,我们的应用程序将接收到 WM_SIZE 消息。
lParam 的高字部分是客户区的高,低字部分是客户区的宽。
wParam请求的调整大小类型。此参数可以是下列值之一。
SIZE_MAXHIDE:当某个其他窗口最大化时,消息将发送到所有弹出窗口。
SIZE_MAXIMIZED:窗口已最大化。
SIZE_MAXSHOW:当某些其他窗口恢复到原来的大小时,消息将发送到所有弹出窗口。
SIZE_MINIMIZED:窗口已最小化。
SIZE_RESTORED:窗口已调整大小,但SIZE_MINIMIZED或SIZE_MAXIMIZED值均不适用。
说明:
lParam和GetClientRect的功能一样,有时候WM_SIZE的效率要比使用GetClientRect高.
可以在程序中使用WM_SIZE来保存客户区的大小方便以后使用.
*******************************************************************************
InvalidateRect函数:向指定的窗体更新区域添加一个矩形,然后窗口客户区域的这一部分将被重新绘制。
BOOL InvalidateRect(
HWND hWnd, //新区域已更改的窗口的句柄。如果此参数为NULL,
//则系统将使所有窗口(不仅是此应用程序的窗口)无效并重新绘制
//所有窗口,并在函数返回之前发送WM_ERASEBKGND和WM_NCPAINT消
//息。不建议将此参数设置为NULL。
const RECT *lpRect,//指向RECT结构的指针,该结构包含要添加到更新区域的矩形的客
//户坐标。如果此参数为NULL,则整个工作区将添加到更新区域。
BOOL bErase //指定在处理更新区域时是否要擦除更新区域内的背景。
//如果此参数为TRUE,则在调用BeginPaint函数时将擦除背景。
//如果此参数为FALSE,则背景保持不变。
);
*/ 运行结果:
图2-7 WM_SIZEE消息
总结
1.读者是否还记得,当我们执行ShowWindow函数时会向消息队列发送一个WM_SIZE消息。WM_SIZE消息用于向窗口发送尺寸改变的消息。当窗口的大小发生变化时,系统会生成并发送WM_SIZE消息给窗口,使程序能够对窗口尺寸的改变做出响应。WM_SIZE消息的lParam 参数的高字部分是客户区的高,低字部分是客户区的宽。wParam参数为请求的调整大小类型。每个Windows消息都有附属的wParam参数和lParam 参数。不同消息的wParam参数和lParam 参数的含义是不同的。我们将在2.4节中详细讲解。
2.细心的读者会发现,本例中的窗口是可以通过鼠标拖动改变窗口的大小,同时窗口客户区绘制的图形和文本也随之而改变相应的位置。请注意处理WM_PAINT消息时的GDI绘图函数,绘制图形和文本的坐标位置是和窗口客户区等比例的。首先定义static变量cxClient,cyClient,接着在处理WM_SIZE消息时通过lParam参数获取窗口客户区的高和宽。当然也可以通过GetClientRect函数获取窗口客户区的宽和高,使用其中一个方法就可以了。
3.请读者对比测试一下,如果将绘制文本和椭圆的坐标和区域设置为固定值,显示效果如何。
2.2.10 第17练:处理WM_CHAR消息
/*------------------------------------------------------------------------
017 编程达人win32 API每日一练
第17个例子WM_CHAR.C:窗口过程---处理WM_CHAR消息
WM_CHAR消息---接收按键消息
_stprintf_s函数
注意:关闭窗口后,程序退出。
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
#include <tchar.h>
/*********************************回调函数***********************************
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/*************************程序入口 ******************************************
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin"); // 窗口类名
……(略)
return msg.wParam;//msg.wParam 来自一条表示退出的消息,返回这个值给系统
}
/************************回调函数——消息处理过程****************************/
//窗口过程回调函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC; //设备环境——绘图的地方
PAINTSTRUCT ps; //绘图结构体变量
RECT rect; //绘图区范围
static int cxClent,cyClient;//客户区的宽度和高度,定义为静态局部变量
switch (message)
{
case WM_CREATE: //创建窗口消息
//return 0;
break; //也可以
case WM_SIZE://更改窗口客户区大小的消息
//GetClientRect(hWnd, &rect);//等价于lParam参数的作用
//InvalidateRect(hWnd, &rect, TRUE);//TRUE重绘窗口客户区背景
cxClent = LOWORD(lParam);//客户区的新宽度
cyClient = HIWORD(lParam);//客户区的新高度
break;
case WM_CHAR:
TCHAR szChar[20];//定义字符缓冲区
//_stprintf_s(szChar, 20, TEXT("您按下了%c"), wParam);
swprintf_s(szChar, 20, TEXT("您按下了%c"), wParam);
MessageBox(hwnd, szChar, TEXT("爱达人!"), 0);
break;
case WM_PAINT://绘图函数,在窗口上画画儿!
hDC = BeginPaint(hwnd, &ps);
//获得客户区大小
GetClientRect(hwnd, &rect);
//绘制字符串---跟随客户区尺寸的变化而变化
//TextOut(hDC, 200, 200, TEXT("爱达人!"), lstrlen(TEXT("爱达人!
")));//对比测试
TextOut(hDC, cxClent / 5, cyClient / 5, TEXT("爱达人!"),
lstrlen(TEXT("爱达人!")));
//绘制椭圆图形---跟随客户区尺寸的变化而变化
//Ellipse(hDC, 250, 250, 1200, 500);//对比测试
Ellipse(hDC, cxClent / 4 , cyClient / 4 , cxClent / 4 * 3, cyClient
/ 4 * 3);
//绘制格式化文本,客户区中间垂直居中对齐
DrawText(hDC, TEXT("我的第一个窗口程序!"), -1, &rect,DT_CENTER |
DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
if (IDYES == MessageBox(hwnd,TEXT("是否真的要退出?"),TEXT("爱达人
"),MB_YESNO))
{
DestroyWindow(hwnd);
break;
}
else
break;//不退出,并且什么都没做。
case WM_DESTROY://处理退出消息
PostQuitMessage(0);
break;
default: //可以放在最后
return DefWindowProc(hwnd, message, wParam, lParam);//调用默认窗口过程
}
return 0;
//return DefWindowProc(hwnd, message, wParam, lParam); //调用默认窗口过程
}
/************************************注意**************************************
WM_CHAR消息:当TranslateMessage函数翻译WM_KEYDOWN消息时,将其发送到具有键盘焦点的窗口中。该WM_CHAR消息包含被按下的键的字符代码。
#define WM_CHAR 0x0102
wParam:虚拟键代码经过翻译后的字符代码
lParam:描述击键消息的32位值,包括6个字段:重复计数、扫描代码、扩展键标志、上下文代码、前一个键状态标志和转换状态标志
备注
该WM_CHAR消息使用Unicode转换格式(UTF)-16。
*******************************************************************************
_stprintf_s函数:将若干个argument按照format格式存到buffer字符串缓冲区中。sprintf_s对于格式化string中的格式化的字符的有效性进行了检查,
sprintf_s也携带着接收格式化字符串的缓冲区的大小。右键查看函数的定义
int sprintf_s(
char *buffer, //字符串缓冲区
size_t sizeOfBuffer, //字符串缓冲区的大小
const char *format, //格式化字符串
... //可选参数列表
);
*/
运行结果:
图2-8 WM_CHAR消息
总结
1.此例中,在处理WM_CHAR消息时,取WM_CHAR消息的wParam参数(字符),通过MessageBox对话框输出。那么WM_CHAR消息又是从哪里来的呢?
当用户按下键盘按键(字符键),会产生一个WM_KEYDOWN消息,并送入窗口消息队列,由主程序的消息循环GetMessage函数获取,然后经过TranslateMessage函数将WM_KEYDOWN消息中的虚拟键码转换为字符,并生成一个WM_CHAR消息送入消息队列。GetMessage函数再次从消息队列中取出WM_CHAR消息后,通过DispatchMessage函数分发给Windows系统,最后调用窗口过程处理WM_CHAR消息。
2.【注意】本例中使用C标准库swprintf_s函数将wParam参数中的字符复制到缓冲区。swprintf_s函数需要添加头文件<tchar.h>。这里也可以使用_stprintf_s函数。二者的区别是:
swprintf_s适用于 Unicode 字符处理,而 _stprintf_s是一个通用的格式化函数,可以根据编译时的字符集选项来处理不同类型的字符。你可以根据自己的需要来选择使用适合的函数。
3.本例WM_SIZE消息中调用了InvalidateRect函数,用于将指定的窗口或窗口的一部分标记为无效,并发送一个绘图消息给窗口,以便在下一次更新时重新绘制该区域。
InvalidateRect(hWnd, &rect, TRUE);//TRUE重绘窗口客户区背景。参数&rect为指定的无效矩形区域指针,TRUE表示重绘时清除背景。
2.2.11 第18练:处理WM_LBUTTONDOWN消息
/*------------------------------------------------------------------------
018 编程达人win32 API每日一练
第18个例子WM_LBUTTONDOWN.C:窗口过程---处理WM_LBUTTONDOWN消息
WM_LBUTTONDOWN消息--接收按下鼠标左键消息
注意:关闭窗口后,程序退出。
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
#include<tchar.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/*************************程序入口 ******************************************
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin"); // 窗口类名
……(略)
return msg.wParam;//msg.wParam 来自一条表示退出的消息,返回这个值给系统
}
//*************************回调函数——消息处理过程*************************
//窗口过程回调函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC; //设备环境——绘图的地方
PAINTSTRUCT ps; //绘图结构体变量
RECT rect; //绘图区范围
static int cxClent,cyClient;//客户区的宽度和高度,定义为静态局部变量
switch (message)
{
case WM_CREATE: //创建窗口消息
//return 0;
break; //也可以
case WM_SIZE://更改窗口客户区大小的消息
//GetClientRect(hWnd, &rect);//等价于lParam参数的作用
//InvalidateRect(hWnd, &rect, TRUE);//TRUE重绘窗口客户区背景
cxClent = LOWORD(lParam);//客户区的新宽度
cyClient = HIWORD(lParam);//客户区的新高度
break;
//操作系统捕获键盘按键消息送入消息队列,然后GetMessage消息循环从消息队列中取出
//TranslateMessage(&msg); //将虚拟键消息转换为字符消息
//DispatchMessage(&msg); //发送消息函数,先把msg发送给操作系统,然后由操作系统再调用Wndproc函数!
case WM_CHAR:
TCHAR szChar[20];
_stprintf_s(szChar, 20, TEXT("您按下了%c"), wParam);
MessageBox(hwnd, szChar, TEXT("爱达人!"), 0);
break;
//操作系统捕获鼠标按键消息送入消息队列,然后GetMessage消息循环从消息队列中取出
//TranslateMessage(&msg); //将虚拟键消息转换为字符消息
//DispatchMessage(&msg); //发送消息函数,先把msg发送给操作系统,然后由操作系统再调用Wndproc函数!
case WM_LBUTTONDOWN:
//可以调用自定义功能函数
MessageBox(hwnd, TEXT("单击左键!"), TEXT("爱达人!"), 0);
break;
case WM_PAINT://绘图函数,在窗口上画画儿!
hDC = BeginPaint(hwnd, &ps);
//获得客户区大小
GetClientRect(hwnd, &rect);
//绘制字符串---跟随客户区尺寸的变化而变化
//TextOut(hDC, 200, 200, TEXT("爱达人!"), lstrlen(TEXT("爱达人!
")));//对比测试
TextOut(hDC, cxClent / 5, cyClient / 5, TEXT("爱达人!"),
lstrlen(TEXT("爱达人!")));
//绘制椭圆图形---跟随客户区尺寸的变化而变化
//Ellipse(hDC, 250, 250, 1200, 500);//对比测试
Ellipse(hDC, cxClent / 4 , cyClient / 4 , cxClent / 4 * 3, cyClient
/ 4 * 3);
//绘制格式化文本,客户区中间垂直居中对齐
DrawText(hDC, TEXT("我的第一个窗口程序!"), -1, &rect,DT_CENTER |
DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
if (IDYES == MessageBox(hwnd,TEXT("是否真的要退出?"),TEXT("爱达人
"),MB_YESNO))
{
DestroyWindow(hwnd);
break;
}
else
break;//不退出,并且什么都没做。
case WM_DESTROY://处理退出消息
PostQuitMessage(0);//此消息直接进入消息队列的头部!
break;
default: //可以放在最后
return DefWindowProc(hwnd, message, wParam, lParam);//调用默认窗口过程
}
return 0;
//return DefWindowProc(hwnd, message, wParam, lParam); //调用默认窗口过程
}
/************************************注意**************************************
WM_LBUTTONDOWN消息:当光标在窗口的客户区域中时用户按下鼠标左键时发布。如果未捕获鼠标,则消息将发布到光标下方的窗口。
否则,该消息将发布到捕获鼠标的窗口中。窗口通过其WindowProc函数接收此消息。
C ++
#define WM_LBUTTONDOWN 0x0201
参量
wParam指示各种虚拟键是否按下。此参数可以是以下一个或多个值。
MK_CONTROL 0x0008 CTRL键按下。
MK_LBUTTON 0x0001 鼠标左键按下。
MK_MBUTTON 0x0010 鼠标中键按下。
MK_RBUTTON 0x0002 鼠标右键按下。
MK_SHIFT 0x0004 SHIFT键按下。
MK_XBUTTON1 0x0020 第一个X按钮按下。
MK_XBUTTON2 0x0040 第二个X按钮按下。
lParam
低位字指定光标的x坐标。坐标相对于客户区域的左上角。
高阶字指定光标的y坐标。坐标相对于客户区域的左上角。
*/
运行结果:
图2-9 WM_LBUTTONDOWN消息
总结
1.当用户按下鼠标左键时,Windows操作系统捕获鼠标左键消息并送入窗口消息队列,由主程序的消息循环GetMessage函数获取,再由DispatchMessage函数分发给Windows系统,调用窗口过程处理WM_LBUTTONDOWN消息。
2.wParam和lParam参数:wParam的前缀w是WORD的意思,表示一个16位无符号短整型,这是Win16位系统遗留,在32位系统中该类型改为UINT类型,但名称继续沿用。lParam的前缀 L是LONG长整型,32位长整数。
不同消息的附属wParam参数和lParam参数具有不同的含义。
消息来源 | wParam(高16位) | wParam(低16位) | lParam |
键盘消息 | ASCII码或虚拟键码 | 按键相关状态信息 | |
鼠标消息 | 鼠标事件的附加信息 | 按键的相关状态信息 | 储存鼠标的坐标 |
菜单消息 | 0 | 低字为菜单ID | 0 |
快捷键消息 | 1 | 快捷键ID | 0 |
控件消息 | 控件通知码 | 控件ID | 控件句柄 |
表2-1 wParam参数和lParam参数