下面这个流程图展示了一个窗口的基本创建流程,图片来自B站一个教程。通过这些过程,我们就可以建立一个空的Windows窗口了。
我们写的控制台程序的入口是main(),而与之类似的,我们的windows窗口程序的入口为WinMain(),它长这个样子:
// WinMain 入口
#include
int WINAPI WinMain(
HINSTANCE hInstance, // 窗口句柄
HINSTANCE hPreInstance, // 上一个窗口类的句柄
LPSTR ICmdLine, // 命令行参数
int CmdShow // 窗口的显示状态
) {}
每个参数的意思:
在创建一个窗口之前,需要先注册窗口类,需要注意的是,“窗口类”不等同于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);
创建了窗口类并注册以后,就可以正式创建这个窗口了。我们要用函数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);
创建完窗口以后它是不可见的,我们还要显示它, 调用showWindow(窗口句柄,显示方式)。
ShowWindow(hWnd, nShowCmd);
显示窗口以后需要更新窗口,至于为什么Show以后还要Update,我也还没搞清楚,就先不误导大家了。照着来就没错了。
UpdateWindow(hWnd);
按照我自己的理解,windows应用程序是由消息来驱动的,在程序内发生的事件都会将消息发送到消息队列,程序通过回调消息处理函数来对事件做出反应。下面看几个需要用到的结构或函数。
typedef struct tagMSG {
HWND hwnd; // 获取消息的窗口句柄
UINT message; // 消息内容
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
LRESULT CALLBACK DefWindowProcW(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
// 我们可以重写它,来实现自己需要的消息处理过程。
BOOL GetMessage(
LPMSG lpMsg, //存放获取到的消息BUFF
HWND hWnd, //窗口句柄
UINT wMsgFilterMin,
UINT wMsgFilterMax);
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。然后关闭窗口以后进程也可以随之关闭了。至此,一个空窗口创建完毕。
#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;
}