Win32:第一个窗口程序-注册窗口类(Part.2)

发布于:2024-06-19 ⋅ 阅读:(121) ⋅ 点赞:(0)

在part 1中我们阐述了窗口模板程序中的相关宏、全局函数和相关函数声明后我们Part2就来说一下part 1中声明的一个函数MyRegisterClass注册窗口类函数(函数中也使用到了定义的一些全局变量),为什么要注册窗口类在part 1中已经阐述过了,这边就不再说了,那么这个时候直接上代码:

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
​
    wcex.cbSize = sizeof(WNDCLASSEX);
​
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
​
    return RegisterClassExW(&wcex);
}

以下代码片段定义了一个函数 MyRegisterClass,用于注册窗口类;窗口类包含了一组与窗口相关的属性,并将这些属性与窗口过程关联起来。在 Windows 程序设计中,窗口类注册是创建窗口的第一步。以下是对代码的详细解释:

函数签名和参数
ATOM MyRegisterClass(HINSTANCE hInstance)

ATOM:函数返回一个 ATOM 类型值,这是一种 Windows 类型,用于标识已注册的窗口类。

HINSTANCE hInstance:这是一个实例句柄,标识应用程序的实例;它通常在 WinMain 函数中传递给 MyRegisterClass

此处跟我再part1中函数声明解释中写的一致。

函数实现
WNDCLASSEXW wcex;

定义一个 WNDCLASSEXW 结构体变量 wcex,用于描述窗口类的属性;通过f12我们可以找到WNDCLASSEXW结构体的具体定义:

typedef struct tagWNDCLASSEXW {
    UINT        cbSize;
    /* Win 3.x */
    UINT        style;
    WNDPROC     lpfnWndProc;
    int         cbClsExtra;
    int         cbWndExtra;
    HINSTANCE   hInstance;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCWSTR     lpszMenuName;
    LPCWSTR     lpszClassName;
    /* Win 4.0 */
    HICON       hIconSm;
} WNDCLASSEXW, *PWNDCLASSEXW, NEAR *NPWNDCLASSEXW, FAR *LPWNDCLASSEXW;

接下去就是对刚才定义的结构体变量wcex中的成员变量进行赋值。

wcex.cbSize = sizeof(WNDCLASSEX);

这行代码在 MyRegisterClass 函数中设置 WNDCLASSEX 结构体的 cbSize 成员为 WNDCLASSEX 结构体的大小。这是注册窗口类时必需的一步,确保 Windows 能正确处理这个结构体。

cbSize: 这是 WNDCLASSEXW 结构体中的一个成员,表示该结构体的大小(以字节为单位)。这是一个 UINT 类型的变量。

sizeof(WNDCLASSEX): 这是一个运算符,用于计算 WNDCLASSEX 结构体的大小(以字节为单位);WNDCLASSEXWNDCLASSEXW 的别名,用于处理宽字符(Unicode)版本的窗口类信息。

为什么需要设置cbsize

在 Windows API 中,许多函数需要结构体的大小作为参数,以确保在不同版本的 Windows 中能够正确处理数据结构。设置 cbSize 可以让 API 函数知道传递给它的结构体的确切大小,从而避免因版本或编译器差异导致的内存错误或兼容性问题。
设置窗口类的属性
①定义了窗口类的重绘行为
wcex.style = CS_HREDRAW | CS_VREDRAW;

这行代码设置了 WNDCLASSEX 结构体的 style 成员,指定了窗口类的样式,具体来说,CS_HREDRAWCS_VREDRAW 是两个常量,定义了窗口类的重绘行为。

常量解释
  • CS_HREDRAW:水平重绘,当窗口的宽度改变时,窗口将被完全重绘。

  • CS_VREDRAW:垂直重绘,当窗口的高度改变时,窗口将被完全重绘。

为什么要使用这些样式?

当窗口的大小发生变化时(例如用户调整窗口大小),CS_HREDRAWCS_VREDRAW 确保窗口的内容被正确地重新绘制。这对于需要动态调整窗口内容的应用程序非常重要。

②指定窗口过程
wcex.lpfnWndProc = WndProc;

这行代码将 WNDCLASSEX 结构体的 lpfnWndProc 成员设置为 WndProc 函数的指针。lpfnWndProc 是一个指向窗口过程(Window Procedure)函数的指针,窗口过程是处理所有窗口消息的核心函数。窗口过程是一个回调函数,用于处理来自 Windows 操作系统的各种消息。(具体实现细节在part4进行刨析,到时候结合食用)

