CreateWindow 并不是 C 语言的标准库函数,它是 Windows API (Win32 API) 中的一个核心函数,专门用于在 Windows 操作系统上创建窗口,这个函数的使用与 C 语言本身无关,而是与 Windows 平台紧密绑定。

(图片来源网络,侵删)
如果你想在 Linux、macOS 或其他操作系统上创建窗口,你需要使用它们各自的 API,X11、GTK、Qt 或 macOS 的 Cocoa API。
CreateWindow 函数简介
CreateWindow (以及更现代的 CreateWindowEx) 函数是 Win32 编程的基石,它的作用是在内存中创建一个窗口对象,然后将其注册到 Windows 系统中,使其在屏幕上可见,调用这个函数后,Windows 操作系统会负责管理这个窗口,包括绘制、处理用户输入(如点击、键盘输入)等。
函数原型
我们通常使用更强大的 CreateWindowEx 函数,它是 CreateWindow 的扩展版本。
HWND CreateWindowEx( DWORD dwExStyle, // 扩展窗口样式 LPCSTR lpClassName, // 预注册的窗口类名 LPCSTR lpWindowName, // 窗口标题 DWORD dwStyle, // 窗口样式 int X, // 窗口左上角 x 坐标 int Y, // 窗口左上角 y 坐标 int nWidth, // 窗口宽度 int nHeight, // 窗口高度 HWND hWndParent, // 父窗口句柄 HMENU hMenu, // 窗口菜单句柄 HINSTANCE hInstance, // 应用程序实例句柄 LPVOID lpParam // 创建参数 );
返回值:

(图片来源网络,侵删)
- 如果函数成功,它会返回一个
HWND类型的值,这是新创建窗口的句柄,你可以通过这个句柄来操作这个窗口(改变大小、发送消息、销毁它等)。 - 如果函数失败,它会返回
NULL。
参数详解:
-
dwExStyle(扩展窗口样式):- 指定窗口的扩展样式,例如是否带有边框、是否在最顶层等。
- 常用值:
WS_EX_CLIENTEDGE(凹陷的边框),WS_EX_WINDOWEDGE(凸起的边框),WS_EX_TOPMOST(始终在最前)。 - 如果不需要,通常设为
0。
-
lpClassName(窗口类名):- 这是一个字符串,指定了窗口的“模板”,Windows 通过这个名称来查找如何创建这个窗口(包括窗口过程函数、图标、鼠标样式等)。
- 这个类名必须是预先注册的,通过
RegisterClassEx函数,你也可以使用系统预定义的类名,如"BUTTON","EDIT","STATIC"或"EDIT",但创建主窗口时,你必须使用自己注册的类名。
-
lpWindowName(窗口标题):
(图片来源网络,侵删)- 栏上显示的文本。
"我的第一个窗口"。
-
dwStyle(窗口样式):- 定义窗口的基本外观和行为。
- 常用值:
WS_OVERLAPPED: 带有标题栏和边框的顶层窗口。WS_CAPTION: 有标题栏。WS_SYSMENU: 有系统菜单(最大化、最小化、关闭按钮)。WS_MINIMIZEBOX: 有最小化按钮。WS_MAXIMIZEBOX: 有最大化按钮。WS_SIZEBOX: 可调整大小。WS_VISIBLE: 窗口创建后立即可见。
- 这些样式可以用 (按位或) 组合起来,
WS_OVERLAPPEDWINDOW等于WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX。
-
X,Y(窗口位置):- 窗口左上角在屏幕上的坐标。
- 使用
CW_USEDEFAULT可以让 Windows 自动选择一个默认位置。
-
nWidth,nHeight(窗口大小):- 窗口的初始宽度和高度(以像素为单位)。
- 使用
CW_USEDEFAULT可以让 Windows 自动选择一个默认大小。
-
hWndParent(父窗口句柄):- 如果创建的是子窗口,这里指定父窗口的句柄,对于主窗口,通常设为
NULL。
- 如果创建的是子窗口,这里指定父窗口的句柄,对于主窗口,通常设为
-
hMenu(菜单句柄):- 窗口的菜单句柄,对于主窗口,通常设为
NULL,或者指定一个资源中定义的菜单句柄。
- 窗口的菜单句柄,对于主窗口,通常设为
-
hInstance(应用程序实例句柄):- 当前应用程序的实例句柄,在
WinMain函数中,这个值由系统传入,它告诉 Windows 这是哪个程序的实例。
- 当前应用程序的实例句柄,在
-
lpParam(创建参数):- 一个可选的指针,用于向窗口过程传递额外的创建数据,对于主窗口,通常设为
NULL。
- 一个可选的指针,用于向窗口过程传递额外的创建数据,对于主窗口,通常设为
完整的 C 语言示例:创建一个简单的窗口
下面是一个完整的、可运行的 C 语言程序,它创建一个窗口并显示出来,这是学习 Win32 编程的 "Hello, World!"。
#include <windows.h> // 必须包含此头文件来使用 Windows API
// 1. 窗口过程函数的声明
// LRESULT 是一个 32 位或 64 位的整数类型,用于表示消息处理函数的返回值。
// CALLBACK 是一个调用约定,告诉编译器这个函数的参数如何传递。
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// 2. WinMain 函数:程序的入口点
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 窗口类名称
const wchar_t* CLASS_NAME = L"MyMainWindowClass";
// 3. 注册窗口类
WNDCLASS wc = { };
// 设置窗口类的各个属性
wc.lpfnWndProc = WindowProc; // 指向窗口过程函数的指针
wc.hInstance = hInstance; // 应用程序实例句柄
wc.lpszClassName = CLASS_NAME; // 窗口类名
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 加载标准箭头光标
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // 设置背景为默认窗口颜色
// 注册窗口类
if (!RegisterClass(&wc)) {
// 如果注册失败,弹出错误对话框并退出
MessageBox(NULL, L"窗口类注册失败!", L"错误", MB_ICONERROR);
return 1;
}
// 4. 创建窗口
HWND hwnd = CreateWindowEx(
0, // 扩展样式
CLASS_NAME, // 窗口类名
L"我的第一个窗口", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, CW_USEDEFAULT, // 初始位置 (x, y)
800, 600, // 初始大小 (width, height)
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 应用程序实例句柄
NULL // 创建参数
);
if (hwnd == NULL) {
MessageBox(NULL, L"窗口创建失败!", L"错误", MB_ICONERROR);
return 1;
}
// 5. 显示窗口
// ShowWindow(hwnd, nCmdShow); // 使用 WinMain 传入的显示方式
ShowWindow(hwnd, SW_SHOW); // 或者直接显示
UpdateWindow(hwnd); // 更新窗口客户区
// 6. 消息循环
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); // 翻译键盘消息
DispatchMessage(&msg); // 将消息发送到窗口过程函数
}
// 当 GetMessage 返回 0 时,程序退出
return (int)msg.wParam;
}
// 7. 窗口过程函数:处理所有发送到该窗口的消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY: // 当窗口被销毁时(例如用户点击了关闭按钮)
// 发送 WM_QUIT 消息,这将导致 GetMessage 函数返回 0,从而退出消息循环
PostQuitMessage(0);
return 0;
case WM_PAINT: // 当窗口需要重绘时
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// 在这里进行绘制操作
// 绘制一段文本
TextOut(hdc, 50, 50, L"你好,Windows 窗口世界!", 22);
EndPaint(hwnd, &ps);
return 0;
}
}
// 对于我们没有处理的消息,调用 DefWindowProc 进行默认处理
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
如何编译和运行
- 保存代码:将上述代码保存为
main.c。 - 编译:你需要一个 Windows 下的 C 编译器,Visual Studio 的 cl.exe 或者 MinGW 的 gcc。
- 使用 MinGW (推荐初学者):
打开命令行(如
cmd或 PowerShell),进入代码所在的目录,然后运行:gcc main.c -o myapp.exe
- 使用 Visual Studio: 创建一个新的 "Windows 桌面应用程序" 项目,然后将代码粘贴进去,直接按 F5 编译运行。
- 使用 MinGW (推荐初学者):
打开命令行(如
- 运行:编译成功后,会生成
myapp.exe文件,双击它,你就能看到一个标题为 "我的第一个窗口" 的窗口。
- 包含头文件:
#include <windows.h>是使用 Win32 API 的前提。 - 定义窗口过程:
WindowProc函数是窗口的“大脑”,它决定了窗口如何响应各种事件(如点击、关闭、重绘)。 WinMain入口:程序从这里开始。- 注册窗口类:通过
WNDCLASS结构体定义窗口的“蓝图”,并用RegisterClass将其告诉 Windows。 - 创建窗口:调用
CreateWindowEx,根据注册的“蓝图”在内存中创建一个窗口实例。 - 显示窗口:
ShowWindow让窗口从内存变为可见,UpdateWindow强制立即重绘一次。 - 消息循环:这是 Windows GUI 程序的核心。
GetMessage不断从消息队列中获取消息,TranslateMessage和DispatchMessage将其分发给WindowProc处理。 - 处理消息:在
WindowProc中,使用switch语句处理不同的消息(如WM_DESTROY关闭窗口,WM_PAINT绘制内容)。 - 默认处理:对于未处理的消息,调用
DefWindowProc让系统执行默认操作。
