Skip to content

基础知识

1.创建项目

选择创建C++空项目

image-20240823094425962

在源文件中创建一个cpp文件

image-20240823094545661

写入如下内容

c++
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{

	MessageBox(NULL, "你好", "", MB_OK);
	
	return 0;
}

编制运行之后,恭喜,没运行成功

2.项目设置

没运行成功是因为,平常的控制台程序入口函数都是main(),而到了这边则变成了WinMain(),还有就是字符编码的问题。目前先照着配置

2.1 设置窗口程序

在解决方案管理器中右键我们的项目,选择属性

image-20240823095043769

配置属性->连接器->系统,我们发现子系统为控制台程序,我们需要改为窗口

改完点击应用,确定

image-20240823095025422

2.2 设置字符编码

上述设置修改完之后,若我们把代码改成如下这样,也可以运行成功

也就是把所有字符串前面加上L,变成宽字符

c++
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{

	MessageBox(NULL, L"你好", L"", MB_OK);
	
	return 0;
}

运行结果生成了一个弹框

image-20240823095451250

这是因为目前项目的字符集为Unicode,一般我们项目开发不使用Unicode,而是使用多字节字符

同样解决方案管理器右键项目,选择属性

配置属性->高级,将字符集由Unicode改为多字节

之后点击应用,确定

image-20240823095636960

之后再把之前写的L给去调,就ok了

c++
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{

	MessageBox(NULL, "你好", "", MB_OK);
	
	return 0;
}

image-20240823095821477

3.第一个win窗口

3.1 创建窗口步骤