③分配窗口类或实例额外内存
wcex.cbClsExtra     = 0;
wcex.cbWndExtra     = 0;

这两行代码在 WNDCLASSEX 结构体中设置了 cbClsExtracbWndExtra 成员,分别为类和窗口分配的额外内存字节数。将它们设置为 0 表示不需要为窗口类或窗口实例分配额外的内存。

cbClsExtra:允许为窗口类附加额外的类级别信息,这些信息可以在窗口类的所有实例之间共享;通常为 0,表示不需要分配额外的内存。

cbWndExtra:允许为每个窗口实例附加额外的实例级别信息,这些信息只能在该窗口实例中使用;通常也0,表示不需要分配额外的内存。

④将成员设置传递至程序实例句柄
wcex.hInstance = hInstance;

这行代码将 WNDCLASSEX 结构体的 hInstance 成员设置为 hInstance,即传递给 MyRegisterClass 函数的实例句柄;hInstance` 是应用程序实例的句柄,用于标识当前运行的应用程序实例。

为什么需要设置 hInstance

设置 wcex.hInstance = hInstance; 告诉 Windows 操作系统这个窗口类是属于哪个应用程序实例的。这样,操作系统可以正确地从这个实例加载资源,如图标、光标、菜单等。

⑤设置默认图标
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));

这行代码将 WNDCLASSEX 结构体的 hIcon 成员设置为一个图标句柄,这个图标将用作窗口类的默认图标。以下是相关代码的解释:

1)hIcon变量
类型:HICON
作用:指定窗口类的默认图标。
显示位置:用于窗口的标题栏和任务切换界面(如 Alt+Tab 切换窗口时显示的图标)。
2)LoadIcon函数

函数原型:

HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);

相关参数:

hInstance:应用程序实例句柄,用于标识从哪个实例加载资源。
lpIconName:图标资源的标识符,可以是资源标识符或资源名称。
3)MAKEINTRESOURCE 宏
作用:将整数资源标识符转换为资源名称(LPCTSTR 类型)。
4)IDI_WINDOWSPROJECT1资源标识符

IDI_WINDOWSPROJECT1 是一个资源标识符,用于标识项目中定义的图标资源。在 Windows 应用程序中,资源通常包括图标、光标、字符串、对话框模板等,这些资源可以在资源脚本文件(通常是 .rc 文件)中定义和引用。

总结:

最后我们可以知道这行代码的意思是从当前应用程序实例中加载标识符为 IDI_WINDOWSPROJECT1 的图标,并将其设置为窗口类的默认图标。

若是要修改默认图标则需要打开项目中对应的rc文件;

右击文件->打开方式->源代码(文本)编辑器

根据源码中设置的资源标识符(我的文件标识符为IDI_WINDOWSPROJECT1)找到如下内容:

我们可以将ICON后的内容指定为我们要设置的图标的路径(要将图片放在项目文件夹中)

IDI_WINDOWSPROJECT1 ICON "path_to_existing_icon.ico"

⑥设置光标句柄
 wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);

这行代码将 WNDCLASSEX 结构体的 hCursor 成员设置为一个光标句柄,该光标将用作窗口类的默认光标。

hCursor成员:

类型:HCURSOR
作用:指定窗口类的默认光标。
显示位置:当鼠标指针位于窗口客户端区域时显示的光标。

LoadCursor 函数:

函数原型:

HCURSOR LoadCursor(HINSTANCE hInstance, LPCTSTR lpCursorName);

参数:

hInstance:应用程序实例句柄。如果加载的是标准光标,则设置为 nullptr。
lpCursorName:光标资源的名称或标识符。如果加载的是标准光标,可以使用预定义的光标标识符。

传入参数:

nullptr:表示加载标准光标,而不是自定义光标资源。
IDC_ARROW:预定义的光标标识符,表示标准箭头光标。

通过调用 LoadCursor 函数并传入 nullptrIDC_ARROW,可以加载系统提供的标准箭头光标,并将其句柄赋值给 wcex.hCursor

⑦设置窗口背景
wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);

这行代码设置了窗口类的背景刷,使得窗口在创建时具有特定的背景颜色;以下是详细解释:

hbrBackground成员变量:

类型:HBRUSH
作用:指定窗口的背景刷,当窗口需要重绘背景时,系统会使用这个刷子填充窗口的背景区域。
COLOR_WINDOW
类型:系统颜色常量
作用:表示系统窗口的默认背景颜色;这个颜色通常是一个淡灰色,具体颜色根据系统主题或用户设置可能会有所不同。
COLOR_WINDOW + 1
在 Windows API 中,系统颜色常量(如 COLOR_WINDOW)是从 1 开始的整数索引,用于标识各种系统颜色。为了将这些整数索引转换为可以使用的刷子句柄,通常需要加 1。
具体地说,COLOR_WINDOW 表示一个系统颜色索引值。
COLOR_WINDOW + 1 是一个特殊的转换,使得可以使用这个索引值作为背景刷。

(HBRUSH)(COLOR_WINDOW + 1):通过这种方式将系统颜色索引转换为一个 HBRUSH 类型的刷子句柄,使其可以赋值给 wcex.hbrBackground

总结:

通过设置 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);我们可以将窗口的背景刷设为系统默认的窗口背景颜色,当窗口需要重绘背景时,系统将使用这个刷子填充背景区域。这可以确保窗口具有一致的外观,符合用户的系统主题和设置。

⑧设置菜单名
 wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);

这行代码设置了窗口类的菜单名称,使得窗口可以包含一个菜单。

lpszMenuName成员:

类型:LPCWSTR
作用:指定与窗口类关联的菜单资源名称,如果窗口类包含菜单,该成员指向菜单资源的名称或标识符。

MAKEINTRESOURCEW是一个宏,用于将整数资源标识符转换为字符串资源标识符,与⑤一致;其原型为:

LPWSTR MAKEINTRESOURCEW(WORD wInteger);

参数wInteger为资源标识符的整数值,转换后的字符串资源标识符,类型为 LPWSTR

通过设置 wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);我们将窗口类关联到一个菜单资源,标识符为 IDC_WINDOWSPROJECT1。当创建窗口时,系统会加载并使用该菜单资源,使得窗口可以包含菜单。这个设置使应用程序窗口能够具备菜单栏,从而提供更多的用户交互选项。

⑨设置窗口类名称
 wcex.lpszClassName  = szWindowClass;

这行代码设置了窗口类的名称,标识该窗口类的唯一名称。

lpszClassName成员:

类型:LPCWSTR
作用:指定窗口类的名称这个名称用于在后续的窗口创建中标识该窗口类。

szWindowClass是一个全局变量,用于存储窗口类的名称。在初始化窗口类时,通过将 szWindowClass 的值赋给 wcex.lpszClassName,注册该窗口类的名称。

类型:WCHAR 数组
定义:WCHAR szWindowClass[MAX_LOADSTRING]; 是一个用于存储窗口类名称的字符数组。
作用:存储窗口类的名称字符串,这个名称在调用 CreateWindow 或 CreateWindowEx 函数时用于标识窗口类。
总结:

通过设置 wcex.lpszClassName = szWindowClass;可以为窗口类指定了一个唯一名称,这在注册和创建窗口时非常重要,因为它标识了窗口类的类型,使得系统能够正确地创建和管理该类窗口。

⑩设置小图标
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

这行代码为窗口类设置了一个小图标,用于任务栏和窗口左上角的小图标。

函数原型为:

HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);

hInstance:应用程序实例句柄。用于标识图标资源所在的模块。 lpIconName:图标资源的名称或标识符。可以是一个字符串或使用 MAKEINTRESOURCE 宏定义的整数值。

此处与hIcon成员的设置一致笔者就不再多做赘述了。

hIconSmhIcon 都是用来为窗口类指定图标的成员变量,但它们有不同的用途和显示位置

hIcon:指定窗口类的主图标。这通常是一个较大的图标,用于窗口的标题栏和任务切换界面(如 Alt+Tab 切换窗口时显示的图标)。
hIconSm:指定窗口类的小图标。这通常是一个较小的图标,用于任务栏和窗口左上角的小图标。

注册窗口类

最后,调用 RegisterClassExW 函数来注册窗口类,并返回该函数的结果。

return RegisterClassExW(&wcex);
RegisterClassExW函数原型
ATOM RegisterClassExW(const WNDCLASSEXW *lpwcx);

lpwcx:指向 WNDCLASSEX 结构的指针,包含窗口类的配置信息。

返回值

  • 成功时返回一个唯一的类原子(ATOM)值,表示已注册的窗口类。

  • 失败时返回零。可以调用 GetLastError 函数获取扩展错误信息。