个人整理的笔记,比较杂乱,可能并不是很适合阅读:)
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,就会加载默认的窗口样式:
MSG结构体
消息用MSG表示.
消息队列.
窗口创建四部曲:
窗口类的设计
窗口类的注册
窗口类的正式创建
窗口的显示与更新
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 到消息队列,进入窗口过程函数, 对窗口进行更新。
貌似和重画操作不同?肯定不同吧。是窗口的更新。
消息循环体系…是啥啊
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 体系则会一直等到消息出现才会进行其他操作。
窗口过程函数,用于处理消息。
典型的过程函数是通过一个 switch 语句,switch 变量 message 的具体内容来进行相应的操作。
default 状态通常会调用一个默认的 DefWindowProc
过程函数。
已经迫不及待要制作一个完整的窗口程序了(这些内容也是附加的)
注销操作并不是必须的,但为了程序稳定最好加上(万一出现意料之外的问题呢)
原型:
BOOL WINAPI UnregisterClass( _In_ LPCSTR lpClassName, _In_opt_ HISTANCE hInstance );
参数分别是注销的类名称以及类的应用程序的实例句柄。
照着写的代码,贴出的与书中不同,有一些修改和自己的理解,注释中给出了.
//-------------------【程序说明】------------------- // 程序名称: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; }
运行效果:
总之就是一个很简单的窗体啦!
第一次以笔记的形式把学习的东西记录下来。之前学过一些东西,总因为后来很久没接触逐渐忘了,重新学又需要整理很多资料重新看,不妨将学习过程写成笔记,方便将很久没用的知识捡回来,也能更好地综合我的学习内容。