win32-注册窗口类、创建窗口

准备

项目属性:
在这里插入图片描述
Configuration Properties ——> Linker ——> System ——> SubSystem ——> Windows.
在这里插入图片描述
Configuration Properties ——> Advanced ——> Character Set ——> Use Multi-Byte Character Set.


入口函数

win32窗口编程的入口函数是 WinMain,就像是控制台应用程序的 main()

int WINAPI WinMain(
	HINSTANCE hInstance, 
	HINSTANCE hPrevInstance, 
	LPSTR lpCmdLine, 
	int nCmdShow);

WinMain 函数是 Windows 程序的入口点之一,它用于 Win32 应用程序的主函数。它的四个参数分别是:

HINSTANCE hInstance:表示当前应用程序实例的句柄。在 Windows 中,每个正在运行的应用程序都有一个唯一的实例句柄。这个参数指示了当前应用程序的实例句柄,可以用来识别和操作应用程序的资源,如对话框、图标、菜单等。

HINSTANCE hPrevInstance:在旧版本的 Windows 中,此参数用于指示前一个应用程序实例的句柄。在现代的 Windows 版本中,它总是被设置为 NULL,因为 Windows 不再支持多个实例共存。

LPSTR lpCmdLine:是一个指向以 null 结尾的字符串的指针,其中包含了命令行参数。这些参数通常是用户通过命令行或者快捷方式传递给程序的额外参数,比如文件名、选项等。

int nCmdShow:表示应用程序窗口的显示方式。它是一个整数,指定了应用程序主窗口的初始显示状态。常见的值包括:

SW_SHOWNORMAL (1):显示窗口并激活它,窗口的大小和位置由操作系统确定。

SW_SHOWMAXIMIZED (3):显示窗口并将其最大化。

SW_SHOWMINIMIZED (2):显示窗口并将其最小化。

ShowWindow(hWnd, nCmdShow);

这些参数一起提供了程序运行所需的环境信息和用户输入的命令行参数,使得程序能够正确地初始化并显示其主窗口。


入口函数有时候写成 int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);

在某些情况下,WinMain 函数的类型可能会声明为 CALLBACK。CALLBACK 是一个宏,通常用于指示函数使用标准的 Windows 调用约定。这种情况通常发生在开发 Windows 应用程序时,特别是在使用 Win32 API 时。

CALLBACK 宏展开为 __stdcall 或 __cdecl,这取决于编译器和平台。在 Win32 API 中,大多数回调函数都使用 __stdcall 调用约定,因此将 CALLBACK 应用于 WinMain 函数是合理的,尽管它并不是必需的。

因此,int CALLBACK WinMain() 与 int WINAPI WinMain() 通常是等效的,只是使用了不同的宏来指示调用约定。

在使用 WinMain() 函数前 需要 #include <Windows.h>


窗口的创建过程

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

注册窗口类

#include <Windows.h>

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hWnd, message, wParam, lParam);     //返回默认的消息处理
}

