Windows API 中的窗口
最后修改于 2023 年 10 月 18 日
窗口是屏幕上的一个矩形区域,应用程序在其中显示输出并从用户接收输入。在 Windows 中,一切都是窗口。至少从程序员的角度来看是这样的。一个主窗口、一个按钮、一个静态文本,甚至一个图标;它们都是窗口。静态文本只是窗口的一种特殊类型,桌面区域也是如此。
wWinMain 函数
每个 Windows UI 应用程序必须至少有两个函数:WinMain 函数和窗口过程。WinMain 函数是 Windows UI 应用程序的入口点。它初始化应用程序,在屏幕上显示应用程序窗口,并进入主循环。在我们的示例中,我们使用 wWinMain
函数原型,该原型用于创建 Unicode UI 程序。
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);
hInstance
是一个实例句柄。它是一个 32 位数字,用于标识我们的程序在操作系统环境中的实例。这个数字是由 Windows 在程序开始执行时提供的。hPrevInstance
参数始终为 NULL
;它是 16 位 Windows 的遗留产物。Windows 程序也可以从命令行启动。给定的参数存储在 pCmdLine
参数中。nCmdShow
值指定窗口将如何显示:最小化、最大化或隐藏。
当它接收到 WM_QUIT
消息时,wWinMain
函数终止。
注册窗口类
在创建窗口之前,我们必须在 Windows 中注册其类。许多控件已经注册了它们的窗口类。因此,当我们创建一个按钮或静态文本时,我们不需要为它们注册窗口类。要注册窗口类,我们必须创建并填充一个 WNDCLASS
结构。我们设置窗口样式、额外分配的字节数、窗口类名、程序实例的句柄、背景画刷、可选的菜单名、窗口过程、光标的句柄和一个图标。然后调用 RegisterClassW
函数。
创建窗口
通过调用 CreateWindowW
函数创建窗口。
HWND CreateWindowW(LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
lpClassName
唯一地标识窗口。它是我们注册窗口的名称。lpWindowName
是窗口名称。它的作用取决于上下文——它可以是父窗口中的窗口标题,也可以是子窗口(如按钮或静态文本)中的标签。可以使用多种样式创建 Windows。为此,我们有 dwStyle
参数。x
, y
指定窗口的初始水平和垂直位置。nWidth
和 nHeight
指定窗口的宽度和高度。
hWndParent
是父窗口的句柄。对于没有父窗口的窗口,我们使用 NULL
。对于父窗口,hMenu
是一个可选的菜单句柄,对于子窗口,它是一个控件标识符。hInstance
是程序实例的句柄。lpParam
是最后一个参数,它是在 WM_CREATE
消息期间传递给窗口的可选值。CreateWindowW
函数返回新创建窗口的句柄。
消息
WinMain
函数创建一个消息循环。它是一个在应用程序生命周期中运行的无限循环。消息循环是一种编程结构,它等待并分发程序中的事件或消息。Windows 使用消息进行通信。一个消息是一个整数值,用于标识一个特定的事件——按钮点击、窗口大小调整或应用程序关闭。
可以在同一时刻创建多个消息。这些消息不能同时处理;因此它们存储在消息队列中。消息进入消息队列并等待被处理。GetMessage
函数从消息队列中检索消息。DispatchMessage
函数将消息分发给窗口过程。如果应用程序获取字符输入,我们在循环中包含 TranslateMessage
函数。
窗口过程
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
每个窗口都有一个关联的窗口过程。它是一个接收消息的函数。hwnd
是将要接收消息的窗口的句柄。uMsg
是消息。wParam
和 lParam
参数提供额外的消息信息。这些参数的值取决于消息类型。
消息来自用户或操作系统。我们对消息做出反应,或者我们调用默认窗口过程以提供默认处理。大多数消息被发送到默认窗口过程。默认窗口过程被称为 DefWindowProcW
。它使用与普通窗口过程相同的参数调用。
一个简单的窗口
以下示例显示了一个骨架 Windows 应用程序。
#include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { MSG msg; HWND hwnd; WNDCLASSW wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.lpszClassName = L"Window"; wc.hInstance = hInstance; wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE); wc.lpszMenuName = NULL; wc.lpfnWndProc = WndProc; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); RegisterClassW(&wc); hwnd = CreateWindowW(wc.lpszClassName, L"Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 350, 250, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } return (int) msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProcW(hwnd, msg, wParam, lParam); }
我们将逐步解释示例代码。
#include <windows.h>
这是一个 C 编程语言的头文件。它包含 API 中的所有函数声明、所有常用宏和所有数据类型。通过链接必要的库——kernel32.lib
、user32.lib
、gdi32.lib
——并通过包含 <windows.h>
头文件,将 Windows API 添加到 C 编程项目中。
wc.style = CS_HREDRAW | CS_VREDRAW;
我们在这里设置窗口样式。CS_HREDRAW
和 CS_VREDRAW
标志表示,每当窗口的高度或宽度发生移动或调整大小时,将重绘整个窗口。
wc.cbClsExtra = 0; wc.cbWndExtra = 0;
在我们的示例中,我们不使用额外的字节。因此,我们将成员设置为零。这两个属性最常见的用途是窗口子类化。
wc.lpszClassName = L"Window";
窗口是这种特定窗口类型的类名。我们在创建窗口时使用此类名。L
字符位于宽字符串之前。
wc.hInstance = hInstance;
我们设置程序的实例。
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
我们在这里设置背景画刷。它用于绘制窗口的客户区。
wc.lpszMenuName = NULL;
在我们的示例中,我们不创建菜单。
wc.lpfnWndProc = WndProc;
我们为窗口类提供窗口过程。
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
我们为我们的应用程序设置光标。我们使用 LoadCursor
函数从系统资源加载光标。IDC_ARROW
是标准箭头光标的值。
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
我们为我们的应用程序设置图标。该图标使用 LoadIcon
函数从系统资源中检索。IDI_APPLICATION
是默认应用程序图标的值。
RegisterClassW(&wc);
我们使用系统注册窗口类。
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
这两行在屏幕上显示窗口。nCmdShow
指定我们在屏幕上显示窗口的方式。
while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); }
这是消息循环。我们使用 GetMessage
函数从消息队列接收消息,并使用 DispatchMessage
函数将它们分发给窗口过程。
return (int) msg.wParam;
在应用程序的末尾,将退出代码返回给系统。
switch(msg) { case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProcW(hwnd, msg, wParam, lParam);
在窗口过程中,我们对 WM_DESTROY
消息做出反应。PostQuitMessage
将 WM_QUIT
消息发送到消息队列。所有其他消息都使用 DefWindowProcW
函数发送到默认处理。

在本部分 Windows API 教程中,我们创建了一个基本窗口。