《WINDOWS游戏编程之从零开始》第三章学习笔记
作者:互联网
个人整理的笔记,比较杂乱,可能并不是很适合阅读:)
-
MessageBox 函数
其四个参数分别为:
HWND 类型的 hWnd, 表示窗口句柄;
HWND 是什么类型?简单说就是给窗口分配的句柄。(废话啊啊啊)总之就用其代指一个窗口
LPCTSTR 类型的 lpText,表示消息内容;
LPCTSTR 类型的 lpCaption,表示消息框标题内容;
UINT 类型的 uType,表示消息窗口的样式
-
PlaySound 函数
其三个参数分别为:
LPCTSTR 类型的 pszSound,指定声音源;
HMODULE 类型的 hmod,资源可执行文件的句柄(暂时不清楚是个什么句柄)
DOWRD 类型的 fdwSound,控制声音播放. 一些常用的标识:
模仿示例程序时的坑
注意 Visual Studio 中要改项目属性,保证链接器子系统设置为 Windows 不要设置为 Console,这样才是一个 Windows 文件。否则 WinMain 不会正确运行
(使用 Console 当然可以通过控制台加载出一样的效果,但是调试和运行都会多一个控制台,当然是客户端不能需要的东西)
例如,MessageBox 的 uType 设为 0,就会加载默认的窗口样式:
3.6
-
MSG结构体
消息用MSG表示.
-
消息队列.
3.7
窗口创建四部曲:
-
窗口类的设计
-
窗口类的注册
-
窗口类的正式创建
-
窗口的显示与更新
- 窗口设计
Windows 中控制窗口特征的结构体就两个, WNDCLASS 与 WNDCLASSEX,我们应当使用EX(更(geng第四声)新)。
所谓设计,其实就是在结构体里“做填空题”,定好参数。
- 窗口注册
在设计好窗口后,还需要调用 RegisterClassEx 对其进行注册。注册成功后才可以创建该类型的窗口。
不过这是啥原因?不知道注册这一步的意义是什么。应该是把固定参数列表的窗口对应到一个具体的窗口名吧
- 窗口创建
调用
AdujstWindowRect()
函数来根据尺寸和风格计算窗口尺寸。设计、注册都完成了之后,就可以调用
CreateWindow
函数来创建设计好的窗口了。一共11个参数(我的天):
(1)LPCTSTR 类的窗口类名称 lpClassName,填写我们设定并注册好的窗口类名。(2)LPCTSTR 类的窗口名字 lpWindowName,就是显示在窗口最上方的名字。
(3)DOWRD 类的窗口样式 dwStyle。有无标题栏、带系统菜单、最小化最大化这些等等…
(4)x. 一般设定为CW_USEDEFAULT
(5)y. 设定为CW_USEDEFAULT
(6)int 型的 nWidth,指定窗口宽度.
(7)int 型的 hHeight,指定窗口高度.
(8)HWND 类的 hWndParent,指定被创建窗口的父窗口句柄。一般设置为 NULL, 没有父窗口
(9)HMENU 类的 hMenu,指定窗口菜单的资源句柄。没有菜单时当然也可以设置为 NULL.
(10)HINSTANCE 类的 hInstance. 和 WinMain 一样。指定应用程序实例的句柄。
(11)lpParam,WM_CREATE 消息的附加参数 lParam 传入的数据指针(暂时不懂)。
- 窗口的显示与更新
(1)改变窗口大小
BOOL WINAPI MoveWindow()
等等,为什么这里的布尔类是大写的?
C++基础----C++ 布尔类型(bool)及BOOL和bool的区别_qiaoxinyu1989的博客-CSDN博客_bool类型
原来,BOOL 类是微软的 VC++ 自己写的宏定义:
typedef int BOOL
这个 BOOL 是三值逻辑,1 为 TRUE,0 为 FALSE,-1为 ERROR.
总之 BOOL 实际上是整型啦,其通过与0的比较来返回其三值逻辑。
(2)显示窗口
ShowWindow(HWND hWnd, int nCmdShow)
第一个就是要显示的窗口,第二个可以直接取 WinMain 中的 nCmdShow (指定窗口该如何显示).
(3)更新窗口
UpdateWindow(HWND hWnd)
发送一个 WM_PAINT 到消息队列,进入窗口过程函数, 对窗口进行更新。
貌似和重画操作不同?肯定不同吧。是窗口的更新。
3.8 消息循环体系
消息循环体系…是啥啊
-
GetMessage 消息循环体系
其实是一个函数,其作用是接收消息。
注意,GetMessage 函数是有返回值的。当收到非 WM_QUIT 消息时,其会返回1,如果出现错误,返回-1,收到 WM_QUIT 消息时返回0.
所以,核心其实就是:
MSG msg = ... while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
-
PeekMessage 消息循环体系
更常用一点.
其多了一个参数 wRemoveMsg. 这个参数指定消息的获取方式(这又是啥)
一个使用 PeekMessage 循环体系的案例:
MSG msg = { 0 }; while (msg.message != WM_QUIT) { if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { Direct3D_Update(hwnd); Direct3D_Render(hwnd); } }
二者区别: PeekMessage 体系在没有消息时会继续进行其他的渲染和更新,而 GetMessage 体系则会一直等到消息出现才会进行其他操作。
3.9 窗口过程函数
窗口过程函数,用于处理消息。
典型的过程函数是通过一个 switch 语句,switch 变量 message 的具体内容来进行相应的操作。
default 状态通常会调用一个默认的
DefWindowProc
过程函数。3.10 窗口类注销
已经迫不及待要制作一个完整的窗口程序了(这些内容也是附加的)
注销操作并不是必须的,但为了程序稳定最好加上(万一出现意料之外的问题呢)
原型:
BOOL WINAPI UnregisterClass( _In_ LPCSTR lpClassName, _In_opt_ HISTANCE hInstance );
参数分别是注销的类名称以及类的应用程序的实例句柄。
3.11
照着写的代码,贴出的与书中不同,有一些修改和自己的理解,注释中给出了.
//-------------------【程序说明】-------------------
// 程序名称:GameCore
// Learn from 浅墨 orz
// 2022 年 2 月 Created by Eke
// 描述:跟着浅墨一起学做一个最简单的Windows窗口程序!
//--------------------------------------------------
//-----------------【头文件部分】---------------------
// 描述:程序依赖的头文件
//--------------------------------------------------
#include <Windows.h>
//-----------------【宏定义部分】--------------------
// 描述:定义辅助宏
//--------------------------------------------------
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
constexpr auto WINDOW_TITLE = L"准备好,我来帮你了朋友!"; // 新版本中使用constexpr比define更安全.
//----------------【全局函数声明部分】----------------
// 描述:全局函数声明.
// 上为窗口过程函数
//--------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
//----------------【WinMain( )函数】-----------------
// 描述:程序主入口
//--------------------------------------------------
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) // 需要将 _In_, _In_opt_ 依照函数原型加上,否则会报警告. 为啥呢?
{
// 四部曲
// 1. Design (Oh 11 paras!)
WNDCLASSEX wndClass = { 0 }; // 定义一个窗口类
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = (HICON)::LoadImage(NULL, L"favicon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // 试了下没有RED\BLUE 的brush,qwq
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";
// 2. Register
if (!RegisterClassEx(&wndClass)) return -1;
// 3. Create
HWND hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOW_TITLE,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
// 4. Move, show and update
MoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 5. msg loop
MSG msg = { 0 };
while (msg.message != WM_QUIT);
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// 6. unregister
UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
ValidateRect(hwnd, NULL);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
运行效果:
总之就是一个很简单的窗体啦!
总结
第一次以笔记的形式把学习的东西记录下来。之前学过一些东西,总因为后来很久没接触逐渐忘了,重新学又需要整理很多资料重新看,不妨将学习过程写成笔记,方便将很久没用的知识捡回来,也能更好地综合我的学习内容。
标签:从零开始,窗口,函数,WINDOWS,hwnd,编程,wndClass,msg,NULL 来源: https://blog.csdn.net/qq_39561693/article/details/122780154