int WINAPI WinMain(
	HINSTANCE hInstance, 
	HINSTANCE hPrevInstance, 
	LPSTR lpCmdLine, 
	int nCmdShow )
{
	//注册窗口类
	WNDCLASS wc = { 0 };     //定义一个结构体wc
	wc.cbClsExtra = 0;       //多少个字节的缓冲区,赋值0表示申请0个字节的缓冲区
	wc.cbWndExtra = 0;       //申请缓冲区,单位字节
	//以上两个成员变量都是申请多少字节的缓冲区,但是是两种不同的缓冲区
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);   //背景色为白色
	wc.hCursor = NULL;       //光标复制NULL,意思是系统默认光标
	wc.hIcon = NULL;      //图标,NULL默认图标
	wc.hInstance = hInstance;     //当前程序实例句柄
	wc.lpfnWndProc = WndProc;     //自定义窗口处理函数名字
	wc.lpszClassName = "Main";     //窗口类名字
	wc.lpszMenuName = NULL;      //NULL不要菜单
	wc.style = CS_HREDRAW | CS_VREDRAW;    //窗口的风格: 窗口的水平、垂直方向大小变化的时候重新绘制窗口
	RegisterClass(&wc);     //将以上所有赋值全部写入操作系统内核

	//在内存中创建窗口
	HWND hWnd = CreateWindow(
		"Main",      //与上面创建窗口类的名字保持一致
		"Window",    //窗口的标题(标题栏)
		WS_OVERLAPPEDWINDOW,    //窗口的风格,填这个参数会比较省心,该有的风格都有了
		100, 50, 500, 400,      //窗口的位置,屏幕的左上角坐标(0,0),(100,500)窗口相对于左上角的位置,(500,400)窗口的大小,长和宽,单位像素。
		NULL,      //父窗口是谁
		NULL,      //菜单,不要菜单置空
		hInstance,      //当前窗口的实例句柄
		NULL       //窗口创建时附加参数 
	);   //在操作系统的内部创建一块内存,保存窗口的各项信息,返回值窗口的句柄 HWND

	//显示窗口
	ShowWindow(hWnd, SW_SHOW);     //第一个参数窗口的句柄,第二个参数窗口的显示方式,可以填入 nCmdShow(命令行传入的显示参数),也可以填自己指定的值,SW_SHOW 原样显示,即上一步在内存中创建窗口的数值
	UpdateWindow(hWnd);     //更新窗口,不调用也行,微软建议调用。

	//消息循环
	MSG msg = { 0 };    //msg 结构体
	while ( GetMessage(&msg, NULL, 0, 0)) {       //GetMessage()专门负责抓取消息(从消息队列中抓取消息),抓到消息后返回值非零,消息被保存在msg结构体中,&msg引用,类似于指针
		TranslateMessage(&msg);     //翻译消息,可以区分大小写字母
		DispatchMessage(&msg);      //派发消息给消息处理函数进行处理,前面定义的 WndProc 函数。
	}

	return 0;
}

wc.cbClsExtra 和 wc.cbWndExtra 分别是窗口类结构体 WNDCLASS 中的两个成员变量,它们用于指定窗口类和窗口实例的额外字节数。

cbClsExtra:

cbClsExtra 指定了每个窗口类实例额外的字节数。这些额外的字节会分配给窗口类的实例,而不是窗口实例本身。

这些额外的字节可以用于存储与窗口类实例相关的自定义数据或信息。

通常情况下,cbClsExtra 很少被使用,除非确实需要为每个窗口类实例分配一些额外的空间。

cbWndExtra:

cbWndExtra 指定了每个窗口实例额外的字节数。这些额外的字节会分配给每个窗口实例。

这些额外的字节可以用于存储与特定窗口实例相关的自定义数据或信息。

cbWndExtra 常常被用于实现子类化或为特定窗口实例分配自定义数据。

总的来说,区别在于 cbClsExtra 分配的额外空间是针对窗口类实例的,而 cbWndExtra 分配的额外空间是针对每个窗口实例的。


以上代码运行以后,会在屏幕上出现一个窗口:
在这里插入图片描述
当点击关闭窗口后,窗口虽然消失了,但是窗口的进程并没有结束,这是因为在窗口的处理函数中并没有写关闭进程的程序,也就意味着窗口仍然在while循环中,没有执行到 return 0;。后面会有同时关闭进程的代码。


WNDCALSS 结构体

typedef struct _WNDCLASS {
	UINT style; //窗口的风格
	WNDPROC lpfnWndProc;   //窗口处理函数
	int cbClsExtra;    //窗口类的附加数据buff的大小
	int cbWndExtra;    //窗口的附加数据buff的大小
	HINSTANCE hInstance;    //当前模块的实例句柄
	HICON hIcon;     //窗口的图标句柄
	HCURSOR hCursor;    //光标的句柄
	HBRUSH hbrBackground;   //绘制窗口背景的画刷句柄
	LPCTSTR lpszMenuName;   //窗口菜单资源ID字符串
	LPCTSTR lpszClassName;   //窗口类的名字
} WNDCLASS, *PWNDCLASS;     //typedef 定义的别名

WNDCLASSEX 结构体

WNDCLASSEX 就是对 WNDCLASS 的扩充。