创建一个窗口要经历以下步骤

  • 定义WinMain函数
  • 定义窗口处理函数(自定义,处理消息)
  • 注册窗口类(向操作系统写入一些数据
  • 创建窗口(内存中创建窗口)
  • 显示窗口(绘制窗口的图像
  • 消息循环(获取/翻译/派发消息)
  • 消息处理

以下代码便可以创建一个win窗口,但目前只是一个空白的窗口

c++
#include <windows.h>
// 定义窗口处理函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_PAINT:
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
            EndPaint(hWnd, &ps);
        return 0;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{
	// 注册窗口类
	WNDCLASS wc = { };
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.lpszClassName = "MainWindow";
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	RegisterClass(&wc);

    // 创建窗口
	HWND hwnd = CreateWindowEx(
		0,
		"MainWindow",
		"第一个窗口程序",
		WS_OVERLAPPEDWINDOW,
		0,
		0,
		800,
		500,
		NULL,
		NULL,
		hInstance,
		NULL
	);
	
    // 显示窗口
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
    // 消息循环
	MSG msg = { };
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}

image-20240823140915553

3.2 入口函数

在Win32开发中,入口函数为WinMain()

c++
int WINAPI WinMain(
	HINSTANCE hInstance,			
    HINSTANCE hPrevInstance,		
    LPSTR IpCmdLine,				
    int nCmdShow					
);

参数

  • hInstance:当前程序的实例句柄,可以理解为每个程序的id
  • hPrevInstance:应用程序的先事例的句柄。对于同一个程序打开两次,出现两个窗口第一次打开的窗口就是先前实例的窗口。对于一个32的位程序,该参数总为NULL。
  • IpCmdLine:命令行参数字符串
  • nCmdShow:指明窗口如何显示。该参数可以是下列值之一
    • SW_HIDE:隐藏窗口并且激活另外一个窗口。
    • SW_MINIMIZE:最小化指定的窗口,并且激活在系统表中的顶层窗口。
    • SW_RESTORE:激活并显示窗口。如果窗口已经最小化或最大化,系统将以恢复到原来的尺寸和位置显示窗口(与SW_SHOWNORMAL相同)。
    • SW_SHOW:激活一个窗口并以原来的尺寸和位置显示窗口。
    • SW_SHOWMAXIMIZED:激活窗口并且将其最大化。
    • SW_SHOWMINIMIZED:激活窗口并将其目标化。
    • SW_SHOWMINNOACTIVE:将一个窗口显示为图标。激活窗口维持活动状态。
    • SW_SHOWNA:以窗口的当前状态显示窗口。激活窗口保持活动状态。
    • SW_SHOWNOACTIVATE:以窗口的最近一次的尺寸和位置显示窗口。激活窗口维持激活状态。
    • SW_SHOWNORMAL:激活并显示窗口。如果窗口最大化或最小化,系统将其恢复到原来的尺寸和位置(与SW_RESTORE相同)。

返回值:如果函数成功,当它接收到一个WM_QUIT消息时就中止,函数应该返回在该消息的wParam参数的退出值。如果函数在进入消息循环时退出,应该返回零。

了解一下

WinMain的本质还是main,只不过main经过了封装

WINAPI:我们右键WINAPI点击转到定义,会发现如下宏

c++
#define WINAPI __stdcall

相同的,WINAPI和CALLBACK的宏一样

c++
#define CALLBACK __stdcall

这是为了定义函数如何从调用方接收参数,大致了解就行

数据类型

我们发现函数中每个参数的类型都是一个不认识的大写单词,可以右键该类型,点击转到定义

会发现这些类型都是一些基本数据定义的宏

后面会详细说明

WinMain批注不一致

这是因为我们写的WinMain函数和头文件里声明的不一样,右键WinMain转到定义我们可以看到WinMain的声明如下,将参数裂变换成如下格式,vs就不会显示批注不一致了

c++
WinMain (
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPSTR lpCmdLine,
    _In_ int nShowCmd
);

我们发现在每个参数前添加了_in_或者_In_opt_

这个叫做Microsoft源码注释语言,简称SAL(source-code annotation language),本质是辅助我们避免使用野指针

以下是四个基本参数注释

类别参数注释说明
输入到调用的函数_In_数据传递给调用的函数,并被视为只读
输入到调用的函数并输出到调用方_Inout_可用数据传递到函数中,并可能进行修改
输出到调用方_Out_调用方只为调用的函数执行写入操作提供空间。 调用的函数将数据写入该空间。
指针输出到调用方_Outptr_与“输出到调用方”一样。 调用的函数返回的值是一个指针。

有个参数为可选参数(可以传NULL),有的为必传参数(不可为NULL),为了区分,可以在以上四个的基础上添加_opt_

参数不允许为NULL参数可以为NULL
输入到调用的函数_In__In_opt_
输入到调用的函数并输出到调用方_Inout__Inout_opt_
输出到调用方_Out__Out_opt_
指针输出到调用方_Outptr__Outptr_opt_

当参数不允许为NULL,但我们传了NULL时,会引起程序的崩溃

微软官方网站给出详细说明:了解SAL

3.3 窗口类

3.3.1 定义窗口类

通过WNDCLASS或者WNDCLASSEX定义一个窗口类的结构体,通过给其中各个属性赋值,来确定后面要注册的窗口类的各种配置,WNDCLASSEXEXextra(额外的),也就是比WNDCLASS多几个成员

c++
typedef struct _WNDCLASS {
    int cbClsExtra;
    int cbWndExtra;
    UINT style;
    WNDPROC lpfnWndProc;
    HANDLE hInstance;
    HICON hIcon;
    HCURSOR hCursor;
    HBRUSH hbrBackground;
    LPCTSTR lpszMenuName;
    LPCTSTR lpszClassName;
} WNDCLASS;

typedef struct _WNDCLASSEX {
    int cbClsExtra;
    int cbWndExtra;
    UINT cbSize;
    UINT style;
    WNDPROC lpfnWndProc;
    HANDLE hInstance;
    HICON hIcon;
    HCURSOR hCursor;
    HBRUSH hbrBackground;
    LPCTSTR lpszMenuName;
    LPCTSTR lpszClassName;
    HICON hIconSm;
} WNDCLASSEX;
  • cbClsExtra:窗口类额外空间大小,我们可以自定义使用拓展空间,若无此需求则设置0

  • cbWndExtra:窗口的额外空间大小,我们可以自定义使用拓展空间,若无此需求则设置0

  • hbrBackground:设置窗口的背景颜色,类背景画笔的手柄。此成员可以是用于绘制背景的画笔的手柄,也可以是颜色值。颜色值必须是以下标准系统颜色之一(必须将值 1 添加到所选颜色中)。如果给定了颜色值,则必须将其转换为以下 HBRUSH 类型之一:

    • COLOR_ACTIVEBORDER
    • COLOR_ACTIVECAPTION
    • COLOR_APPWORKSPACE
    • COLOR_BACKGROUND
    • COLOR_BTNFACE
    • COLOR_BTNSHADOW
    • COLOR_BTNTEXT
    • COLOR_CAPTIONTEXT
    • COLOR_GRAYTEXT
    • COLOR_HIGHLIGHT
    • COLOR_HIGHLIGHTTEXT
    • COLOR_INACTIVEBORDER
    • COLOR_INACTIVECAPTION
    • COLOR_MENU
    • COLOR_MENUTEXT
    • COLOR_SCROLLBAR
    • COLOR_WINDOW
    • COLOR_WINDOWFRAME
    • COLOR_WINDOWTEXT
  • hCursor:窗口的鼠标样式,NULL为默认鼠标样式

  • hIcon:窗口左上角的图标,如果此成员为 NULL,则系统将提供默认图标。

    • 可以使用LoadIcon()或者LoadImage()设置

      c++
      wc.hIcon = LoadIcon(NULL, IDI_QUESTION);
  • hIconSm:状态栏中的图标,如果此成员为 NULL,则系统将在 hIcon 成员指定的图标资源中搜索要用作小图标的适当大小的图标。

  • hInstance:标识此类的窗口过程所在的实例,一般传入WinMain参数中的hInstance

  • lpfnWndProc:函数是一个应用程序定义的回调函数,用于处理发送到窗口的消息

  • lpszClassName:窗口类名,在创建相应窗口时要对应

  • lpszMenuName:菜单名

  • style:指定类样式。可以使用按位OR(|)运算符组合样式。该成员可以是以下值的任意组合:

    • CS_HREDRAW:如果移动或尺寸调整改变了客户区域的高度,则重绘整个窗口。
    • CS_VREDRAW: 如果移动或尺寸调整改变了客户区域的宽度,则重绘整个窗口。
    • CS_DBLCLKS: 当用户该窗口中双击鼠标时,将双击消息发送到窗口过程。
    • CS_GLOBALCLASS:指示窗口类是应用程序全局类。 有关详细信息,请参阅 关于窗口类的“应用程序全局类”部分。
    • CS_NOCLOSE:在窗口菜单上禁用 “关闭 ”。
    • CS_OWNDC:为 类中的每个窗口分配唯一的设备上下文。
    • CS_PARENTDC:将子窗口的剪裁矩形设置为父窗口的剪裁矩形,以便子窗口可以在父窗口上绘制。 具有 CS_PARENTDC 样式位的窗口从系统的设备上下文缓存接收常规设备上下文。 它不会为子级提供父级的设备上下文或设备上下文设置。 指定 CS_PARENTDC 可增强应用程序的性能。
    • CS_CLASSDC:分配一个设备上下文,以便类中的所有窗口共享。 由于窗口类特定于进程,因此应用程序的多个线程可以创建同一类的窗口。 线程还可以尝试同时使用设备上下文。 发生这种情况时,系统仅允许一个线程成功完成其绘制操作。
    • CS_SAVEBITS:保存此类窗口遮盖的屏幕图像部分作为位图。 删除窗口时,系统会使用保存的位图还原屏幕图像,包括被遮盖的其他窗口。 因此,如果位图使用的内存尚未丢弃,并且其他屏幕操作未使存储的图像失效,则系统不会将 WM_PAINT 消息发送到被遮盖的窗口。 此样式适用于小型窗口 (例如菜单或对话框) ,这些菜单或对话框在发生其他屏幕活动之前会短暂显示,然后删除。 此样式会增加显示窗口所需的时间,因为系统必须先分配内存来存储位图。
    • CS_BYTEALIGNCLIENT:将窗口的工作区与 x 方向) 的字节边界 (对齐。 此样式会影响窗口的宽度及其在显示器上的水平位置。
    • CS_BYTEALIGNWINDOW:使窗口在字节边界 (沿 x 方向) 对齐。 此样式会影响窗口的宽度及其在显示器上的水平位置。
  • cbSize:此窗口类结构的大小(以字节为单位)。 将此成员设置为 sizeof(WNDCLASSEX)。 在调用GetClassInfoEx 函数之前,请务必设置此成员。

3.3.2 注册窗口

使用RegisterClass注册WNDCLASS窗口类的窗口

使用RegisterClassEx注册WNDCLASSEX窗口类的窗口

c++
ATOM RegisterClass(
	CONST WNDCLASS *lpWndClass	//具有类数据的结构地址
);

ATOM RegisterClassEx(
	CONST WNDCLASSEX *lpWndClass	//具有类数据的结构地址
);

返回值ATOM(原子):也就是无符号短整型

c++
typedef WORD ATOM; 
typedef unsigned short WORD;
  • 如果函数成功,则返回值是唯一标识正在注册的类的原子。
  • 如果函数失败,返回值为零。要获取扩展错误信息,请调用GetLastError.
c++
ATOM res = RegisterClass(&wc);
if (res == NULL) {
	// 注册失败
}

3.4 创建窗口

使用CreateWindow()CreateWindowEx()创建窗口,CreateWindowEx()CreateWindow()多一个参数dwExStyle

c++
HWND CreateWindow(
  [in, optional] LPCSTR    lpClassName,
  [in, optional] LPCSTR    lpWindowName,
  [in]           DWORD     dwStyle,
  [in]           int       x,
  [in]           int       y,
  [in]           int       nWidth,
  [in]           int       nHeight,
  [in, optional] HWND      hWndParent,
  [in, optional] HMENU     hMenu,
  [in, optional] HINSTANCE hInstance,
  [in, optional] LPVOID    lpParam
);

HWND CreateWindowEx(
  [in]           DWORD     dwExStyle,
  [in, optional] LPCSTR    lpClassName,
  [in, optional] LPCSTR    lpWindowName,
  [in]           DWORD     dwStyle,
  [in]           int       X,
  [in]           int       Y,
  [in]           int       nWidth,
  [in]           int       nHeight,
  [in, optional] HWND      hWndParent,
  [in, optional] HMENU     hMenu,
  [in, optional] HINSTANCE hInstance,
  [in, optional] LPVOID    lpParam
);

参数详细说明

  • dwExStyle:正在创建的窗口的扩展窗口样式,无特殊需求设置0,详情,以下展示部分

    • WS_EX_ACCEPTFILES:窗口接受拖放文件
    • WS_EX_TOPMOST:窗口置顶
  • lpClassName:指定窗口类名,要与RegisterClass注册的窗口类的类名相同

  • lpWindowName:窗口标题

  • dwStyle:正在创建的窗口的样式, 此参数可以是 窗口样式值以及“备注”部分中指示的控件样式的组合。

  • X:窗口的初始水平位置

  • Y:窗口的初始垂直位置

  • nWidth:窗口的宽度(以设备单位为单位)

  • nHeight:窗口的高度(以设备单位为单位)

  • hWndParent:正在创建的窗口的父窗口或所有者窗口的句柄,若无则为NULL

  • hMenu:菜单句柄

  • hInstance:要与窗口关联的模块实例的句柄。

  • lpParam:指向要通过 CREATESTRUCT 结构传递到窗口的值的指针,

返回值:HWND,窗口句柄

  • 如果函数成功,则返回值是新窗口的句柄。
  • 如果函数失败,则返回值为 NULL

注意:可以在 lpClassName 参数中指定以下预定义控件类。 请注意可在 dwStyle 参数中使用的相应控件样式。

含义
BUTTON指定一个小矩形子窗口,该窗口表示用户可以单击以将其打开或关闭的按钮。 按钮控件可以单独使用,也可以成组使用,并且可以不带文本标记或显示。 当用户单击按钮控件时,按钮控件通常会更改外观。 有关详细信息,请参阅 按钮。有关可在 dwStyle 参数中指定的按钮样式表,请参阅 按钮样式
COMBOBOX指定由列表框和类似于编辑控件的选择字段组成的控件。 使用此样式时,应用程序应随时显示列表框或启用下拉列表框。 如果列表框可见,在选择字段中键入字符会突出显示与键入的字符匹配的第一个列表框条目。 相反,选择列表框中的项会在选择字段中显示所选文本。 有关详细信息,请参阅 组合框。有关可以在 dwStyle 参数中指定的组合框样式表,请参阅 组合框样式
EDIT指定一个矩形子窗口,用户可以从键盘键入文本。 用户选择控件,并通过单击控件或通过按 TAB 键移动到控件来为它提供键盘焦点。 编辑控件显示闪烁的插入点时,用户可以键入文本;使用鼠标移动光标,选择要替换的字符,或定位光标以插入字符:或使用 键删除字符。 有关详细信息,请参阅 编辑控件。有关可在 dwStyle 参数中指定的编辑控件样式的表,请参阅 编辑控件样式
LISTBOX指定字符串列表。 每当应用程序必须提供用户可从中选择的名称列表(如文件名)时,请指定此控件。 用户可以通过单击来选择字符串。 突出显示所选字符串,并将通知消息传递到父窗口。 有关详细信息,请参阅 列表框。有关可在 dwStyle 参数中指定的列表框样式表,请参阅 列表框样式
MDICLIENT指定 MDI 客户端窗口。 此窗口接收控制 MDI 应用程序的子窗口的消息。 建议的样式位是 WS_CLIPCHILDRENWS_CHILD。 指定 WS_HSCROLLWS_VSCROLL 样式以创建允许用户将 MDI 子窗口滚动到视图中的 MDI 客户端窗口。 有关详细信息,请参阅 多文档接口
RichEdit指定 Microsoft Rich Edit 1.0 控件。 此窗口允许用户使用字符和段落格式查看和编辑文本,并且可以将嵌入式组件对象模型 (COM) 对象。 有关详细信息,请参阅 Rich Edit 控件。有关可以在 dwStyle 参数中指定的丰富编辑控件样式的表,请参阅 Rich Edit 控件样式
RICHEDIT_CLASS指定 Microsoft Rich Edit 2.0 控件。 此控件允许用户使用字符和段落格式查看和编辑文本,并且可以包含嵌入的 COM 对象。 有关详细信息,请参阅 Rich Edit 控件。有关可以在 dwStyle 参数中指定的丰富编辑控件样式的表,请参阅 Rich Edit 控件样式
SCROLLBAR指定一个矩形,该矩形包含一个滚动框,两端都有方向箭头。 每当用户单击控件时,滚动条就会向其父窗口发送通知消息。 如有必要,父窗口负责更新滚动框的位置。 有关详细信息,请参阅 滚动条。有关可在 dwStyle 参数中指定的滚动条控件样式的表,请参阅 滚动条控件样式
STATIC指定用于标记、框或分隔其他控件的简单文本字段、框或矩形。 静态控件不采用任何输入,也不提供任何输出。 有关详细信息,请参阅 静态控件。有关可在 dwStyle 参数中指定的静态控件样式表,请参阅 静态控件样式

3.5 显示窗口

通过ShowWindow()函数让窗口显示

c++
BOOL ShowWindow(
	HWND hwnd,
    int nCmdShow
)

参数:

  • hwnd:需要显示窗口的句柄。
  • nCmdShow:控制窗口的显示方式,如果启动应用程序的程序提供 STARTUPINFO 结构,则应用程序首次调用 ShowWindow 时将忽略此参数。 否则,首次调用 ShowWindow 时,该值应为 WinMain 函数在其 nCmdShow 参数中获取的值。 在后续调用中,此参数可以是以下值之一。
含义
SW_HIDE 0隐藏窗口并激活另一个窗口。
SW_SHOWNORMAL SW_NORMAL 1激活并显示窗口。 如果窗口最小化、最大化或排列,系统会将其还原到其原始大小和位置。 应用程序应在首次显示窗口时指定此标志。
SW_SHOWMINIMIZED 2激活窗口并将其显示为最小化窗口。
SW_SHOWMAXIMIZED SW_MAXIMIZE 3激活窗口并显示最大化的窗口。
SW_SHOWNOACTIVATE 4以最近的大小和位置显示窗口。 此值类似于 SW_SHOWNORMAL,只是窗口未激活。
SW_SHOW 5激活窗口并以当前大小和位置显示窗口。
SW_MINIMIZE 6最小化指定的窗口,并按 Z 顺序激活下一个顶级窗口。
SW_SHOWMINNOACTIVE 7将窗口显示为最小化窗口。 此值类似于 SW_SHOWMINIMIZED,但窗口未激活。
SW_SHOWNA 8以当前大小和位置显示窗口。 此值类似于 SW_SHOW,只是窗口未激活。
SW_RESTORE 9激活并显示窗口。 如果窗口最小化、最大化或排列,系统会将其还原到其原始大小和位置。 还原最小化窗口时,应用程序应指定此标志。
SW_SHOWDEFAULT 10根据启动应用程序的程序传递给 CreateProcess 函数的 STARTUPINFO 结构中指定的**SW_**值设置显示状态。
SW_FORCEMINIMIZE 11最小化窗口,即使拥有窗口的线程没有响应。 仅当最小化不同线程的窗口时,才应使用此标志。

返回值:

  • 类型: BOOL
  • 如果窗口以前可见,则返回值为非零值。
  • 如果以前隐藏窗口,则返回值为零。

使用UpdateWindow()更新窗口

c++
BOOL UpdateWindow(
  HWND hWnd
);

参数:

  • hWnd:要更新的窗口的句柄。

返回值:

  • 如果该函数成功,则返回值为非零值。
  • 如果函数失败,则返回值为零。

4.消息机制

当我们鼠标点击,或者键盘输入时,系统会把这些动作变成一个个消息,推送到一个消息队列中,当消息被处理完成,就会被移除

4.1 消息循环

在消息队列里,消息环调用GetMessage()方法,如果你的消息队列空了,那么你的程序将会停止然后等待消息,相当于挂起的状态

c++
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

当系统中有消息时,GetMessage()会获取消息,然后通过TranslateMessage()把消息翻译成字符,方便识别,最后通过DispatchMessage()把消息发送给消息处理函数

4.1.1 GetMessage()

GetMessage函数从调用线程的消息队列中检索消息,并将其放在指定的结构中

c++
BOOL GetMessage(
    LPMSG msg,	
    HWND hWnd,	
    UINT wMsgFilterMin,
    UINT wMsgFilterMax	
);

参数

  • msg:指向从线程的消息队列接收消息信息的MSG结构。

    • MSG:消息信息的结构体
    c++
    typedef struct tagMSG { 
        HWND hwnd;					// 标识窗口过程接收到消息的窗口。
        UINT message;				// 指定消息号
        WPARAM wParam;				// 指定有关该消息的附加信息。确切的含义取决于信息成员的值。
        LPARAM lParam;				// 指定有关该消息的附加信息。确切的含义取决于信息成员的值。
        DWORD time;					// 指定消息发布的时间。
        POINT pt;					// 指定邮件发布时的屏幕坐标中的光标位置。
    } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
  • hWnd:标识要检索其消息的窗口。

    • 当传入一个确切的窗口句柄时,GetMessage()只检索该窗口的消息
    • 当传入NULL时,GetMessage()检测该程序下所有窗口的消息
  • wMsgFilterMin:指定要检索的最低消息值的整数值。

  • wMsgFilterMax:指定要检索的最高消息值的整数值。

返回值:BOOL,应用程序通常使用返回值来确定是否结束主消息循环并退出程序。

4.1.2 TranslateMessage()

将虚拟键消息转换为字符消息。字符消息被发布到调用线程的消息队列中,以便下次线程调用GetMessagePeekMessage功能时被读取。

c++
BOOL TranslateMessage(
	CONST MSG *LPMSG	//具有消息结构的地址
);

参数:

  • 指向MSG结构,其中包含使用GetMessagePeekMessage函数从调用线程的消息队列中检索的消息信息。

返回值:

  • 如果消息被翻译(即,字符消息被发布到线程的消息队列),返回值不为零。
  • 如果消息未被翻译(即,一个字符消息没有发布到线程的消息队列),返回值为零。

备注:

  • TranslateMessage功能不会修改*【LPMSG】*参数指向的消息。

4.1.3 DispatchMessage()

DispatchMessage功能将消息分派给窗口过程。它通常用于调度由GetMessage函数检索到的消息。

c++
LONG DispatchMessage(
	CONST MSG* LPMSG	//指向结构的消息指针
);

参数:

  • LPMSG:指向包含该消息的MSG结构。

返回值:

  • 返回值指定窗口过程返回的值。虽然其含义取决于要分派的消息,但通常忽略返回值。

备注:MSG结构必须包含有效的消息值。如果*【LPMSG】参数指向WM_TIMER消息,并且WM_TIMER消息的【lParam的】参数不为NULL,则【lParam的】*指向调用的函数,而不是窗口过程。

4.2 消息处理函数

在我们注册窗口类时,有这么一步

c++
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	// ...
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int WinMain(){
    WNDCLASS wc = { };
	wc.lpfnWndProc = WindowProc;
}

这个WindowProc()就是窗口处理函数,当程序通过GetMessage()获取消息,并通过DispatchMessage()分发消息把MSG结构的消息传过来,就是由此窗口处理函数,通过接收的消息,从而来处理自己的逻辑

4.2.1 WindowProc()

c++
LRESULT CALLBACK WindowProc(
    HWND hWnd, 
    UINT uMsg, 
    WPARAM wParam, 
    LPARAM lParam
);

参数

  • hWnd:标识窗口。哪个窗口发送的消息,就是哪个窗口的句柄
  • uMsg:指定消息的ID
  • wParam:指定附加消息信息。该参数的内容取决于*【uMsg】*参数的值。
  • lParam:指定附加消息信息。该参数的内容取决于*【uMsg】*参数的值。

uMsg的值不同时,wParam和lParam可能会有不同的值

c++
typedef UINT_PTR  WPARAM;
typedef unsigned __int64 UINT_PTR, *PUINT_PTR;

typedef LONG_PTR  LPARAM;
typedef __int64 LONG_PTR, *PLONG_PTR;

返回值:返回值是消息处理的结果,取决于发送的消息。

  • 当uMsg具有不同值时,我们可以返回不同的值
  • 默认返回DefWindowProc(hWnd, uMsg, wParam, lParam)DefWindowProc()会自动处理很多默认的消息
c++
typedef LONG_PTR  LRESULT;
typedef __int64 LONG_PTR, *PLONG_PTR;

4.2.2 退出消息

当我们关闭一个窗口时,系统会先发送WM_CLOSE消息,然后是WM_DESTORY消息

DefWindowProc()函数会自动帮我们处理窗口关闭的消息,但不会帮我们处理退出程序

我们需要使用PostQuitMessage()函数来退出程序

c++
VOID PostQuitMessage(
	INT  nExitCode 	//退出代码
);

参数:

  • nExitCode:指定应用程序退出代码。该值用作WM_QUIT消息的*【wParam中】*参数。

5.控制台输出

窗口应用程序中,不再会有终端窗口,所以cout已不再适用

使用OutputDebugString()可在WIn32程序中的控制台打印信息

c++
VOID OutputDebugString(
	LPCTSTR lpOutputString	//指向要显示的字符串的指针
);

MIT Licensed