首先上代码,下面讨论都根据此展开.
#include "stdafx.h"
#include <Windows.h>
#include<mmsystem.h> //窗口创建时播放声音所要包含的头文件
#pragma comment(lib,"winmm.lib")
LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
TCHAR appszName[]=TEXT("demo");
WNDCLASS wndcls;
wndcls.lpszClassName=appszName;
wndcls.cbClsExtra=NULL;
wndcls.cbWndExtra=NULL;
wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor=LoadCursor(hInstance,IDC_ARROW);
wndcls.hIcon=LoadIcon(hInstance,IDI_APPLICATION);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WindowProc;
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClass(&wndcls))
{
MessageBox(NULL,_T("注册失败"),TEXT("提示"),NULL);
}
HWND hwnd=CreateWindow(appszName,_T("demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,nCmdShow); //nCmdShow确定最初的显示方式
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg,NULL,NULL,NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
RECT rect;
GetClientRect(hwnd,&rect);
HDC hdc;
PAINTSTRUCT ps;
switch (uMsg)
{
case WM_CREATE:
PlaySound(_T("hellowin.wav"),NULL,SND_FILENAME|SND_ASYNC);
return 0;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
DrawText(hdc,_T("demo"),-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
3.1窗口创建
窗口是在“窗口类”的基础上建立的,“窗口类”标识了窗口的消息处理函数。(这里我们都已经知道多个窗口对象属于同一个窗口类,并使用同一个窗口消息处理函数)。
就如面向对象程序设计,我们这里可以认为窗口是一种对象,函数则是窗口消息过程。数据则是窗口过程保存的信息和windows为每个窗口和窗口类别保存的信息。Windows通过调用窗口过程处理给窗口发送的消息,窗口过程处理完成后将控制再传给Windows。
上述代码中用到的绝大部分函数都声明于WINUSER.H中。如LoadIcon,CreateWindow等。
大写字母标志符定义的规则:

新的数据类型
UINT unsigned int(无正负号整数)
PSTR 指向字符串指针 即char*
WPARAM LPARAM 这两个参数前面的L和W来源于16位Window时期,那时WndProc的第三个参数定义为WORD(16位无正负号短整数),第四个参数定义为LONG(32位有正负号长整型)。 在32位系统中WPARAM定义为UINT,LPARAM定义为LONG,都是32位值,但仍保留了16位系统中的定义方法。
LRESULT 定义为LONG
WINAPI CALLBACK 这是窗口过程和WinMain函数定义的形态,这两个标识符都被定义为_stdcall. 关于stdcall,我们只要知道它是函数调用约定的一种、__stdcall表示
1.参数从右向左压入堆栈
2.函数被调用者修改堆栈
3.函数名(在编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
在win32应用程序里,宏APIENTRY,WINAPI,都表示_stdcall,非常常见。
MSG 消息结构
WNDCLASS 窗口类别结构
PAINSTRUCT 绘图结构
RECT 矩形结构
HINSTANE 执行实例句柄
HWND 窗口句柄
HDC 设备内容句柄
windows中许多变量都采用“匈牙利表示法”命名规则,即:变量名以一个或多个小写字母开始。这些字母表示变量的数据型态。如, szCmdLine sz代表「以0结尾的字符串」。
hInstance hPrevInstance h表示「句柄」;在iCmdShow i前缀表示「整数」。各种通用前缀如下表所示:
上面代码中窗口类的各参数分开定义,通过这种方式定义是非常方便的,虽然每个窗口都以同样的方式工作,但并非所有窗口都一样。他们有不同大小,位置等,这样我们就可以通过不同的特征值定义出来。
消息循环、
typedef struct tagMS
{
HWND hwnd ;
UINT message ;
WPARAM wParam
LPARAM lParam
DWORD time ;
POINT pt ;
}
MSG, * PMSG ;
hwnd 接收消息的窗口句柄。
message 消息标识符。这是一个数值,每个消息均有一个对应的标识符,这些标识符定义于Windows 头文件(大多在WINUSER.H中),以前缀WM开头。
wParam 一个32位的其含义和数值根据消息的不同而不同。
lParam 一个32位的消息参数,其值与消息有关。
time 消息放入消息队列中的时间。
pt 消息放入消息队列时的鼠标坐标。
在消息循环中,只要从消息队列中取出消息的message字段不为WM_QUIT(其值为0x0012), GetMessage就传回一个非零值。 WM_QUIT消息将导致GetMessage传回0。
窗口过程定义的形式为
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
一个窗口过程总是与RegisterClass注册的特定窗口类别相关联。通过上面我们可以注意到前四个参数是与消息结构中相同。
程序一般不调用窗口过程,而一般由windows调用。但通过SendMessage函数,程序能直接调用自己的窗口过程。
消息处理,上述我们处理了三个消息:
WM_CREATE
窗口过程接收处理的第一个消息。当WinMain中处理CreateWindow时接收。调用WndProc时,第一个参数设定为窗口句柄,第二个参数设定为WM_CREATE。 WndProc处理WM_CREATE消息并将控制传回给Windows。然后WinMain中进行下一步的处理。
通常,窗口过程在WM_CREATE处理期间进行一次窗口初始化。本例中播放声音。最后, WndProc通过从窗口消息处理程序中传回0,结束了整个WM_CREATE的处理。
WM_SIZE (何时发送和应用点)
1.当WinMain函数调用CreateWindow时,窗口过程收到WM_CREATE消息,而第一条WM_SIZE消息就发生在那之后(准确的说是WinMain调用ShowWindow函数时)【应用汇总:①可以在窗口过程的WM_SIZE处理中获取字体的高度和宽度; ②可以获取窗口(客户区)的宽度和高度(59页和81页有WM_SIZE相应lParam变量的解释); ③可以对滚动条进行诸如SetScrollInfo的初始化工作】。
2.窗口尺寸改变时. 【应用汇总:①在WndProc处理WM_SIZE消息的时候设定滚动条的范围相对于在WM_CREATE中更合适(客户区大小改变后,滚动条页需要做出相应调整),例如SetScrollInfo的使用; ②缩放编辑控件的动作也应该放在此处,除以上原因以外,还因为此动作需要获取客户区大小
WM_PAINT
我们都知道,当显示区域无效需要更新画面时就发送个消息。那什么情况下才会发送呢?
1、窗口建立时,这时候肯定整个显示区域是无效的。发送第一条WM_PAINT消息(通常发生于UpdateWindow)
2、改变窗口大小时。如风格CS_HREDRAW等,在窗口大小变化后整个显示内容无效,这时候也就需要重绘了。
3、最小化窗口恢复时。因为窗口显示区域涉及的数据量很大,所以一般windows不会保存显示区域的内容。这时窗口过程收到此消息,自动恢复窗口内容。
4、移动窗口导致多个窗口重叠时。window不会保存窗口中其他窗口遮盖的内容。当被遮盖内容重新露面时就被标志为无效,同样发送此消息重绘。
WM_PAINT的处理总是从一个BeginPaint开始:
hdc=BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
调用BeginPaint,如果显示区域背景还未被删除,则由Windows来删除。如果消息处理程序不处理WM_PAINT(很少见),必须传送给DefWindowProc,DefWindowProc也只是依次调用BeginPaint和EndPaint使显示区域有效。
WM_DESTROY
该消息指示Windows正在根据使用者指示关闭窗口。该程序通过调用PostQuitMessage以标准方式响应WM_DESTROY消息:
PostQuitMessage(0);
该函数在程序的消息队列中插入一个WM_QUIT消息。前面提到过, GetMessage对于除了WM_QUIT之外的从消息队列中取出的所有消息都传回非0值。而当GetMessage得到一个WM_QUIT消息时,它传回0。这将导致WinMain退出消息循环,并终止程序。然后程序执行下面的叙述:
return msg.wParam ;
结构的wParam字段是传递给PostQuitMessage函数的值(通常是0)。然后return叙述将退出WinMain并终止程序。
Windows程序设计难点
从上面我们可以得知,基本一个程序的所有动作都在窗口消息处理函数中发生。而windows做的一切,也只是响应发送给窗口消息处理函数的消息而已。这是我们在写程序之前必须要明白的一件事情。
所有WndProc调用都以消息的形式进行。大多数Windows程序的主要部分都用来处理消息。 Windows可以发送给窗口消息处理程序的消息通常都以WM开头,并且都在WINUSER.H头文件中定义。
一个窗口消息处理函数可以处理同一个类别建立的多个窗口消息。hwnd标志具体哪个窗口。参数message是WM_SIZE。消息WM_SIZE的参数wParam的值是SIZE_RESTORED、 SIZE_MINIMIZED、 SIZE_MAXIMIZED、 SIZE_MAXSHOW或SIZE_MAXHIDE (在WINUSER.H表头文件中分别定义为数字0到4)。即参数wParam表明窗口是非最小化还是非最大化,是最小化、最大化,还是隐藏。
lParam参数包含了新窗口的大小,新宽度和新高度均为16位值,合在一起成为32位的lParam。
队列化与非非队列化消息
队列化消息是由Windows放入程序消息队列中的。
非队列化的消息在Windows调用窗口时直接送给窗口过程。
也就是说队列化消息直接发送给消息队列,而非发送给窗口消息处理函数。
队列化消息基本上是使用者输入的结果,以击键(如WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮(WM_LBUTTONDOWN)的形式给出。队列化消息还包含时钟消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT)。
非队列化消息则是其它消息。在许多情况下,非队列化消息来自呼叫特定的Windows函数。例如,当WinMain呼叫CreateWindow时, Windows将建立窗口并在处理中给窗口消息处理程序发送一个WM_CREATE消息。当WinMain呼叫ShowWindow时, Windows将给窗口消息处理程序发送WM_SIZE和WM_SHOWWINDOW消息。当WinMain呼叫UpdateWindow时, Windows将给窗口消息处理程序发送WM_PAINT消息。键盘或鼠标输入时发出的队列化消息信号,也能在非队列化消息中出现。例如,用键盘或鼠标选择了一个菜单项时,键盘或鼠标消息就是队列化的,而说明菜单项已选中的WM_COMMAND 消息则可能就是非队列化的。