typedef struct tagWNDCLASSEX {
    UINT      cbSize;    //结构体的大小,sizeof(WNDCLASSEX)。
    UINT      style;     //窗口类的样式,决定了窗口的外观和行为。
    WNDPROC   lpfnWndProc;      //窗口过程,是一个回调函数,处理窗口消息的函数。
    int       cbClsExtra;
    int       cbWndExtra;
    HINSTANCE hInstance;    //窗口所属的应用程序实例句柄。
    HICON     hIcon;     //窗口的图标句柄。
    HCURSOR   hCursor;    //窗口的光标句柄。
    HBRUSH    hbrBackground;     //窗口的背景画刷句柄。
    LPCTSTR   lpszMenuName;         //窗口的菜单名称。
    LPCTSTR   lpszClassName;        //窗口类的类名。
    HICON     hIconSm;              //小图标句柄,用于任务栏、标题栏等较小的图标。
} WNDCLASSEX, *PWNDCLASSEX;

WNDCLASSEX 是在 WNDCLASS 结构的基础上进行了扩展,增加了额外的成员变量,提供了更多的窗口类信息。

具体来说,WNDCLASSEX 结构相比于 WNDCLASS 结构多了以下成员变量:

cbSize:

cbSize 指定了结构体的大小,用于标识结构体的版本。这样做是为了向后兼容性,允许在未来的版本中添加更多的成员变量而不会破坏现有的代码。

hIconSm:

hIconSm 是一个小图标的句柄,用于在窗口的标题栏和任务栏中显示。

通过添加这些额外的成员变量,WNDCLASSEX 结构提供了更多的灵活性和功能,使得窗口类的定义更加完善和全面。

对于 WNDCLASSEX 的窗口类使用 RegisterClassEx(&wc);

RegisterClassA function (winuser.h)

Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.

Note The RegisterClass function has been superseded by the RegisterClassEx function. You can still use RegisterClass, however, if you do not need to set the class small icon.

ATOM RegisterClassA(
  [in] const WNDCLASSA *lpWndClass
);

/*if (!RegisterClass(&wc)) 
       return FALSE; */

参数:A pointer to a WNDCLASS structure. You must fill the structure with the appropriate class attributes before passing it to the function.

返回值类型 ATOM,其实就是使用 typedef 定义的一个别名 typedef unsigned short WORD; typedef WORD ATOM;

窗口类的概念

  • 窗口类包含了窗口的各种参数信息的数据结构(结构体)
  • 每个窗口都具有窗口类,基于窗口类创建窗口。
  • 每个窗口类都具有一个名称,使用前必须注册到系统。

窗口类的分类

  • 系统窗口类:系统已经定义好的窗口类,所有应用程序都可以直接使用。
  • 应用程序全局窗口类:由用户自己定义,当前应用程序所有模块都可以使用。(类似全局变量)
  • 应用程序局部窗口类:由用户自己定义,当前应用程序中本模块可以使用。(类似局部变量)全局变量、局部变量是C语言的概念,弄清全局变量、和局部变量便可方便理解其使用范围。

全局窗口类(Global Window Class):指的是在整个程序中都可以访问的窗口类。在GUI编程中,这些类通常代表着应用程序的主窗口或全局可见的窗口元素。

局部窗口类(Local Window Class):指的是在特定作用域(通常是在函数内部)定义的窗口类。这些类通常只在特定的函数或代码块中可见和使用。

与之相对应的,在编程中提到的"全局变量"和"局部变量"与"全局窗口类"和"局部窗口类"之间存在一定的对应关系:

全局变量:是在整个程序中都可以访问的变量,其作用域是整个程序。

局部变量:是在特定作用域(通常是在函数内部)定义的变量,其作用域仅限于该作用域内。


对于系统窗口类,不需要注册,系统已经注册好了,程序员直接拿来使用。例如:按钮 BUTTON,编辑框 EDIT。对于Windows操作系统来说,按钮、编辑框等都是窗口,他们可以是嵌入到窗口中的窗口,也可以是单独出现的窗口。窗口的概念对于该操作系统是多么的重要,以至于微软用窗口(Windows)来命名这个操作系统。创建一个大按钮 BUTTON
在这里插入图片描述

#include <Windows.h>

