CreateWindow 是 Windows API(应用程序编程接口)中的一个核心函数,用于在内存中创建(或称“生成”)一个窗口,这个函数非常重要,它是所有图形用户界面程序的起点。

CreateWindow 是什么?
CreateWindow 函数的作用是根据你提供的参数,在系统中注册并创建一个窗口的“对象”,你可以把它想象成在图纸上设计一个房子的蓝图,这个蓝图定义了房子的名字、大小、样式、拥有者等信息,但房子本身还不存在,你只是创建了这个设计。
这个函数返回一个窗口句柄,这是一个唯一的标识符(通常是一个 HWND 类型的值),程序后续通过这个句柄来操作这个窗口,比如显示它、移动它、向它发送消息等。
函数原型
CreateWindow 有两个版本:一个用于 ANSI 字符集,一个用于 Unicode 字符集,在 Windows 中,推荐使用 Unicode 版本,因为它能更好地支持多语言环境。
// Unicode 版本 (推荐) HWND CreateWindowW( LPCWSTR lpClassName, // 窗口类名 LPCWSTR lpWindowName, // 窗口标题 DWORD dwStyle, // 窗口样式 int X, // 初始 x 坐标 int Y, // 初始 y 坐标 int nWidth, // 初始宽度 int nHeight, // 初始高度 HWND hWndParent, // 父窗口句柄 HMENU hMenu, // 窗口菜单句柄 HINSTANCE hInstance, // 应用程序实例句柄 LPVOID lpParam // 创建参数 ); // ANSI 版本 (不推荐,除非有特殊需求) HWND CreateWindowA( LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam );
在 Visual Studio 中,如果你包含 <windows.h> 并调用 CreateWindow,预处理器会根据项目设置(Unicode/多字节字符集)自动将其解析为 CreateWindowW 或 CreateWindowA。

