#include <windows.h>
//函数返回值类型("LONG") 函数类型(回调函数"_stdcall") 函数名 (函数参数);
LRESULT CALLBACK WndProc (HWND, UINT,WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPreInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName [] =TEXT ("HelloWin");
HWND hwnd;
MSG msg;
WNDCLASS wndclass; //WNDCLASS结构定义在WINUSER.H中,包含10个字段
//类风格:当窗口尺寸改变(水平/垂直)时,所有基于该窗口类的窗口都将被重绘。
wndclass.style =CS_HREDRAW | CS_VREDRAW;
//处理所有基于该窗口类创建的窗口的所有消息
wndclass.lpfnWndProc= WndProc;
//类预留空间,字节数为0
wndclass.cbClsExtra= 0;
//窗口预留空间,字节数为0
wndclass.cbWndExtra= 0;
//应用程序的实例句柄,问:每个窗口类的这个字段都是“应用程序的实例句柄”?
wndclass.hInstance= hInstance;
//使用预定义图标:NULL, 图标标识符
//加载自定义图标:程序实例句柄hInstance, 图标标识
wndclass.hIcon =LoadIcon (NULL, IDI_APPLICATION);
//当鼠标指针出现在这类窗口的客户区时,将变成一个小箭头
wndclass.hCursor= LoadCursor (NULL, IDC_ARROW);
//返回一个白色画刷句柄,画刷句柄:用于区域填充的像素着色模式,
//Windows有几个标准画刷("库存"画刷)
wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH);
//无菜单:NULL
wndclass.lpszMenuName= NULL;
//窗口类的名称。貌似:Windows实现了窗口类的模板,而这里定义一个窗口类,以及//接下来的注册窗口类,都是使用Windows的窗口类模板确实创建一个类
wndclass.lpszClassName = szAppName;
//注册窗口类,分配相应的内存(分配内存时总要做错误检查)
if(!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0 ;
}
//当HELLOWIN调用CreateWindow时,Windows完成其必须的操作,
//并调用WndProc(发送WM_CREATE消息)
hwnd = CreateWindow ( szAppName, // 窗口类名称:基于该窗口类创建一个窗口
TEXT ("The Hello Program"), // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口风格:普通的层叠(overlapped)窗口
CW_USEDEFAULT, // 初始x坐标:窗口左上角相对于屏幕左上角的初始位置
CW_USEDEFAULT, // 初始y坐标
CW_USEDEFAULT, // 初始x方向尺寸
CW_USEDEFAULT, // 初始y方向尺寸
NULL, // 父窗口句柄:新建窗口为顶级窗口(如应用程序窗口),则参数为NULL
NULL, // 窗口菜单句柄:没有菜单,为NULL
hInstance, //程序实例句柄
NULL); // 创建参数
//iCmdShow的初值是什么?
//该参数决定窗口在屏幕中的初始显示形式:正常显示、最小化、最大化
ShowWindow(hwnd, iCmdShow);
//调用之后,新建窗口在屏幕中完全可见
UpdateWindow(hwnd);
//消息循环
//NULL,0,0:希望获取由该程序所创建的所有窗口的所有消息
//GetMessage函数,当msg.message != WM_QUIT (其值为0xx12)时,返回非0值,
//否则返回0,此时退出消息循环。
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg); //消息解释
DispatchMessage (&msg); //分发消息到各个窗口(MSG结构中有HWND参数)
}
return msg.wParam;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINTmessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
//SND_FILENAME | SND_ASYNC:使用文件名、异步播放
PlaySound (TEXT ("A whole new world.wav"), NULL, SND_FILENAME| SND_ASYNC);
return 0;
case WM_PAINT:
//&ps:对客户区进行绘制的信息。BeginPaint调用,
//使用画刷擦除背景、使整个客户区有效,返回“设备环境句柄”(显示硬件)。
hdc = BeginPaint (hwnd, &ps);
//以像素为单位的客户区的位置信息
GetClientRect (hwnd, &rect);
//在&rect内绘制单行/水平/垂直居中的文本
DrawText (hdc, TEXT ("hello Windows XP !"), -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
//释放设备环境句柄
EndPaint (hwnd, &ps);
return 0;
case WM_DESTROY:
//PostQuitMessage函数将WM-QUIT消息插入到消息队列,
//使得GetMessage返回0,从而退出消息循环
PostQuitMessage (0);
return 0;
}
//对所有窗口过程没有处理的消息进行默认处理
return DefWindowProc (hwnd, message, wParam, lParam);
}
/*
提醒您注意:如果您使用MicrosoftVisual C++ 为此程序建立新项目,那么您得加上连结程序所需的链接库文件。从Project菜单选择 Setting选项,然后选取Link页面标签。从Category清单方块中选择General,然后在 Object/Library Modules文字方块添加WINMM.LIB(Windows multimedia-Windows多媒体)。您这样做是因为HELLOWIN将使用多媒体功能呼叫,而内定的项目中又不包括多媒体链接库文件。不然连结程序报告了错误信息,表明PlaySound函数不可用。
HELLOWIN将存取文件HELLOWIN.WAV,该文件在本书所附光盘的HELLOWIN目录中。执行HELLOWIN.EXE时,内定的目录必须是HELLOWIN。在Visual C++中执行此程序时,虽然执行文件会产生在HELLOWIN的RELEASE或DEBUG子目录中,但执行程序的目录还是必须在HELLOWIN中。
*/
1、Windows、窗口类、窗口、窗口过程的关系
一般,应用程序不直接调用窗口过程,而由Windows自身调用(通过消息机制)。若应用程序希望调用自身的窗口过程,则可通过调用函数SendMessage实现。
2、Windows消息
Windows消息分为队列消息和非队列消息。
队列消息是指那些由Windows放入程序的消息队列中的消息。在消息循环中检索,并被投递到窗口过程。
非队列消息是由Windows对窗口过程的直接调用产生的。
队列消息主要由用户的输入产生,如按键消息、字符消息、鼠标消息、定时器消息、重绘消息、退出消息等。
非队列消息包含除队列消息以外的其他所有消息。非队列消息通常由调用特定的Windows函数引起。如调用CreateWindow、UpdateWindow等函数的时候。
对于用户来说,不用区分消息类型,Windows内部承担了这部分复杂度。从窗口过程的角度看,这些消息是以有序、同步方式到来的。
笔者认为:队列消息就是普通的用户消息,需要排队;而非队列消息到来就会被放在队首,不需要排队。
<Windows程序设计>原文:
虽然Windows程序可有多个执行线程,但每个线程的消息队列仅为哪些其窗口过程在该线程内执行的窗口进行消息处理。换言之,消息循环和窗口过程不是并发运行的。
当一个消息循环从其自身的消息队列中检索消息,并调用DispatchMessage函数将检索到的消息发送给窗口过程时,只有在窗口过程将控制权还给Windows后,DispatchMessage才会返回。
注释:消息处理,其本质是窗口过程的调用。因此,消息循环其实是一个函数嵌套调用的过程,内层返回,外层才能继续。
但是,窗口过程可以调用为其发送其他消息的函数。这种情形下,在该函数调用返回之前,窗口过程必须将第二个消息处理完毕,此时窗口过程才处理前一条消息。例如,当一个
窗口过程调用UpdateWindow时,Windows会以一条WM_PAINT消息来调用窗口过程。当窗口过程处理完WM_PAINT消息后,UpdateWindow调用才将控制权返还给窗口过程。
注释:消息处理虽然不能被中断,但是一个消息的处理过程中可以发送另一个消息,形成消息的嵌套,也就是窗口过程的嵌套。
但是,消息处理时发送的消息需要在消息队列中排队吗?那么怎么知道该消息已经处理完毕而返回到调用过程呢?还是使用了其他的手段,如SendMessage类似的?
这就意味着,窗口过程必须是可重入(reentrant)的。那么,与“可重入”相关的问题必须注意。
这段话不理解:
在许多情况下,窗口过程必须保留其从消息中获取的消息,并在处理其他消息时使用该消息。这种信息必须保存在窗口过程所定义的静态变量中,或保存在全局变量中。