int WINAPI WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
	HWND hWnd = CreateWindow(
		"BUTTON",      
		"Window",    
		WS_OVERLAPPEDWINDOW,    
		100, 50, 500, 400,
		NULL,      
		NULL,      
		hInstance,      
		NULL      
	); 

	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

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

	return 0;
}

创建一个大的编辑框 EDIT
在这里插入图片描述


style 窗口类风格

应用程序全局窗口类的注册,需要在窗口类的风格中增加 CS_GLOBALCLASS

WNDCLASS global_wc = { 0 };
global_wc.style = ... | CS_GLOBALCLASS;

局部窗口类不需要添加 CS_GLOBALCLASS

  • CS_HREDRAW :窗口水平方向大小发生变化时重绘。
  • CS_VREDRAW : 窗口的垂直方向大小变化的时候重新绘制窗口。
  • CS_DBLCLKS :允许窗口接收鼠标双击,不加该风格的话,无论鼠标点击多块都是单击。
  • CS_NOCLOSE :窗口没有关闭按钮。

全局窗口类并不常用,一般使用局部窗口类即可。

创建窗口

CreateWindowExA function (winuser.h)

HWND CreateWindowExA(
  [in]           DWORD     dwExStyle,     //窗口的扩展风格
  /*The extended window style of the window being created. For a list of possible values, see Extended Window Styles.*/
  [in, optional] LPCSTR    lpClassName,   //已经注册的窗口类的名称
  /*A null-terminated string or a class atom created by a previous call to the RegisterClass
   or RegisterClassEx function. The atom must be in the low-order word of lpClassName; 
   the high-order word must be zero. If lpClassName is a string, it specifies the window class name. 
   The class name can be any name registered with RegisterClass or RegisterClassEx, 
   provided that the module that registers the class is also the module that creates the window. 
   The class name can also be any of the predefined system class names.*/
  [in, optional] LPCSTR    lpWindowName,   //窗口标题栏的名字
  /*The window name. If the window style specifies a title bar, the window title pointed 
  to by lpWindowName is displayed in the title bar. When using CreateWindow to create controls, 
  such as buttons, check boxes, and static controls, use lpWindowName to specify the text of the control. 
  When creating a static control with the SS_ICON style, use lpWindowName to 
  specify the icon name or identifier. To specify an identifier, use the syntax "#num".*/
  [in]           DWORD     dwStyle,      //窗口的基本风格
  /*The style of the window being created. This parameter can be a combination of the 
  window style values, plus the control styles indicated in the Remarks section.*/
  [in]           int       X,         //窗口左上角距离屏幕原点的x水平坐标位置
    /*The initial horizontal position of the window. 
    For an overlapped or pop-up window, the x parameter is the initial x-coordinate of the window's upper-left corner, 
    in screen coordinates. For a child window, 
    x is the x-coordinate of the upper-left corner of the window relative to the upper-left corner of the parent window's client area. 
    If x is set to CW_USEDEFAULT, 
    the system selects the default position for the window's upper-left corner and ignores the y parameter. 
    CW_USEDEFAULT is valid only for overlapped windows; 
    if it is specified for a pop-up or child window, the x and y parameters are set to zero.*/
  [in]           int       Y,       //距离屏幕原点的垂直方向的位置
    /*The initial vertical position of the window. 
    For an overlapped or pop-up window, the y parameter is the initial y-coordinate of the window's upper-left corner, 
    in screen coordinates. For a child window, 
    y is the initial y-coordinate of the upper-left corner of the child window relative to the 
    upper-left corner of the parent window's client area. 
    For a list box y is the initial y-coordinate of the upper-left corner of the list box's 
    client area relative to the upper-left corner of the parent window's client area.
    
    If an overlapped window is created with the WS_VISIBLE style bit set and the x parameter 
    is set to CW_USEDEFAULT, then the y parameter determines how the window is shown. 
    If the y parameter is CW_USEDEFAULT, then the window manager calls ShowWindow with the SW_SHOW 
    flag after the window has been created. If the y parameter is some other value, 
    then the window manager calls ShowWindow with that value as the nCmdShow parameter.
    */
  [in]           int       nWidth,      //窗口的宽度
    /*The width, in device units, of the window. For overlapped windows, nWidth is the window's width, in screen coordinates, or CW_USEDEFAULT. If nWidth is CW_USEDEFAULT, the system selects a default width and height for the window; the default width extends from the initial x-coordinates to the right edge of the screen; the default height extends from the initial y-coordinate to the top of the icon area. CW_USEDEFAULT is valid only for overlapped windows; if CW_USEDEFAULT is specified for a pop-up or child window, the nWidth and nHeight parameter are set to zero.*/
  [in]           int       nHeight,     //窗口的高度
    /*The height, in device units, of the window. 
    For overlapped windows, nHeight is the window's height, 
    in screen coordinates. If the nWidth parameter is set to CW_USEDEFAULT, 
    the system ignores nHeight.*/
  [in, optional] HWND      hWndParent,      //父窗口的句柄
    /*A handle to the parent or owner window of the window being created. 
    To create a child window or an owned window, supply a valid window handle. 
    This parameter is optional for pop-up windows.

To create a message-only window, supply HWND_MESSAGE or a handle to an existing message-only window.*/
  [in, optional] HMENU     hMenu,     //窗口菜单句柄
    /*A handle to a menu, or specifies a child-window identifier, depending on the window style. 
    For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; 
    it can be NULL if the class menu is to be used. For a child window, hMenu specifies the child-window identifier, 
    an integer value used by a dialog box control to notify its parent about events. 
    The application determines the child-window identifier; it must be unique for 
    all child windows with the same parent window.*/
  [in, optional] HINSTANCE hInstance,     //应用程序实例句柄
    /*A handle to the instance of the module to be associated with the window.*/
  [in, optional] LPVOID    lpParam      //窗口创建时附加参数
    /*Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) 
    pointed to by the lParam param of the WM_CREATE message. This message is sent to the 
    created window by this function before it returns.

If an application calls CreateWindow to create a MDI client window, 
lpParam should point to a CLIENTCREATESTRUCT structure. 
If an MDI client window calls CreateWindow to create an MDI child window, 
lpParam should point to a MDICREATESTRUCT structure. 
lpParam may be NULL if no additional data is needed.*/
);