参数详解
理解每个参数的含义是使用 CreateWindow 的关键。
| 参数 | 类型 | 描述 |
|---|---|---|
lpClassName |
LPCWSTR |
窗口类名,这是一个字符串,用于标识窗口的“模板”,你不能直接创建一个没有任何定义的窗口,这个类名必须是一个已经注册的窗口类,注册通常通过 RegisterClassEx 函数完成,你也可以使用一些预定义的类名,如 TEXT("BUTTON"), TEXT("EDIT"), TEXT("STATIC") 等。 |
lpWindowName |
LPCWSTR |
,这是显示在窗口标题栏上的文本,对于按钮等控件,这通常是按钮上显示的文字。 |
dwStyle |
DWORD |
窗口样式,这是一个或多个使用按位或 () 运算符组合起来的标志,常用样式有: - WS_OVERLAPPED: 创建一个带边框和标题栏的顶层窗口。- WS_CAPTION: 创建一个带标题栏的窗口(必须与 WS_OVERLAPPED 组合)。- WS_SYSMENU: 在标题栏上添加一个系统菜单(关闭按钮等)。- WS_THICKFRAME: 创建一个可调整大小的窗口边框。- WS_MINIMIZEBOX: 添加最小化按钮。- WS_MAXIMIZEBOX: 添加最大化按钮。- WS_VISIBLE: 非常重要! 创建窗口后立即显示它,如果不加这个标志,窗口虽然被创建,但不可见。- WS_POPUP: 创建一个弹出式窗口。- WS_CHILD: 创建一个子窗口(控件)。常用组合: WS_OVERLAPPEDWINDOW = WS_OVERLAPPED \| WS_CAPTION \| WS_SYSMENU \| WS_THICKFRAME \| WS_MINIMIZEBOX \| WS_MAXIMIZEBOX |
X |
int |
窗口左上角的初始 x 坐标(相对于屏幕左上角),使用 CW_USEDEFAULT 可以让系统自动选择一个默认位置。 |
Y |
int |
窗口左上角的初始 y 坐标,同样可以使用 CW_USEDEFAULT。 |
nWidth |
int |
窗口的初始宽度(以像素为单位),使用 CW_USEDEFAULT 可以让系统自动选择一个默认宽度。 |
nHeight |
int |
窗口的初始高度(以像素为单位)。 |
hWndParent |
HWND |
父窗口句柄,如果创建的是子窗口或控件,这里应填入父窗口的句柄,对于顶层窗口,此项为 NULL。 |
hMenu |
HMENU |
窗口菜单句柄,对于顶层窗口,通常为 NULL,对于子窗口(控件),它被用作控件的 ID。 |
hInstance |
HINSTANCE |
应用程序实例句柄,在 WinMain 函数中,这个值由系统传入,你可以使用全局变量 GetModuleHandle(NULL) 来获取它。 |
lpParam |
LPVOID |
创建参数,这是一个指向任意数据的指针,用于向窗口过程传递初始化数据,对于大多数简单窗口,此项为 NULL。 |
完整示例:创建并显示一个窗口
下面是一个完整的 C 语言程序,它演示了如何注册一个窗口类,然后使用 CreateWindow 创建并显示这个窗口。
#include <windows.h>
// 窗口过程函数的声明
// LRESULT 是一个 32 位或 64 位的整数,取决于平台
// CALLBACK 是一个调用约定
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 1. 定义窗口类
WNDCLASSEXW wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEXW);
wcex.style = CS_HREDRAW | CS_VREDRAW; // 类样式
wcex.lpfnWndProc = WndProc; // 指向窗口过程函数的指针
wcex.cbClsExtra = 0; // 类的额外内存
wcex.cbWndExtra = 0; // 窗口的额外内存
wcex.hInstance = hInstance; // 应用程序实例句柄
wcex.hIcon = LoadIconW(NULL, IDI_APPLICATION); // 默认图标
wcex.hCursor = LoadCursorW(NULL, IDC_ARROW); // 默认光标
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // 背景画刷
wcex.lpszClassName = L"MyWindowClass"; // 窗口类名
wcex.hIconSm = LoadIconW(NULL, IDI_APPLICATION); // 小图标
// 2. 注册窗口类
if (!RegisterClassExW(&wcex)) {
MessageBoxW(NULL, L"窗口类注册失败!", L"错误", MB_OK);
return 1;
}
// 3. 创建窗口
HWND hWnd = CreateWindowW(
L"MyWindowClass", // 窗口类名 (必须和注册时的一致)
L"我的第一个窗口", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, // x 坐标
CW_USEDEFAULT, // y 坐标
800, // 宽度
600, // 高度
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 应用程序实例句柄
NULL // 创建参数
);
// 检查窗口是否创建成功
if (!hWnd) {
MessageBoxW(NULL, L"窗口创建失败!", L"错误", MB_OK);
return 1;
}
// 4. 显示和更新窗口
ShowWindow(hWnd, nCmdShow); // nCmdShow 决定了窗口的初始显示状态(正常、最大化、最小化等)
UpdateWindow(hWnd); // 强制立即重绘窗口客户区
// 5. 消息循环
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); // 翻译键盘消息
DispatchMessageW(&msg); // 将消息发送到窗口过程函数处理
}
return (int)msg.wParam;
}
// 6. 窗口过程函数 - 这是窗口的“大脑”
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// 在这里进行绘制操作
TextOutW(hdc, 50, 50, L"你好, CreateWindow!", 20);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY: {
// 当用户点击关闭按钮时,发送 WM_DESTROY 消息
PostQuitMessage(0); // 发送 WM_QUIT 消息,使 GetMessage 返回 0,从而结束消息循环
}
break;
default:
// 对于我们没有处理的消息,交给系统默认处理
return DefWindowProcW(hWnd, message, wParam, lParam);
}
return 0;
}
代码解释:
WinMain: 这是 Windows 程序的入口点,类似于控制台程序的main。WNDCLASSEXW结构体: 定义了窗口的“蓝图”或“模板”,它告诉系统这个窗口应该用什么函数来处理消息(lpfnWndProc),它长什么样(图标、光标、背景色)等等。RegisterClassExW: 将WNDCLASSEXW结构体注册到系统中,只有注册后,才能用这个类名来创建窗口。CreateWindowW: 根据注册好的MyWindowClass这个模板,在内存中创建一个具体的窗口实例。ShowWindow和UpdateWindow:CreateWindow只是创建了窗口对象,但你看不见它。ShowWindow负责将它显示在屏幕上,UpdateWindow确保窗口的初始内容被绘制出来。- 消息循环: 这是 Windows GUI 程序的核心,程序会一直循环,等待用户的各种操作(如点击鼠标、敲击键盘),这些操作会转换成消息,被放入消息队列。
GetMessage从队列中取出消息,DispatchMessage将消息发送给对应的窗口过程函数 (WndProc) 处理。 WndProc(窗口过程函数): 这是真正处理消息的地方。switch语句根据不同的消息代码(message)执行不同的代码,我们只处理了WM_PAINT(需要重绘时)和WM_DESTROY(窗口被销毁时)两个消息,其他所有消息都通过DefWindowProcW交由系统处理。
CreateWindow vs. CreateWindowEx
你还会看到一个功能更强大的函数:CreateWindowEx。
CreateWindowEx 与 CreateWindow 几乎完全相同,但它多了一个 dwExStyle 参数。

dwExStyle 用于指定扩展窗口样式,这些是标准窗口样式无法实现的更高级的特性。
WS_EX_CLIENTEDGE: 给窗口客户区添加一个凹陷的边框。WS_EX_TOOLWINDOW: 创建一个工具栏窗口,不出现在任务栏上。WS_EX_LAYERED: 创建一个分层窗口,支持透明效果。
除非你需要使用扩展样式,否则 CreateWindow 和 CreateWindowEx 可以互换使用。CreateWindowEx 是更现代和推荐的选择,因为它提供了更多的灵活性。
CreateWindow的核心作用是创建一个窗口对象,并返回其句柄。- 它需要一个已注册的窗口类名作为模板,这个类名通常通过
RegisterClassEx注册。 - 通过组合窗口样式 参数,你可以定义窗口的外观和行为(如是否可调整大小、是否有标题栏等)。
CreateWindow只是创建了窗口,要让它显示出来,还需要调用ShowWindow。CreateWindow创建的窗口本身什么也做不了,你需要通过消息循环 和窗口过程函数 来响应用户的交互。
