主要是对前面几个部分知识内容的一些总结并进行详细的分析
主函数入口点
在 main.cpp 中需要做得第一件事是包含 Win32 程序所需的头文件和定义函数入口点。 需要注意的是, Win32窗体应用程序的主函数入口点是 WinMain 函数(控制台程序的入口点是 main 函数)。 对于现在来说,我们只需要在源文件的顶部包含 windows.h 头文件即可。 源文件 main.cpp 的头文件和空的 WinMain 函数见于以下程序:
#include<Windows.h>
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow)
{
return 0;
}
使用 wWinMain 代替 WinMain。这两个主函数的不同在于 wWinMain 的第三个参数cmdLine 使用 Unicode 编码,而另一个使用 ANSI 编码将会转换 Unicode 为 ANSI。 因此将导致 Unicode 字符串中的字符丢失,而使用 wWinMain 则允许我们正确处理传入应用程序的 Unicode 参数。
(w)WinMain 函数有四个参数,其定义如下:
HINSTANCE hInstance: 应用程序当前实例的句柄(译者注:了解 Win32 的基本知识最好是看 Windows 程序
设计第五版)。
HINSTANCE prevInstance:应用程序的前一个实例的句柄。 根据 MSDN 的文档现在此参数将一直是 NULL。虽然此参数一直是 NULL,如果你想要确定该应用程序是否已经有实例在运行,文档推荐使用 CreateMutex函数来创建唯一名字的 mutex(互斥体)。 当已经有实例运行时,再次创建 mutex, CreateMutex 函数将会返回 ERROR_ALREADY_EXISTS。
LPSTR cmdLine :(或使用 Unicode 编码的 LPWSTR):应用程序的命令行由程序外部输入。允许你传递命令给程序,例如通过 cmd 命令终端,或者是通过快捷方式提供命令参数,等等。
int cmdShow:窗口被显示为哪个模式的 ID 号(译者注:例如最小化,正常,最大化等)。
窗口初始化
尽管上述程序能够编译运行,可是由于没有创建窗口,运行时什么也没显示。 So,下一个步骤就是创建 Win32窗口。 首先要注册一个窗口类并且创建窗口本身。 应用程序必须在系统中注册它的窗口
程序循环就是一个无限的循环,直到用户跳出该循环为止。 由接受 WM_QUIT 事件(Win32 的退出消息)来发生结束指令, 假如在主菜单中设定了用户按下 Esc 键产生该事件(当游戏中时,按下 Esc 键并没有退出,而是暂停,除非是制作者设计了该退出方式),或者你设定它退出的任何其他方式。
MSG 是一个用于持有来自于操作系统中 window 消息的 Win32 结构,它用于程序响应这些消息。如果发送一定数量的消息后程序一直没有响应,则操作系统将会报告该程序未响应。 通常我们假定它的意思就是程序被冻结或者发生了某些错误,这也意味着应用程序没有从操作系统中恢复过来。 如果程序被设计为响应式的,假设程序已经被冻结即窗口显示“未响应”消息或任务管理器显示该应用未响应,过一会儿它仍然是安全的。我就曾经多次运行十分复杂并且长时间查询在网络上的微软 Access。操作系统就报告程序未响应,尽管程序只是忙于做一些任务没有处理事件而已。
对于 window 消息,我们需要做两件事。首先需要取得新的消息并且处理它们,其次需要调度(响应)这些消息。PeekMessage 函数取得相关窗口的消息(我们使用 CreateWindow 函数创建的窗口)。此函数的第一个参数是持有消息的结构的地址,其次是窗口句柄(可选),最小最大消息过滤标志,移除标志。PM_REMOVE 移除标志,将会将消息从它的队列中移除。因为我们处理该消息,一旦处理后它将不需要留在队列中。如果有消息被 PeekMessage 函数获得,我们调用 TranslateMessage 函数和 DispatchMessage 函数来响应该消息。Win32 函数 TranslateMessage 将虚拟键消息翻译为字符消息,调度函数 DispatchMessage 将消息传给窗口过程处理,窗口过程将对于它所接受到的消息执行实际的行动。如果没有收到消息,在程序中就只做更新 Update 和渲染 rendering 这两件事。例如根据所侦测到的用户输入,物理计算,动画,更新音频缓存等的更新操作和渲染场景几何体。
拿大家熟悉的视频游戏举个例子,查看游戏循环就一目了然了。游戏程序对于每一帧的渲染做一系列的具体事情。游戏中的帧是游戏循环的单一的反复过程。大多数游戏力求达到每秒 30 或 60 帧,换句话说就是相对于真实世界中的每一秒钟,游戏做 30 到 60 次的循环。当游戏变得越来越复杂时,这一目标更难达到。 每秒的帧数通常用来衡量游戏渲染的速度,但是游戏不仅仅有渲染代码,还需要每数帧就处理游戏中的物理,碰撞, AI(人工智能) 数据流和游戏的特效。
窗口过程(回调函数)
在我们能够编译和运行我们的程序之前的最后一部分就是所提供的窗口过程,也就是窗口过程函数。窗口过程函数是回调函数,意味着在我们的程序中当获得消息后就调用该函数来处理。回调函数一般写成下面的形式:
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0; }
窗口过程函数返回 LRESULT 类型并且有回调 CALLBACK 修饰。 此函数的命名遵循普通函数命名标准该回调函数接受参数窗口的句柄来调度该窗口的消息,无符号整型消息代号,两个可携带额外信息的参数(wParam 和 lParam) 。最后两个参数用于补充的数据,在回调函数中处理消息需要更多的数据时。
退出消息由调用 Win32 函数 PostQuitMessage 来处理,这将导致在应用程序循环中的 MSG 对象取得 WM_QUIT消息,然后就按照我们的设定结束应用程序循环并且退出程序。 假如想退出而又不立即退出的话,只需要发送一个退出消息即可。 例如,在微软的 Word 软件中如果没有保存就尝试退出的话,将会弹出一个对话框询问你是否保存之后再退出。
回调函数的最后一部分就是调用 DefWindowProc 函数,它有着与窗口过程函数同样的参数。 这个函数只被那些我们没有写自定义响应代码的消息所调用。 在此 Demo 中我们只响应了绘制消息和退出消息,但是还有其他大量的可以查到的消息,如按键,鼠标点击,鼠标移动,计时器等等。 DefWindowProc 函数是一个 Win32 函数,它调用默认行为处理所有的消息,来确保我们响应每一个消息,即使我们对那些消息不感兴趣。