//create window instance
HWND hWnd = CreateWindowEx(
	0, pClassName,
	L"Happy Hard Window",      //窗口标题
	WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
	200, 200, 640, 480,        //窗口位置(200,200),大小长宽(640,480)。
	nullptr, nullptr, hInstance, nullptr
);

CreateWindowEx()百科



在操作系统的内核中会有很多注册好的窗口类:
在这里插入图片描述

  1. CreateWindowEx() 函数根据传进的参数 "Main" 窗口类的名字,在应用程序的局部窗口类中查找,如果找到执行2,如果未找到执行3。
  2. 比较局部窗口类与创建窗口时传入的 HINSTANCE 变量。如果发现相等,创建和注册的窗口类在同一模块,创建窗口并返回。如果不相等,执行3。
  3. 在应用程序全局窗口类中查找,如果找到执行4,未找到执行5。
  4. 使用找到的窗口类信息,创建窗口并返回。
  5. 在系统窗口类中查找,如果找到创建窗口并返回,否则创建窗口失败。
匹配查找窗口类
if(找到窗口类){
	申请一大块内存,将窗口类中的窗口信息以及CreateWindowEx函数中的参数信息放入这块内存中。
	return 这块内存的句柄;
} else {
	return NULL;
}

句柄(handle)

在一些上下文中,术语"句柄"可以与指针相关联,但它们并不完全相同。

指针(Pointer):是一个变量,其值为另一个变量的内存地址。指针直接指向内存中的某个位置。

句柄(Handle):通常是指一种特殊的数据结构,用于代表或引用另一个对象,而不直接指向对象的内存地址。在某些系统和编程环境中,句柄可以被认为是一种间接引用,用于访问某个资源或对象。

在一些情况下,句柄可能是指向对象的指针,但并非所有句柄都直接对应于内存地址。例如,在某些图形用户界面(GUI)编程中,窗口句柄通常是一个用于标识窗口的抽象标识符,而不是直接指向窗口对象在内存中的位置。

