Windows窗口程序之新建窗口(C++)

Windows窗口程序之新建窗口(C++)

创建Windows的基本流程

下面这个流程图展示了一个窗口的基本创建流程,图片来自B站一个教程。通过这些过程,我们就可以建立一个空的Windows窗口了。
Windows窗口程序之新建窗口(C++)_第1张图片

1. WinMain入口 (主函数)

我们写的控制台程序的入口是main(),而与之类似的,我们的windows窗口程序的入口为WinMain(),它长这个样子:

// WinMain 入口
#include
int WINAPI WinMain(
	HINSTANCE hInstance,   	// 窗口句柄
	HINSTANCE hPreInstance, // 上一个窗口类的句柄
	LPSTR ICmdLine,         // 命令行参数 
	int CmdShow             // 窗口的显示状态
) {}

每个参数的意思:

  1. HINSTANCE hInstance:窗口句柄,可以理解为一个窗口的标识符,相当于我们每个人都有一个身份证号一样,用来辨识不同的窗口。
  2. HINSTANCE hPreInstance:32位及以上的计算机不会用到,都设为0。
  3. LPSTR ICmdLine:等同于char* 类型,即字符串指针,往往是在命令行由用户输入的,所以很少用到。
  4. int CmdShow: 用于指定窗口的显示状态,比如最大化、最小化等。
  5. WINAPI:我们发现WinMain前面还有一个WINAPI,它表明函数的调用约定,即函数从调用方接受参数的方式。我们现在只需要先按这个格式定义我们的入口就行了。

2. 注册窗口类

在创建一个窗口之前,需要先注册窗口类,需要注意的是,“窗口类”不等同于C++面向对象中提到的类,而是指窗口的类型。注册一个窗口之前,我们要先声明我们需要的窗口类。窗口类的定义如下:

// 窗口类定义
typedef struct tagWNDCLASSA {
    UINT        style;          // 窗口的风格
    WNDPROC     lpfnWndProc;    // 指向窗口过程的函数指针
    int         cbClsExtra;     
    int         cbWndExtra;
    HINSTANCE   hInstance;    // 窗口类实例的句柄
    HICON       hIcon;        // 窗口图标的句柄
    HCURSOR     hCursor;      // 窗口光标的句柄
    HBRUSH      hbrBackground;   // 背景画刷的句柄
    LPCSTR      lpszMenuName;  // 窗口菜单的名字
    LPCSTR      lpszClassName;  // 窗口类的名字
}WINCLASS;

看起来很麻烦,但我们现在不需要考虑太多。他们都是一个窗口类的属性,供我们创建自己想要的窗口实例。下面我们来创建一个窗口实例:

// 创建一个wnd窗口类实例
WINCLASS wnd = {
	CS_HREDRAW,
	DefWindowProc,
	0,0,
	hInstance,
	LoadIcon(NULL, IDI_APPLICATION),
	LoadCursor(NULL, IDC_ARROW),
	(HBRUSH)(GetStockObject(WHITE_BRUSH)),
	NULL,L"MyWindow"
};

我们现在就创建了一个名字为"MyWindow"的窗口类实例wnd,里面每个参数的具体意思有兴趣可以自己查一下,这里不展开。有了窗口类,我们现在就要去注册它:

//注册一个窗口类实例
RegisterClass(&wnd);

2. 创建窗口

创建了窗口类并注册以后,就可以正式创建这个窗口了。我们要用函数CreateWindow()来创建一个窗口,它的定义如下:

HWND WINAPI CreateWindowExW(
    _In_ DWORD dwExStyle,           // 扩展窗口的风格
    _In_opt_ LPCWSTR lpClassName,   // 窗口类名
    _In_opt_ LPCWSTR lpWindowName,  // 窗口标题
    _In_ DWORD dwStyle,             // 窗口风格
    _In_ int X,                     // 初始X坐标
    _In_ int Y,						// 初始Y坐标
    _In_ int nWidth,                // 初始X方向尺寸
    _In_ int nHeight,               // 初始Y方向尺寸
    _In_opt_ HWND hWndParent,       // 父窗口句柄 
    _In_opt_ HMENU hMenu,           // 菜单句柄
    _In_opt_ HINSTANCE hInstance,   // 程序实例句柄
    _In_opt_ LPVOID lpParam);       // 创建参数

这里的_In_表示该参数为输入值,opt表示该参数是可选的,我们先不关注这些。实际上,我们只需要调用CreateWindows()就可以创建窗口,因为在WinUser.h中有这么一段宏定义,为我们默认了扩展的窗口风格:

