Direct3D 10学习笔记(四)——Windows编程
本篇将简单整理基本的Windows应用程序的实现,并作为创建Direct3D 10应用程序的铺垫。具体内容参照《 Introduction to 3D Game Programming with DirectX 10》(中文版有汤毅翻译的电子书《DirectX 10 3D游戏编程入门》)中的附录A。
1.编写入口函数WinMain
Windows编程中的WinMain函数与普通C++编程的main函数作用相同,指定了程序开始的执行点。其函数原型如下:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nShowCmd)
hInstance:唯一标志当前运行中的实例的句柄,同一Windows应用程序可以有多个实例并行运行,用于引用某个特定实例以作区分。
hPrevInstance:Win32环境下该参数总为NULL,即Win32编程不再使用。
lpCmdLine:程序启动时传入命令行参数的字符串。
nShowCmd:指定程序窗口的显示方式。其可用的值用宏来枚举,如下:
#define SW_HIDE 0
#define SW_SHOWNORMAL 1
#define SW_NORMAL 1
#define SW_SHOWMINIMIZED 2
#define SW_SHOWMAXIMIZED 3
#define SW_MAXIMIZE 3
#define SW_SHOWNOACTIVATE 4
#define SW_SHOW 5
#define SW_MINIMIZE 6
#define SW_SHOWMINNOACTIVE 7
#define SW_SHOWNA 8
#define SW_RESTORE 9
#define SW_SHOWDEFAULT 10
#define SW_FORCEMINIMIZE 11
#define SW_MAX 11
SW_HIDE:隐藏窗口并将其活动状态传递给其他窗口。
SW_NORMAL:激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志。
SW_SHOWMINIMIZED:激活窗口并将其最小化。
SW_SHOWMAXIMIZED:激活窗口并将其最大化。
SW_MAXIMIZE:最大化指定的窗口。
SW_SHOWNOACTIVATE:以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态。
SW_SHOW:在窗口原来的位置以原来的尺寸激活和显示窗口。
SW_MINIMIZE:最小化指定的窗口并且激活在Z序中的下一个顶层窗口。
SW_SHOWMINNOACTIVE:将窗口显示为图标。当前激活窗口仍维持激活状态。
SW_SHOWNA:以窗口原来的状态显示窗口。激活窗口仍然维持激活状态。
SW_RESTORE:激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志。
SW_SHOWDEFAULT:依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态,STARTUPINFO结构是由启动应用程序的程序传递给CreateProcess函数的。
SW_FORCEMINIMIZE:在WindowNT5.0中最小化窗口,即使拥有窗口的线程被挂起也会最小化。在从其他线程最小化窗口时才使用这个参数。
如果WinMain调用成功,将返回WM_QUIT中的wParam成员。如果函数在进入消息循环前就已退出,则返回0。其中标识符WINAPI指定了函数的调用方式,即如何处理堆栈上的函数参数,其定义如下:
#define WINAPI __stdcall
2.填充并注册WNDCLASS
要初始化一个窗口,首先要填充一个WNDCLASS结构体,以描述窗口的基本属性。WNDCLASS定义如下:
typedef struct tagWNDCLASS
{
UINT style;
WNDPROC lpfnWndProc;
INT cbClsExtra;
INT cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
}WNDCLASS, *PWNDCLASS, NEAR *NPWNDCLASS, FAR *LPWNDCLASS;
style:指定窗口样式。其可用枚举如下:
#define CS_VREDRAW 0x0001
#define CS_HREDRAW 0x0002
#define CS_DBLCLKS 0x0008
#define CS_OWNDC 0x0020
#define CS_CLASSDC 0x0040
#define CS_PARENTDC 0x0080
#define CS_NOCLOSE 0x0200
#define CS_SAVEBITS 0x0800
#define CS_BYTEALIGNCLIENT 0x1000
#define CS_BYTEALIGNWINDOW 0x2000
#define CS_GLOBALCLASS 0x4000 #define CS_IME 0x00010000
#if(_WIN32_WINNT >= 0x0501)
#define CS_DROPSHADOW 0x00020000
#endif /* _WIN32_WINNT >= 0x0501 */
lpfnWndProc:指向消息处理函数的指针。
cbClsExtra、cbWndExtra:用于存储附加数据,一般不会用到,设置为NULL。
hInstance:应用程序实例句柄。与WinMain函数的第一个参数相同。
hIcon:指定显示窗口标题栏上的图标。
hCursor:指定鼠标位于窗口客户区上显示的光标。
hbrBackground:指定客户窗口区的背景色。可以使用Win32函数GetStockObject返回一个内置画刷句柄,其用法示例如下:
wc.hbrBackground = static_cast<HBRUSH>(GetStockObject(NULL_BRUSH));
lpszMenuName:指定窗口菜单。
lpszClassName:指定窗口类名称,即为该WNDCLASS实例起名。若需使用相应WNDCLASS实例,可以通过这个名称来引用。
填充完成WNDCLASS后,需要使用RegisterClass函数注册WNDCLASS到系统中,调用失败返回NULL。
ATOM WINAPI RegisterClass(_In_ CONST WNDCLASS *lpWndClass);
3.创建并显示窗口
创建窗口使用CreateWindow函数,其原型如下:
HWND WINAPI CreateWindow(
_In_opt_ LPCTSTR lpClassName,
_In_opt_ LPCTSTR lpWindowName,
_In_ DWORD dwStyle,
_In_ INT X,
_In_ INT Y,
_In_ INT nWidth,
_In_ INT nHeight,
_In_opt_ HWND hWndParent,
_In_opt_ HMENU hMenu,
_In_opt_ HANDLE hInstance,
_In_opt_ LPVOID lpParam);
lpClassName:已注册的WNDCLASS实例名称。
lpWindowName:窗口名称,即显示在窗口标题栏上的字符串名称。
dwStyle:定义窗口样式。如WS_OVERLAPPEDWINDOW是多个描述窗口特征的标志值的组合:
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
x:窗口左上角位于屏幕上的x坐标。若指定为CW_USEDEFAULT,系统会为窗口选择一个适当的默认坐标。
y:窗口左上角位于屏幕上的y坐标。若指定为CW_USEDEFAULT,系统会为窗口选择一个适当的默认坐标。
nWidth:窗口宽度,单位为像素。若指定为CW_USEDEFAULT,系统会为窗口选择一个适当的默认宽度。
nHeight:窗口高度,单位为像素。若指定为CW_USEDEFAULT,系统会为窗口选择一个适当的默认高度。
hWndParent:当前窗口的父窗口句柄。若没有父窗口则设置为NULL。
hMenu:菜单句柄。若窗口没有菜单,则设置为NULL。
hInstance:与窗口关联的应用程序实例句柄。
lpParam:传递给WM_CREATE消息处理函数的用户自定义数据指针。当创建窗口时,操作系统会向窗口发送一个WM_CREATE消息。该消息会在CreateWindow方法返回前发出。如果希望创建窗口时执行某些操作,则需要处理WM_CREATE消息。
CreateWindow创建窗口成功后返回该窗口的句柄。创建失败则返回NULL。需要记录下该窗口的句柄,以便将其传入给API函数,明确操作的窗口对象。
控制窗口的显示需要使用ShowWindow函数,其原型如下:
BOOL WINAPI ShowWindow(_In_ HWND hWnd, _In_ int nCmdShow);
hWnd:指定想要显示的窗口的句柄。
nCmdShow:指定窗口的初始显示状态。该值可以设为WinMain的第四个参数。
初次显示窗口,必须调用UpdateWindow方法,对窗口进行一次手动刷新,其原型如下:
BOOL WINAPI UpdateWindow(_In_ HWND hWnd);
4.消息循环
完成窗口的初始化工作后,需要编写程序的核心部分——消息循环。
首先声明一个MSG结构体类型的变量来表示Windows消息,其原型如下:
typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
#ifdef _MAC
DWORD lPrivate;
#endif
}MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
hwnd:指定接收消息的窗口句柄。
message:用于标识特定消息预定义常量。
wParam:与消息相关的附加值,取值依具体消息而定。
lParam:与消息相关的附加值,取值依具体消息而定。
time:消息被送入队列的时间。
Pt:当消息送入队列时,鼠标在屏幕上的坐标。
随后进入消息循环,使用GetMessage函数从队列中检索消息,并使用消息的具体内容填充MSG变量,其原型如下:
BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax)
lpMsg:指向MSG变量的指针。
hWnd、wMsgFilterMin、wMsgFilterMax:均设置为NULL。
当出错时,GetMessage返回-1;当收到WM_QUIT消息时,GetMessage返回0;当返回其他值时,需要调用TranslateMessage和DispatchMessage函数。这两个函数均接收一个指向MSG变量的指针。TranslateMessage将虚拟键盘码转换为字符信息,而DispatchMessage将消息分发给特定的消息处理函数。
消息循环示例:
INT run()
{
MSG msg = { }; BOOL bRet = ;
while ((bRet = GetMessage(&msg, NULL, NULL, NULL)))
{
if (bRet != -)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} return static_cast<INT>(msg.wParam);
}
在游戏中由于会主动重绘界面,而不是坐等消息到来。传统消息循环中GetMessage会导致当队列没有消息时让线程进入休眠状态。故在游戏中使用PeekMessage取代GetMessage,该函数能在没有消息时直接返回,不会阻塞线程。
改进后的消息循环示例:
INT run()
{
MSG msg = { }; while (msg.message != WM_QUIT)
{ if (PeekMessage(&msg, , , , PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} return static_cast<INT>(msg.wParam);
}
5.消息处理函数
消息处理函数也称为窗口过程函数。它用来对窗口接收到的消息做出响应,执行对应的指令。
消息处理函数由自己定义,它需是一个回调函数,使用CALLBACK标识符,系统将在程序的代码空间外调用这个函数,即不需要直接调用该函数。当窗口需要处理一个消息时,系统会自动调用该函数。
消息处理函数的声明可以如下:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
hwnd:接收消息的窗口句柄。
msg:标识特定消息的预定义常量。用前缀"WM"表示窗口消息,可取的值有上百个,具体参见MSDN。
wParam:与消息相关的附加值,取值依具体消息而定。
lParam:与消息相关的附加值,取值依具体消息而定。
DefWindowProc函数是默认的消息处理函数,那些未被处理的消息就会以DefWindowProc中定义的默认行为来处理,即不需要亲自处理所有消息。
可以用多分支语句来处理消息,如接收到由窗口销毁函数DestroyWindow发送的WM_DESTROY消息时,调用PostQuitMessage函数再发送一个WM_QUIT消息来告诉程序终止消息循环的运行。
消息处理函数示例:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(NULL);
return ; default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Direct3D 10学习笔记(四)——Windows编程的更多相关文章
- Direct3D 10学习笔记(三)——文本输出
本篇将简单整理Direct3D 10的文本输出的实现,具体内容参照< Introduction to 3D Game Programming with DirectX 10>(中文版有汤毅 ...
- Direct3D 10学习笔记(二)——计时器
本篇将简单整理Direct3D 10的计时器实现,具体内容参照< Introduction to 3D Game Programming with DirectX 10>(中文版有汤毅翻译 ...
- Direct3D 10学习笔记(一)——初始化
本篇将简单整理Direct3D 10的初始化,具体内容参照< Introduction to 3D Game Programming with DirectX 10>(中文版有汤毅翻译的电 ...
- 孙鑫VC学习笔记:多线程编程
孙鑫VC学习笔记:多线程编程 SkySeraph Dec 11st 2010 HQU Email:zgzhaobo@gmail.com QQ:452728574 Latest Modified ...
- kvm虚拟化学习笔记(四)之kvm虚拟机日常管理与配置
KVM虚拟化学习笔记系列文章列表----------------------------------------kvm虚拟化学习笔记(一)之kvm虚拟化环境安装http://koumm.blog.51 ...
- Hadoop学习笔记(7) ——高级编程
Hadoop学习笔记(7) ——高级编程 从前面的学习中,我们了解到了MapReduce整个过程需要经过以下几个步骤: 1.输入(input):将输入数据分成一个个split,并将split进一步拆成 ...
- IOS学习笔记(四)之UITextField和UITextView控件学习
IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...
- WCF学习笔记之事务编程
WCF学习笔记之事务编程 一:WCF事务设置 事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元: WCF通过System.ServiceModel.TransactionFlowA ...
- ES6学习笔记<四> default、rest、Multi-line Strings
default 参数默认值 在实际开发 有时需要给一些参数默认值. 在ES6之前一般都这么处理参数默认值 function add(val_1,val_2){ val_1 = val_1 || 10; ...
随机推荐
- Css Js Loader For Zencart
Css Js Loader 描述:这个插件很早就出来了,可能知道人非常少 这个插件的功能是整合所有的网站的CSS和JS内容到一个文件里边. 因为CSS和JS文件到了一个文件,加快了程序的运行 在配合其 ...
- 全球Top10最佳移动统计分析sdk
监视应用程序的分析帮助您优化您的移动应用程序的某些元素,它也给你正确的洞察到你的营销计划.没有手机的分析软件包会有缺乏必要的数据,以帮助你提高你的应用程序需要.如果你是一个软件开发者或出版商为Goog ...
- ACCESS中计算日均值
如图所示,现有时间数据的时间字段是精确到时分秒的,现在需要计算PM2.5的日平均值,因此在查询时需要过滤时间字段的格式,去掉时分秒部分,只提取年月日部分. 查找资料,发现一般用CONVERT()函数实 ...
- scalac 学习
val logEnable = false def log(msg: => String) = if (logEnable) println(msg) val MSG = "progr ...
- Leetcode 详解(ReverseWords)
Leetcode里面关于字符串的一些问题,描述如下: Given an input string, reverse the string word by word. For example,Given ...
- 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler定时器
在C#WINFORM或者是ASP.NET的WEB应用程序中,根据各种定时任务的需求,比如:每天的数据统计,每小时刷新系统缓存等等,这个时候我们得应用到定时器这个东东. .NET Framework有自 ...
- CSipSimple的插件结构
CSipSimple的第三方编码器是以插件形式集成的,那么它是怎么实现的?我们以音频编码器为例进行说明. 一.何为插件 工程中有一个包,com.csipsimple.plugins.codecs.从包 ...
- linux命令每日一练习 创建新文件 列出文件的时候带着行号
touch ××× nl ****
- Linux下man安装及使用方法
常用法: man [section] name 其中: section 指的是手册页的哪个部分,可以是1.2.3…8.,若不指定,man会按照次序依次查找,知道找到第一个. name 指的是某个命令. ...
- Python错误和异常学习
一:错误解释 1.语法错误:代码不符合解释器或者编译器语法 2.逻辑错误:不完整或者不合法输入或者计算出现问题 代码运行前的语法或者逻辑错误,语法错误在执行前修改,逻辑错误无法修改 二:异常 执行过程 ...