总的来说,指针是直接指向内存位置的变量,而句柄通常是一种更高级别的抽象,用于引用或标识对象或资源。


关闭窗口时结束进程

前面的窗口关闭时,进程并没有关闭,这是因为在消息处理函数中并没有进行相应的处理,关闭窗口时进程停在了 while(GetMessage()) 循环里。默认的消息处理函数DefWindowProc()并不会帮助我们在窗口关闭的消息产生时同时结束进程,所以需要我们自己去写:

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
	switch (msgID)
	{
	case WM_CLOSE:     //窗口关闭的消息
		PostQuitMessage(0);      //发送一个退出的消息到消息队列中
		//发送的这个消息可以使GetMessage()函数返回零,从而结束while循环,退出程序
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);
}

int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
	const auto pClassName = "Main";

	WNDCLASSEX wc = { 0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = nullptr;
	wc.hIcon = nullptr;
	wc.hInstance = hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = pClassName;
	wc.lpszMenuName = nullptr;
	wc.hIconSm = nullptr;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.cbSize = sizeof(wc);

	RegisterClassEx(&wc);

	HWND hWnd = CreateWindowEx(
		0, pClassName, "My Window",
		WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPEDWINDOW,
		200, 200, 640, 480,        //窗口位置(200,200),大小长宽(640,480)。
		nullptr, nullptr, hIns, nullptr
	);

	ShowWindow(hWnd, SW_SHOW);

	UpdateWindow(hWnd);

	MSG nMsg = { 0 };
	while (GetMessage(&nMsg, nullptr, 0, 0))     //GetMessage()返回值一直为非零时,while继续循环下去的条件一直为真
	{
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}

	return 0;
}

创建子窗口

  • 创建时设置父窗口句柄。
  • 创建风格要增加 WS_CHILD | WS_VISIBLE
#include <Windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
	switch (msgID)
	{
	case WM_CLOSE:
		PostQuitMessage(0);
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);
}

int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
	const auto pClassName = "Main";

	WNDCLASSEX wc = { 0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = nullptr;
	wc.hIcon = nullptr;
	wc.hInstance = hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = pClassName;
	wc.lpszMenuName = nullptr;
	wc.hIconSm = nullptr;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.cbSize = sizeof(wc);

	RegisterClassEx(&wc);

	WNDCLASSEX wc1 = { 0 };
	wc1.cbClsExtra = 0;
	wc1.cbWndExtra = 0;
	wc1.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc1.hCursor = nullptr;
	wc1.hIcon = nullptr;
	wc1.hInstance = hIns;
	wc1.lpfnWndProc = WndProc;
	wc1.lpszClassName = "child";
	wc1.lpszMenuName = nullptr;
	wc1.hIconSm = nullptr;
	wc1.style = CS_HREDRAW | CS_VREDRAW;
	wc1.cbSize = sizeof(wc1);

	RegisterClassEx(&wc1);

	HWND hWnd = CreateWindowEx(
		0, pClassName, "My Window",
		WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPEDWINDOW,
		200, 200, 640, 480,        //窗口位置(200,200),大小长宽(640,480)。
		nullptr, nullptr, hIns, nullptr
	);

	HWND hWnd1 = CreateWindowEx(
		0, "child", "Child Window",
		WS_CHILD | WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPEDWINDOW,
		200, 200, 640, 480,        //窗口位置(200,200),大小长宽(640,480)。
		hWnd, nullptr, hIns, nullptr
	);

	ShowWindow(hWnd, SW_SHOW);
	ShowWindow(hWnd1, SW_SHOW);

	UpdateWindow(hWnd);
	UpdateWindow(hWnd1);

	MSG nMsg = { 0 };
	while (GetMessage(&nMsg, nullptr, 0, 0))
	{
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);
	}

	return 0;
}

子窗口也可以有自己的消息处理函数,在上面的代码中子窗口使用了父窗口的消息处理函数,所以在子窗口关闭时(执行到了 PostQuitMessage())进程也会跟着结束。

在以上的代码中,为子窗口又注册了一个窗口类 wc1,其实也可以直接使用父窗口的窗口类 wc 创建子窗口。基于一个窗口类可以创建多个窗口。

  • 27
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jackey_Song_Odd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值