#define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam) \
CreateWindowExW(0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)

创建窗口会返回窗口句柄。完成创建窗口只需要这么一句:

HWND hWnd = CreateWindow(L"MyWindow", L"newWindow", 
						WS_OVERLAPPEDWINDOW,100,100,300,300,NULL,NULL,hInstance,NULL);

3. 显示窗口

创建完窗口以后它是不可见的,我们还要显示它, 调用showWindow(窗口句柄,显示方式)。

ShowWindow(hWnd, nShowCmd); 

4. 更新窗口

显示窗口以后需要更新窗口,至于为什么Show以后还要Update,我也还没搞清楚,就先不误导大家了。照着来就没错了。

UpdateWindow(hWnd);

5. 消息循环

按照我自己的理解,windows应用程序是由消息来驱动的,在程序内发生的事件都会将消息发送到消息队列,程序通过回调消息处理函数来对事件做出反应。下面看几个需要用到的结构或函数。

  1. 消息结构体
typedef struct tagMSG {
    HWND   hwnd;    // 获取消息的窗口句柄  
    UINT   message;  // 消息内容
    WPARAM wParam;  
    LPARAM lParam;  
    DWORD  time;    
    POINT  pt;      
} MSG;
  1. 默认消息处理函数
LRESULT CALLBACK DefWindowProcW(
    _In_ HWND hWnd,
    _In_ UINT Msg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam);
    // 我们可以重写它,来实现自己需要的消息处理过程。
  1. 消息获取
BOOL GetMessage(
	LPMSG lpMsg, 			 //存放获取到的消息BUFF
	HWND hWnd,				 //窗口句柄
	 UINT wMsgFilterMin,
	UINT wMsgFilterMax);  
  1. 消息翻译与派发
LRESULT WINAPI DispatchMessageA(
    _In_ CONST MSG *lpMsg);   //要翻译的消息地址
LRESULT WINAPI DispatchMessageW(
    _In_ CONST MSG *lpMsg);  //要发送的消息地址

接下来我们看看最主要的消息循环过程:

MSG msg;
while (GetMessage(&msg,NULL, 0, 0)) { 
	TranslateMessage(&msg); //翻译消息
	DispatchMessage(&msg);  //分发消息到窗口过程
}

整个过程就是不断的:获取–>翻译–>派发。

自定义消息处理函数

通过上面的流程,我们已经可以创建一个窗口了。但是运行窗口以后,我们关掉这个窗口,发现它关闭以后,窗口虽然消失了,但是进程并没有终止。这是因为默认的消息处理函数,对于“关闭”窗口这个消息没有做任何处理,这就需要我们重写一个消息处理函数。

//自定义的窗口过程
LRESULT CALLBACK MyWindowProc(HWND hWnd,UINT Msg, WPARAM wParam,LPARAM lParam) {
	switch (Msg) {
		case WM_DESTROY:  // WM_DESTORY 代表“窗口关闭” 消息
			PostQuitMessage(0);
			return 0;
		default:
			return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

然后我们把创建窗口类的第二个参数WNDPROC lpfnWndProc修改为我们定义的MyWindowProc。然后关闭窗口以后进程也可以随之关闭了。至此,一个空窗口创建完毕。

6. 完整代码

#include

//自定义的窗口过程
LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
	switch (Msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance,LPSTR ICmdLine,int nCmdShow) {
	
	// 创建窗口类
	WNDCLASS wnd = {            
		CS_HREDRAW,
		MyWindowProc,          // 使用自定义的窗口过程函数
		0,0,hInstance,
		LoadIcon(NULL,IDI_APPLICATION),
		LoadCursor(NULL,IDC_ARROW), 
		(HBRUSH)(GetStockObject(WHITE_BRUSH)),
		NULL, L"MyWindow"
	};
	// 注册窗口类
	RegisterClass(&wnd);
	// 创建窗口
	HWND hWnd = CreateWindow(L"MyWindow", L"newWindow", WS_OVERLAPPEDWINDOW,
		100, 100, 500, 500, NULL, NULL, hInstance, NULL);
	// 显示窗口
	ShowWindow(hWnd, nCmdShow);
	// 更新窗口
	UpdateWindow(hWnd);
	//消息循环
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0)) {    // 循环获取消息
		TranslateMessage(&msg);               // 翻译消息
		DispatchMessage(&msg);                // 派发消息
	}
	return 0;
}

你可能感兴趣的:(c++,windows)