VC 6中新建一个Win32 Application,在Source Files中新建一个C++源文件WinMain.cpp,代码如下所示:
#include "stdafx.h"
HWND hWnd=NULL; // 保存 Windows 分配给程序的窗口句柄,它是全局的
LRESULT APIENTRY MyWndProc(HWND,UINT,WPARAM,LPARAM);
void MyMsgLoop();
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
char clsName[]="myWnd";
WNDCLASS wc;
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = MyWndProc;
wc.cbClsExtra = 0L;
wc.cbWndExtra = 0L;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = clsName;
RegisterClass(&wc);
hWnd = CreateWindow(clsName,"主窗口",
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
100, 100,400, 300,NULL,NULL,hInstance,NULL); // 创建窗口
ShowWindow( hWnd, SW_SHOWDEFAULT ); // 显示窗口
UpdateWindow( hWnd ); // 刷新窗口
MyMsgLoop(); // 进入消息循环
return 0;
}
LRESULT APIENTRY MyWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam ) {// 消息处理
switch(message) {
case WM_CREATE: // 当收到建立窗口的消息时,进行处理...
//MessageBox(NULL,"窗口创建的时候","消息提示",MB_OK|MB_ICONEXCLAMATION);
return 0;
break;
case WM_CLOSE: // 当收到关闭窗口的消息时,进行处理...
MessageBox(NULL,"窗口要关闭了","消息提示",MB_OK|MB_ICONEXCLAMATION);
PostQuitMessage(0);
return 0;
break;
case WM_SIZE: // 当窗口尺寸变化时,进行处理...
MessageBox(NULL,"窗口尺寸发生变化了","消息提示",MB_OK|MB_ICONEXCLAMATION);
return 0;
break;
case WM_DESTROY: // 当退出消息的时候,进行处理...
MessageBox(NULL,"退出消息了","消息提示",MB_OK|MB_ICONEXCLAMATION);
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP: // 当收到按ESC键的消息时,(如果当前是全屏模式,必须要加入退出方式)
switch (wParam) {
case VK_ESCAPE:
MessageBox(NULL,"按ESC键了","消息提示",MB_OK|MB_ICONEXCLAMATION);
PostQuitMessage(0);
return 0;
break;
}
default:
break;
}
return (DefWindowProc(hWnd, message, wParam, lParam));
}
void MyMsgLoop() {
MSG msg;
BOOL receiveMessage;
PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);
while(msg.message != WM_QUIT) { // 消息循环
receiveMessage = PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE);
if(receiveMessage) { // 有消息
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
通过这个简单的例子,理解Windows基于消息映射的机制。
先从主函数WinMain开始看。
因为要创建一个Windows窗体,所以首先需要注册一个Windows类,因为调用CreateWindow函数的时候,需要根据注册的Windows类的信息来创建一个窗体的。
Windows定义了一个WNDCLASS结构,用来描述一个Windows窗体类的信息,定义如下所示:
typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS;
关于WNDCLASS结构的各个成员变量的含义,可以查看MSDN了解。在上面的程序中,直接设置WNDCLASS结构变量wc的成员:
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; // 窗体风格
wc.lpfnWndProc = MyWndProc; // 窗体进程的指针,上面程序中定义了MyWndProc这个进程
wc.cbClsExtra = 0L;
wc.cbWndExtra = 0L;
wc.hInstance = hInstance; // 窗体进程实例的句柄,通过WinMain主函数传递进来
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW); // 背景画刷,也就是窗口显示背景颜色
wc.lpszMenuName = NULL;
wc.lpszClassName = clsName; // 注册的Windows类名称
接下来,调用RegisterClass函数进行注册:
RegisterClass(&wc);
该API函数声明如下:
ATOM RegisterClass(
CONST WNDCLASS *lpWndClass // 指向WINDCLASS类实例的指针
);
如果注册成功,返回ATOM类型,也就是WORD类型,也就是无符号短整型,如下所示:
typedef unsigned short WORD;
注册完成之后,具备了创建一个窗体的信息,开始调用API函数CreateWindow来进行窗体的创建工作,上述程序中创建如下所示:
hWnd = CreateWindow(clsName,"主窗口",
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
100, 100,400, 300,NULL,NULL,hInstance,NULL); // 创建窗口
窗体创建成功以后,会返回这个窗体的一个句柄,赋给定义的全局的HWND hWnd。关于CreateWindow函数,声明如下:
HWND CreateWindow(
LPCTSTR lpClassName, // 注册的类的指针
LPCTSTR lpWindowName, // 窗体名称字符串指针
DWORD dwStyle, // 窗体风格
int x, // 窗体显示位置的水平x坐标
int y, // 窗体显示位置的垂直y坐标
int nWidth, // 窗体宽度
int nHeight, // 窗体高度
HWND hWndParent, // 父窗口的句柄
HMENU hMenu, // 菜单句柄
HANDLE hInstance, // 应用程序实例的句柄
LPVOID lpParam // 窗体创建信息的指针
);
获取到了创建成功之后的窗体的句柄,然后根据这个返回的句柄来显示这个成功创建的窗体,程序中调用API函数ShowWindow进行显示:
ShowWindow( hWnd, SW_SHOWDEFAULT ); // 显示窗体
其中,ShowWindow函数的声明如下:
BOOL ShowWindow(
HWND hWnd, // 窗体句柄
int nCmdShow // 窗体的状态
);
其中,窗体的状态nCmdShow取值可以是如下所示的值:
SW_FORCEMINIMIZE
SW_HIDE Hides
SW_MAXIMIZE
SW_MINIMIZE
SW_RESTORE
SW_SHOW
SW_SHOWDEFAULT
SW_SHOWMAXIMIZED
SW_SHOWMINIMIZED
SW_SHOWMINNOACTIVE
SW_SHOWNA
SW_SHOWNOACTIVATE
SW_SHOWNORMAL
这样,一个窗体就显示出来了,窗体的客户区内是空的。
UpdateWindow函数用于更新窗口,该函数更新窗口的时候会向指定的窗体的进程发送消息,如果被更新的客户区为空,说明窗体没有接收到消息。
最后,调用了自定义了MyMsgLoop函数,进入消息循环。这时,窗体进入一种等待状态,等待消息的来到,然后在该函数中对不同类型的消息进行处理。
我们看一下MyWndProc函数的实现,它是窗口进程,用来根据窗体接收到的消息并进行处理。窗口进程函数的声明如下所示:
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
可以自定义自己的窗口进程函数,包含上述四个参数。然后根据消息进行选择处理方式。
上述程序中,给出了几种典型的消息,你可以根据需要在case语句中实现处理的过程,处理完成之后,返回DefWindowProc(hWnd, message, wParam, lParam),这时是一个默认的窗口进程函数,可以处理任何Windows消息。
函数MyMsgLoop主要对消息进行管理的,其中,调用PeekMessage函数会检查线程消息队列,根据指定的消息的结构并从消息队列中取出消息,而且,可以设定消息在被PeekMessage函数处理完成以后,是否保留在线程消息队列中,从PeekMessage函数的声明来看:
BOOL PeekMessage(
LPMSG lpMsg, // pointer to structure for message
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax, // last message
UINT wRemoveMsg // removal flags
);
最后一个参数wRemoveMsg表示是否在调用PeekMessage函数处理之后从线程消息队列中移除,它有两个取值:
PM_NOREMOVE
PM_REMOVE
对该程序进行编译,运行,显示出一个窗体,如图所示:
根据在MyWndProc函数中定义的窗口进程,可以通过向窗体传递不同类型的消息,来看case语句中对应的输出,有助于理解Windows的消息机制。