《Windows游戏编程大师技巧》学习笔记(二)

《Windows游戏编程大师技巧》学习笔记(二)

多任务和多线程

Windows允许不同的应用程序以轮询的方式“同时”执行,每一个应用程序都占用一段很短的时间片来运行,而后轮到下一个应用程序运行,如下图所示,CPU由几个不同的应用程序以循环的方式共享,而负责判断出下一个运行的程序、给每个程序分配运行时间的是调度程序的工作:
调度程序示意图

  • 简单的调度策略:给每个应用程序分配固定的运行时间;
  • 复杂的调度策略:将应用程序设定为不同的优先级和抢先性或低优先级的事件;

在大部分情况下,我们是不需要对Windows调度程序过度关注。
深入接触Windows,会发现其不仅是多任务的,还是多线程的,程序可以有许多较为简单的执行线程构成;这些线程被视为具有较重的权值的进程,从而如程序一样被调度;程序可以有一个主线程和几个工作线程构成,如下图:
Windows多线程机制示意图

事件模型

Windows是一个事件驱动的操作系统,和DOS程序不同的是,Windows程序都是等待用户去触发事件,然后系统对该事件发生响应,进行动作;如下图所示,每个程序都向Windows发送待处理的事件和消息,Windows对其中的一些进行处理,大部分消息和事件都被传递给应用程序来处理:
Windows事件处理

微软编程风格:匈牙利命名法

1. 前缀

前缀在大多数情况下用于变量名,并且因数据类型不同而不同,见下图:
匈牙利符号表示法使用的前缀代码

2. 变量

变量由 “(一个或多个)前缀 + (一个或多个)子名” 构成,每一个子名都要以大写字母开头,如:char* szFileName;int lpiData;
一般来说,对于函数内的局部变量没有特殊的命名要求,但是对于全局变量,则必须以g_g开头,如:int g_iXPos;

3. 函数

函数命名使用大驼峰命名法,与变量命名规则相似,但是没有前缀,但是需要注意的是,函数名中不能使用下划线,如:int PlotPixel(int ix, int iy, int ic);

4. 类型和常量

所有类型和常量都是大写字母,但是名字中可以使用下划线,如:const LONG NUM_SECTORS = 100;#define MAX_CELLS 64typedef unsigned char UCHAR;

5. 类

所有的类必须以大写C为前缀,后续子名遵循大驼峰命名法,如:class CVector { ... };;开发者可以在此基础上独立地补充更多规则;

6. 参数

参数命名与标准的变量命名相同,但是也可以容忍不使用前缀,如:UCHAR GetPixel(int ix, int iy);UCHAR GetPixel(int x, int y);

最简单的Windows程序

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>

#include <tchar.h>

int WINAPI WinMain(
	HINSTANCE hinstance, 
	HINSTANCE hprevinstance, 
	LPSTR lpcmdline,
	int ncmdshow)
{
	MessageBox(
		NULL, 
		_T("This is MessageBox Content"), 
		_T("Title Here"), 
		MB_OK | MB_ICONEXCLAMATION
	);

	return 0;
}

如果使用VS进行编译,那么则需要确保使用的子系统为窗口,也就是在自动生成的编译指令中存在/SUBSYSTEM:WINDOWS,控制台程序会以main作为程序入口,如果找不到程序入口则会在链接阶段报错;
另外,如果是在Unicode环境下,tchar.h头文件和_T()宏是必要的,而在非Unicode环境下则可以省去。

程序运行截图:
程序运行截图
程序分析:

  1. 定义WIN32_LEAN_AND_MEAN确保不会将Windows.h中的MFC部分引入程序;
  2. WINAPI一般会被定义成__stdcall,强制参数从左向右传递;
  3. WinMain函数的参数分析:
    • HINSTANCE hinstance:本程序实例句柄,与地址类似,用于追踪应用程序自身;
    • HINSTANCE hprevinstance:已废弃,Windows旧版本中用于追踪应用程序之前的实例,即产生当前实例的应用程序实例;
    • LPSTR lpcmdline:与标准C/C++入口程序main(int argc, char** argv);中的命令行参数类似,只不过它没有一个单独的参数像argc那样指出参数个数,例如:使用如下参数运行TEST.EXE one two threelpcmdline将变成如下值"one two three"
    • int ncmdshow:这个整数在启动过程中被传递给应用程序,带有如何打开主应用程序窗口的信息,常见的参数值如下图;在大部分情况下,只会用到SW_SHOWSW_SHOWNORMALSW_HIDE;
      ncmdshow参数值
  4. 在Unicode环境下,MessageBoxW被定义为MessageBox,函数原型如下:
int WINAPI MessageBoxW(
    HWND hWnd,          // 父窗口句柄
    LPCWSTR lpText,     // 内容
    LPCWSTR lpCaption,  // 标题
    UINT uType);        // 样式

其中,当hwnd为空时,Windows桌面被用作父窗口,此时的信息框为非模态弹窗;utype可以通过或运算设置多重样式,常见的值如下:
信息框类型
信息框图标
我们可以通过MessageBox函数的返回值来确定用户选中的选项,所有可能的返回值如下:
MessageBox()返回值

MessageBox函数相似,MessageBeep函数可以发出简单的提示音,函数原型如下:

BOOL MessageBeep(UINT utype);

utype可能的参数值如下:
utype参数值