//
一个简单的Win32应用程序
//
通过这个简单的实例讲解Windows消息是如何传递的
#include
"
stdafx.h
"
#include
<
windows.h
>
//
声明窗口过程函数
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
//
定义一个全局变量,作为窗口类名
TCHAR szClassName[]
=
TEXT(
"
SimpleWin32
"
);
//
应用程序主函数
int
WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR szCmdLine,
int
iCmdShow)
{
//
窗口类
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
=
szClassName;
//
类名称
//
注册窗口类
if
(
!
RegisterClass (
&
wndclass))
{
MessageBox (NULL, TEXT (
"
RegisterClass Fail!
"
),
szClassName, MB_ICONERROR);
return
0
;
}
//
建立窗口
HWND hwnd;
hwnd
=
CreateWindow(szClassName,
//
窗口类名称
TEXT (
"
The Simple Win32 Application
"
),
//
窗口标题
WS_OVERLAPPEDWINDOW,
//
窗口风格,即通常我们使用的windows窗口样式
CW_USEDEFAULT,
//
指定窗口的初始水平位置,即屏幕坐标系的窗口的左上角的X坐标
CW_USEDEFAULT,
//
指定窗口的初始垂直位置,即屏幕坐标系的窗口的左上角的Y坐标
CW_USEDEFAULT,
//
窗口的宽度
CW_USEDEFAULT,
//
窗口的高度
NULL,
//
父窗口句柄
NULL,
//
窗口菜单句柄
hInstance,
//
实例句柄
NULL);
ShowWindow(hwnd,iCmdShow);
//
显示窗口
UpdateWindow(hwnd);
//
立即显示窗口
//
消息循环
MSG msg;
/*
GetMessage()从消息队列中取消息,对取出的消息进行转换(TranslateMessage),
对于能够将虚拟键码转化成字符码的消息,会在消息队列里放一条WM_CHAR消息,
最后将消息发送到相应的消息处理函数进行处理。
循环执行这个处理过程,直到收到WM_QUIT消息,才退出循环,结束程序。
*/
while
(GetMessage(
&
msg,
0
,
0
,
0
))
//
从消息队列中取消息
{
TranslateMessage (
&
msg);
//
转换消息
DispatchMessage (
&
msg);
//
派发消息
}
return
msg.wParam;
/*
---------------GetMessage---------------------------------
GetMessage
函数原型:
BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
参数:
lpMsg:一个指向MSG结构的指针,该结构用于存放从消息队列里取出的消息。
hWnd:窗口句柄。如果该参数是非零值,则GetMessage只检索该窗口(也包括其子窗口)消息,如果为零,则GetMessage检索整个进程内的消息。
wMsgFilterMin:指定被检索的最小消息值,也就是消息范围的下界限参数。
wMsgFilterMax:上界限参数。如果wMsgFilterMin和wMsgFilterMax都为零,则不进行消息过滤,GetMessage检索所有有效的消息。
返回值:
GetMessage检索到WM_QUIT消息,返回值是零;其它情况,返回非零值。
函数功能:
这个API函数用来从消息队列中“摘取”一个消息,放到lpMsg所指的变量里。(注:如果所取窗口的消息队列中没有消息,则程序会暂停在GetMessage(…) 函数里,不会返回。)
再通俗一点讲解GetMessage函数:
当程序执行GetMessage()的时候,会检查消息队列,如果有消息在消息队列里,它取出该消息,将该消息填充到lpMsg所指的MSG结构,并返回TRUE值。如果此时消息队列里没有消息(消息队列为空),它会将线程阻塞,也就是将控制权交给系统,直到消息队列中有内容时,才唤醒线程继续执行。
对于GetMessage()函数,还有一点需要说明,就是当从消息队列中取出的消息是WM_QUIT时,函数返回值是0。我们一般利用这一点退出消息循环,结束程序。
---------------------------------------------------------------
*/
/*
------------------TranslateMessage---------------------
TranslateMessage
函数原型:
BOOL TranslateMessage(CONST MSG*lpMsg);
参数:
IpMsg:指向MSG结构的指针,该结构是函数GetMessage或PeekMessage从消息队列里取得的消息。
函数功能:
该函数将虚拟键消息转换为字符消息。字符消息被寄送到调用线程的消息队列里,当下一次线程调用函数GetMessage或PeekMessage时被读出。
什么是虚拟键码呢?Windows为了方便输入管理,减少程序对设备的依赖性,将键盘上所有的按键都用一个两位十六进制数对应,这些数称为虚拟键码。虚拟键码一般以VK_开头,如:Esc键对应的虚拟键码是VK_ESCAPE;空格键对应的虚拟键码是VK_SPACE;VK_LWIN与左边的Windows徽标键相对应。
当一个按键被按下时,会触发WM_KEYDOWN消息, WM_KEYDOWN消息的wParam参数值就是虚拟键值。通过这个值就可以判断哪个键被按下了。
为什么我们要把虚拟键码转换为字符码呢?
比如我们按下了‘A’键,此时我们得到的字符可能是‘A’,也可能是小写的‘a’,这由当时的大写状态(Caps Lock)以及是否同时按下了Shift键有关。TranslateMessage()函数的作用就是不用我们考虑这些问题,而是根据这些情况,自动返回一个ASCII码值,以方便用户使用。
并不是所有的虚拟键码值都会Translate成字符码。字母、数字键都有字符码相对应,而像方向箭头键、F1—F12功能键这些按键就没有字符码相对应。当虚拟键码需要转化成字符码时,TranslateMessage()函数就在消息队列里放一条WM_CHAR消息,WM_CHAR消息的wParam参数值就是转换后的ASCII码值。
-------------------------------------------
*/
/*
--------------------DispatchMessage-------------------
DispatchMessage
函数原型:
LONG DispatchMessage(CONST MSG *lpmsg);
函数功能:
它的作用很简单,就是分派消息到窗口的消息处理函数去执行。
-------------------------------------------
*/
/*
-----------------PeekMessage-------------
PeekMessage
函数原型:
BOOL PeekMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);
参数:
lpMsg、hWnd、wMsgFilterMin、wMsgFilterMax这四个参数的意义和GetMessage对应参数的意义相同,在此不再赘述。
wRemoveMsg:这个参数决定读消息时是否删除消息,可选值有PM_NOREMOVE和PM_REMOVE。如果您选PM_NOREMOVE,执行该函数后消息仍然留在消息队列(我称为读消息);如果您选PM_REMOVE,执行该函数后将在消息队列中移除该消息(同GetMessage())。
返回值:
消息队列中有消息,返回值为TRUE;消息队列中没有消息,返回值为FALSE。
函数功能:
PeekMessage()也是从消息队列中取消息,但它是GetMessage()不同,主要在以下两点:
(一)、GetMessage()只能从消息队列中取走消息,也就是说,GetMessage()执行后,该消息将从消息队列中移除。
PeekMessage()可以从消息队列中取走消息。也可以读消息,让消息继续留在消息队列里。
(二)、当消息队列中没有消息时,GetMessage()将会阻塞线程,等待消息;而PeekMessage()与GetMessage()不同,它执行后会立刻返回,消息队列中有消息时,返回值为TRUE;消息队列中没有消息时,返回值为FALSE。
------------------------------------
*/
/*
-------------------PeekMessage版的消息循环---------------------------
//消息循环
MSG msg;
while(true)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) //从消息队列中取消息
{
if(msg.message == WM_QUIT)
break;
TranslateMessage (&msg); //转换消息
DispatchMessage (&msg); //派发消息
}
else
WaitMessage();
} //End of while(true)
*/
/*
------------------WaitMessage--------------------------
WaitMessage
函数原型:
BOOL WaitMessage(VOID);
函数功能:
这个函数的作用是当消息队列中没有消息时,将控制权交给其它线程。该函数将会使线程挂起,直到消息队列中又有新消息。
这个函数专门和PeekMessage配合使用,当消息队列中没有消息时,挂起线程,等待消息队列中新消息的到来,这样可以减轻CPU的运算负担。
--------------------------------------------------
*/
}
//
消息处理函数
//
参数:窗口句柄,消息,消息参数,消息参数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//
处理感兴趣的消息
switch
(message)
{
case
WM_DESTROY:
//
当用户关闭窗口,窗口销毁,程序需结束,发退出消息,以退出消息循环
PostQuitMessage(
0
);
return
0
;
}
//
其他消息交给由系统提供的缺省处理函数
return
::DefWindowProc (hwnd, message, wParam, lParam);
}