概述
- 本篇说讲的是window窗口程序——由主窗口、菜单、工具栏、滚动条、按钮等控件组成,而D3D游戏编程并不需要过于复杂的GUI(graphics user interface),仅仅需要一个主窗口用以渲染
基础
-
windows中,多个应用程序可以并发运行,因此硬件资源都在多应用程序的共享范围。然而为了防止一些无组织的访问或修改资源,windows应用程序无法直接访问硬件资源
-
windows应用程序依靠事件(如按键、点击鼠标等)不断运行,当事件发生后,操作系统向发生事件的应用程序发送相应消息,消息便进入此应用程序的消息队列,而应用程序会于消息循环中不断检测队列中的消息,接受到消息时则将消息分派到相应窗口的窗口过程函数,而窗口过程函数则写有处理特定消息的代码
- 不一定非要给每个窗口都写一个独立的窗口过程。每个窗口都有与之相关联的窗口过程函数,但窗口间可以共享相同的窗口过程函数
- 目标窗口不处理的消息转发至Win32API提供的默认窗口过程DefWindowProc
-
windows程序中含有各种各样的资源(如窗口、光标),系统创建这些资源时会为它们分配内存并返回标识这些资源的标识号,而标识号则为句柄
-
编码约定:Windows API由函数或COM接口组成,很少由c++类提供
-
整数类型:
数据类型 大小 有无符号 BYTE 8bit 无 DWORD 32bit 无 INT32 32bit 有 INT64 64bit 有 LONG 32bit 有 LONGLONG 64bit 有 UINT32 32bit 无 UINT64 64bit 无 ULONG 32bit 无 ULONGLONG 64bit 无 WORD 16bit 无 -
BOOL:为int类型别名,不可与bool互换
#define FALSE 0 #define TRUE 1
-
指针类型:16 位Windows有 2 种类型的指针,P 表示“指针”,LP 表示“长指针”。 长指针 (也称为 远指针 ,) 用于处理当前段之外的内存范围。 保留 LP 前缀,以便更轻松地将 16 位代码移植到 32 位Windows。 今天没有区别,这些指针类型都是等效的。 如果必须使用一个指针类型,请使用 P
- 精度类型:以下数据类型始终是指针的大小,即 32 位应用程序中宽 32 位,64 位应用程序宽 64 位。 大小在编译时确定。 当 32 位应用程序在 64 位Windows上运行时,这些数据类型仍宽 4 字节。 (64 位应用程序无法在 32 位Windows上运行,因此不会发生反向情况。)
- DWORD_PTR
- INT_PTR
- LONG_PTR
- ULONG_PTR
- UINT_PTR
- 精度类型:以下数据类型始终是指针的大小,即 32 位应用程序中宽 32 位,64 位应用程序宽 64 位。 大小在编译时确定。 当 32 位应用程序在 64 位Windows上运行时,这些数据类型仍宽 4 字节。 (64 位应用程序无法在 32 位Windows上运行,因此不会发生反向情况。)
-
-
Unicode:支持所有字符集和语言,windows使用UTF-16编码表示Unicode字符,其中每个字符编码为一个或两个16位值。UTF-16字符为宽字符
- c++以wchar_t宽字符表示Unicode,且无论64位或32位windows操作系统,wchar_t都为16位
- 使用宽字符时,必须为字符串字面值冠以前缀L。因为这样会将其当作宽字符串处理
- 处理宽字符时需使用相应版本的字符串函数
- 获取宽字符串长度:wcslen()
- 复制宽字符串:wcscpy()
- 比较两个宽字符串:wcscmp()
- 宽字符串类std::wstring
Typedef 定义 CHAR char PSTR or LPSTR char* PCSTR or LPCSTR const char* PWSTR or LPWSTR wchar_t* PCWSTR or LPCWSTR const wchar_t* - ANSI 版本效率较低,因为操作系统必须在运行时将 ANSI 字符串转换为 Unicode
windows应用程序
-
流程:
- 注册、创建窗口结构体
- 初始化程序主窗口并展示更新
- 开始消息循环,窗口过程对消息进行处理
-
//头文件 //含有编写Windows应用程序所需的所有Win32 API结构体、数据类型、函数声明 #include<windows.h> //指定所创建主窗口的句柄 //不少API需要指定特定窗口句柄进行处理 HWND g_hMainWnd = 0; //创建并初始化Winodws应用程序主窗口。初始化成功返回true,失败false bool initMainWindow(HINSTANCE instanceHandle, int show); //消息循环 int run() //主窗口的窗口过程 LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); //应用程序入口点,相当于main() int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd) { //用hInstance、nShowCmd作为参数调用initMainWindow if(!initMainWindow(hInstance, nShowCmd)) { return 0; } //初始化完后,即可开始消息循环 return run(); } bool initMainWindow(HINSTANCE instanceHandle, int show) { //填写WNDCLASS结构体 WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = instanceHandle; wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = L"BasicWndClass"; //注册WNDCLASS实例 if(!RegisterClass(&wc)) { MessageBox(0, L"RegisterClass FAILED", 0, 0); return false; } //CreateWindow函数返回创建的窗口的句柄(HWND),若创建失败返回0 g_hMainWnd = CreateWindow( L"BasicWndClass", //采用的前面注册的WNDCLASS L"Win32Basic", //窗口标题 WS_OVERLAPPEDWINDOW, //窗口的样式标志 CW_USEDEFAULT, //x坐标 CW_USEDEFAULT, //y坐标 CW_USEDEFAULT, //窗口宽度 CW_USEDEFAULT, //窗口高度 0, //父窗口 0, //菜单句柄 instanceHandle, //应用程序实例句柄 0 //其他参数 ); if(g_hMainWnd == 0) { MessageBox(0, L"CreateWindow FALIED", 0, 0); return false; } //展示窗口并更新 ShowWindow(g_hMainWnd, show); //show为显示模式 Update(g_hMainWnd); return true; } int run() { MSG msg = {0}; while(msg.message != WM_QUIT) { if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { //动画或游戏逻辑 } } return (int)msg.wParam; } LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_LBUTTONDOWN: //按下鼠标左键 MessageBox(0, L"Hello, World", L"Hello", MB_OK); return 0; case WM_KEYDOWN: //非键盘键被按下 if(wParam == VK_ESCAPE) { DestoryWindow(g_hMainWnd); return 0; } case WM_DESTORY: //窗口被销毁 PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, msg, wParam, lParam); }
-
WinMain函数:
- HINSTANCE hInstance:当前应用程序实例句柄
- HINSTANCE hPrevInstance:在 16 位Windows中使用,但现在始终为零
- PWSTR pCmdLine:运行此程序所用的命令行参数字符串
- int nCmdShow:标志。指定应用程序如何显示
//函数调用约定 #define WINAPI _stdcall
-
WNDCLASS结构体
typedef struct _WNDCLASS { UINT style; //窗口类样式。CS_HREDRAW | CS_VREDRAW表示宽或高改变时重绘窗口 WNDPROC lpfnWndProc; //指向与此WNDCLASS实例相关联的窗口过程函数的指针 int cbClsExtra; //要按照窗口类结构分配的额外字节数。 系统将字节初始化为零 int cbWndExtra; //在窗口实例之后分配的额外字节数。 系统将字节初始化为零 HINSTANCE hInstance; //当前应用程序实例的句柄 HICON hIcon; //类图标的句柄。 图标资源的句柄。 如果为 NULL,则系统提供默认图标 HCURSOR hCursor; //类游标的句柄。 游标资源的句柄。 如果为 NULL,则每当鼠标移动到应用程序的窗口中时,应用程序都必须显式设置光标形状 HBRUSH hbrBackground; //类背景画笔的句柄,指定窗口工作区的背景颜色 LPCSTR lpszMenuName; //窗口菜单 LPCSTR lpszClassName; //所创窗口类结构体的名字 } WNDCLASS;
-
创建并显示窗口
HWND CreateWindow( [in, optional] LPCTSTR lpClassName, //前面已注册的WNDCLASS [in, optional] LPCTSTR lpWindowName, //窗口名称 [in] DWORD dwStyle, //窗口样式 [in] int x, //x坐标 [in] int y, [in] int nWidth, //宽度 [in] int nHeight, //高度 [in, optional] HWND hWndParent, //父窗口句柄 [in, optional] HMENU hMenu, //菜单句柄 [in, optional] HANDLE hInstance, //相关的应用程序句柄 [in, optional] LPVOID lpParam //指向用户定义数据 );
-
消息循环
-
首先为Windows消息的MSG类型创建msg实例
typedef struct tagMSG { HWND hwnd; //接受消息的窗口过程所属的窗口句柄 UINT message; //识别消息的预定义常量值(如WM_QUIT) WPARAM wParam; //通常用来存储小段信息,如标志 LPARAM lParam; //通常用于存储消息所需的对象 DWORD time; //消息被发出时间 POINT pt; //消息发出时,鼠标指针坐标 } MSG, *PMSG, *NPMSG, *LPMSG;
-
PeekMessage
BOOL PeekMessage( [out] LPMSG lpMsg, //指向收到的MSG消息 [in, optional] HWND hWnd, //要检索消息的窗口的句柄 [in] UINT wMsgFilterMin, //要检索的消息范围内的第一条消息的值 [in] UINT wMsgFilterMax, //要检查的消息范围内最后一条消息的值 [in] UINT wRemoveMsg //指定如何处理消息 );
- TranslateMessage():键盘按键的转换
- DispatchMessage():将消息分派给相应的窗口过程
-
-
窗口过程
-
LRESULT Wndproc( HWND hWnd, //接收此消息的窗口的句柄 UINT msg, //标识特定消息的预定值 WPARAM wParam, LPARAM lParam );
-
-
消息框函数
int MessageBox( [in, optional] HWND hWnd, //消息框所属窗口的句柄 [in, optional] LPCTSTR lpText, //消息框显示的文本 [in, optional] LPCTSTR lpCaption, //消息框的标题文本 [in] UINT uType //消息框样式 );