// DEMO3_7.CPP - WM_CLOSE demo
// INCLUDES ///
#define WIN32_LEAN_AND_MEAN // just say no to MFC
#include <windows.h> // include all the windows headers
#include <windowsx.h> // include useful macros
#include <mmsystem.h> // very important and include WINMM.LIB too!
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// DEFINES
// defines for windows
#define WINDOW_CLASS_NAME "WINCLASS1"
// GLOBALS
HWND main_window_handle = NULL; // globally track main window
HINSTANCE hinstance_app = NULL; // globally track hinstance
// FUNCTIONS //
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
// this is the main message handler of the system
PAINTSTRUCT ps; // used in WM_PAINT
HDC hdc; // handle to a device context
// what is the message
switch(msg)
{
case WM_CREATE:
{
// do initialization stuff here
// return success
return(0);
} break;
case WM_CLOSE:
{
// kill message, so no further WM_DESTROY is sent
return(0); //这儿是关闭窗体时的消息,你可以做二件事,一是什么也不干,二是阻止关闭
} break;
case WM_PAINT:
{
// simply validate the window
hdc = BeginPaint(hwnd,&ps);
// you would do all your painting here
EndPaint(hwnd,&ps);
// return success
return(0);
} break;
case WM_DESTROY:
{
// kill the application, this sends a WM_QUIT message
PostQuitMessage(0);
// return success
return(0);
} break;
default:break;
} // end switch
// process any messages that we didn't take care of
return (DefWindowProc(hwnd, msg, wparam, lparam));
} // end WinProc
// WINMAIN
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASSEX winclass; // this will hold the class we create
HWND hwnd; // generic window handle
MSG msg; // generic message
// first fill in the window class stucture
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_DBLCLKS | CS_OWNDC |
CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;
winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// save hinstance in global
hinstance_app = hinstance;
// register the window class
if (!RegisterClassEx(&winclass))
return(0);
// create the window
if (!(hwnd = CreateWindowEx(NULL, // extended style
WINDOW_CLASS_NAME, // class
"WM_CLOSE Demo", // title
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0,0, // initial x,y
400,400, // initial width, height
NULL, // handle to parent
NULL, // handle to menu
hinstance,// instance of this application
NULL))) // extra creation parms
return(0);
// save main window handle
main_window_handle = hwnd;
// enter main event loop, but this time we use PeekMessage()
// instead of GetMessage() to retrieve messages
while(TRUE)
{
// test if there is a message in queue, if so get it
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
// test if this is a quit
if (msg.message == WM_QUIT)
break;
// translate any accelerator keys
TranslateMessage(&msg);
// send the message to the window proc
DispatchMessage(&msg);
} // end if
// main game processing goes here
// Game_Main(); // or whatever your loop is called
} // end while
// return to Windows like this
return(msg.wParam);
} // end WinMain
///
WM_PAINT 消息
WN_PAINT消息通知程序,全部或部分客户窗口需要重新绘制。当用户在最小化、重叠或调整客户窗口区域的时候,就会产生这条消息。重新绘制,你需要做两件事,首先是要用到WM_PAINT消息专用的一对函数,第一个是BeginPaint()函数,这是它的原形:
HDC BeginPaint( HWND hwnd, // handle to window LPPAINTSTRUCT lpPaint // pointer to structure for paint information ); |
在我告诉你返回值是什么之前,让我们先看看参数:
※ HWND hwnd:需要重绘的窗口的句柄。你应该已经对于这种参数比较熟悉了。
※ LPPAINTSTRUCT lpPaint:这是很重要的一个。是指向PAINTSTRUCT结构的指针,该结构包含所有的要被重绘区域的信息。
继续之前,我应该给你看看PAINTSTRUCT结构:
typedef struct tagPAINTSTRUCT { // ps HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT; |
结构内的成员如下:
※ HDC hdc
※ BOOL fErase:指明应用程序是否应该抹去背景。如果是FALSE,说明系统已经删除了背景。还记得在Windows类中我们曾经用黑色画刷定义了一个背景吗?这就意味着系统将用这个画刷抹去无效的区域。
※ RECT rcPaint:这是最重要的一个成员。它将告诉你需要被重绘的无效区域的矩形。我将稍后告诉你RECT结构。
※ BOOL fRestore,BOOL fIncUpdate,BYTE rgbReserved[32]:好消息,这些是保留成员,为老Windows服务的,所有你我都不必管它们。:)
现在我已经给你看了这么多,这就是BeginPaint()函数的全部。它做了三件事儿。首先,它使窗口再次有效,直到下一次被改变,WM_PAINT消息发出前,这个窗口都是有效的。第二,如果在窗口类(Windows class)里定义了背景画刷,就像我们做过的那样,就用这个画刷重绘无效的区域。(所谓无效,就是被改变的)第三,返回了被重绘区域的DC句柄。重绘的区域,是由RECT结构定义的:
typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT; |
我们已经指出这个结构描绘了一个矩形,但是有一件事情需要说说。RECT包含左上角,但不包含右下角。什么意思呢?让我们先定义一个RECT对象:
RECT myRect = {0, 0, 5, 5}; |
这个RECT包含象素(0,0),但是没有达到(5,5),所以矩形的右下角实际是(4,4)。看起来没有什么意义,但是你得习惯它。现在,还记得我所说的关于使用DC的事儿吗?一旦你用完了,你就必须释放它。用EndPaint()函数释放。回应WM_PAINT消息,每次调用完BeginPaint()函数,必须匹配一个EndPaint()函数释放DC。这是函数的原形:
BOOL EndPaint( HWND hWnd, // handle to window CONST PAINTSTRUCT *lpPaint // pointer to structure for paint data ); |
函数通过返回TRUE或FALSE分别表明成功还是失败。有两个简单的参数:
※ HWND hWnd:就是窗口的句柄。
※ CONST PAINSTRUCT *lpPaint:指向PAINTSTRUCT类型的结构变量地址。同BeginPaint()的第二个参数是一回事。不要被CONST迷惑了,它只是保证和确认函数没有改变结构的内容。你还可以通过调用ValidateRect()函数代替BeginPaint()函数使得窗口再次有效。但你得手工操作一切。可能我们真的什么时候就要用到它。所以给你它的原形:
BOOL ValidateRect( HWND hWnd, // handle of window CONST RECT *lpRect // address of validation rectangle coordinates ); |
通过返回TRUE或FALSE来确定函数调用成功还是失败。参数很简单:
※ HWND hWnd
※ CONST RECT *lpRect:是指向RECT结构是否有效的指针。如果你传递NULL,则整个客户区域都是有效的。
现在把以上讲到的做个样子给你看吧,假设我们已经定义了一个全局的变量hMainWindow作为我们的窗口句柄。
if (msg == WM_PAINT) { PAINTSTRUCT ps; // declare a PAINTSTRUCT for use with this message HDC hdc; // display device context for graphics calls hdc = BeginPaint(hMainWindow, &ps); // validate the window // your painting goes here! EndPaint(hMainWindow, &ps); // release the DC // tell Windows we took care of it return(0); } |
这段代码很简单。
有三个消息看起来差不多,都是处理关闭的事情的。它们是WM_DESTROY,WM_CLOSE,和WM_QUIT。它们的确很相似,但你需要知道它们之间的不同!一个窗口或者应用程序应该被关闭时发出WM_CLOSE消息,当接收到WM_CLOSE消息时,如果你愿意,向用户提出是否真的退出。你知道让用户作确认或有错误出现或有什么应该注意的事情发生的时候,往往弹出一个消息框。消息框的制作是很容易的,由于它用途广泛,我们还是介绍一下:
int MessageBox( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ); |
这些参数,尤其是最后一个,需要一些解释:
※ HWND hWnd:过一会儿我将向你介绍一个不含有它的函数,我保证。
※ LPCTSTR lpText:这是将要显示在消息框里的文本。你可以用n等调整一下格式。
※ LPCCTSTR lpCaption:这是显示在消息框标题栏里的文本。
※ UINT uType:这个参数可以联合使用几个不同的标记。这些标记可以根据你的目的选择,有好多MB_打头的标记供你选择,联合使用时要用“|”分隔开。下面列出了一些常用的:
按钮类
◎ MB_ABORTRETRYIGNORE:建立有“Abort”、“Retry”、“Ignore”按钮的消息框。
◎ MB_OK:建立有“OK”按钮的消息框。
◎ MB_OKCANCEL:建立有“OK”和“Cancel”按钮的消息框。
◎ MB_RETRYCANCEL:建立有“Retry”、和“Cancel”按钮的消息框。
◎ MB_YESNO:建立有“Yes”和“NO”按钮的消息框。
◎ MB_YESNOCANCEL:建立有“Yes”、“No”和“Cancel”按钮的消息框。
图标类
◎ MB_ICONEXCLAMATION:加个惊叹号图标。
◎ MB_ICONINFORMATION:加个消息图标。(好像是个问号)
◎ MB_ICONQUESTION:加个问号图标
◎ MB_ICONSTOP:加个停止图标。
默认按钮标志
◎ MB_DEFBUTTON1:设置第一个按钮为默认按钮。(默认按钮即消息框弹出后,直接敲回车就被按下的那个按钮)
◎ MB_DEFBUTTON2:第二个为默认按钮。
◎ MB_DEFBUTTON3:第三个为默认按钮。
◎ MB_DEFBUTTON4:第四个为默认按钮。
其它的标志
◎ MB_HELP:添加一个帮助按钮。通常按下该按钮或者敲F1键都将产生WM_HELP消息。
◎ MB_RIGHT:文本右对齐。
◎ MB_TOPMOST:设置消息框总在窗口的最上面。
如果消息框建立失败,返回值为0,否则是下列任一个值:
◎ IDABORT:“Abort”按钮被选择。
◎ IDCANCEL:“Cancel”按钮被选择。
◎ IDIGNORE:“Ignore”按钮被选择。
◎ IDNO:“No”按钮被选择。
◎ IDOK:“OK”按钮被选择。
◎ IDRETRY:“Retry”按钮被选择。
◎ IDYES:“Yes”按钮被选择。
总之,当收到WM_CLOSE消息,你可以做两件事儿。一件是你接受默认的处理返回一个值,你若这样做了,应用程序或窗口按照计划关闭;再者,你返回0,应用程序或窗口将保持原样。以下是代码的基本部分:
if (msg == WM_CLOSE) { if (MessageBox(hMainWindow, "Are you sure want to quit?", "Notice", MB_YESNO | MB_ICONEXCLAMATION) == IDNO) return(0); // otherwise, let the default handler take care of it } |
WM_DESTROY消息有点儿不同。它是窗口正在关闭时发出的。当得到WM_DESTROY消息的时候,窗口已经从视觉上被删除。一个主窗口被关闭,并不意味着应用程序结束了,它将在没有窗口的条件下继续运行。然而,当一个用户关闭了主窗口,也就意味着他要结束应用程序,所以如果你希望应用程序结束,在收到WM_DESTROY消息的时候,你必须发出一个WM_QUIT消息。你可以使用PostMessage()函数,但由于这是一个特殊的情况,就为它准备了一个特殊的函数:
VOID PostQuitMessage(int nExitCode); |
参数nExitCode是你的应用程序返回给Windows的一个退出代码(通常是0)。记住,WinMain()返回的是一个int(实数),不是void(空的)。nExitCode参数的值被赋值给wparam。WM_QUIT消息表示要关闭应用程序,所以得到这个消息后,你应跳出循环,把wparam返回给Windows。下面是一个简单的WinMain()函数实例:
int WinMain(HINSTANCE hinstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { // initialization stuff goes here // main loop - infinite! while (TRUE) { // check the message queue if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) // exit main loop on WM_QUIT break; TranslateMessage(&msg); DispatchMessage(&msg); } // main program logic goes here } // perform any shutdown functions here - releasing objects and such return(msg.wparam); // return exit code to Windows } |