一、前置知识
1.1如何创建窗口?
在 Windows 编程中,创建一个窗口通常包括几个明确的步骤。这些步骤确保窗口不仅被正确创建,还能响应用户操作和系统消息。以下是创建窗口的典型步骤:
1.注册窗口类
- 在创建任何窗口之前,必须先定义并注册一个窗口类。这个类包括一些基本信息,如窗口过程地址、背景颜色和光标类型。
- 使用
WNDCLASSEX
结构体来定义窗口类的属性,然后调用RegisterClassEx()
函数将其注册到系统。
2. 创建窗口
- 使用
CreateWindowEx()
函数来实际创建窗口。这个函数需要窗口类名、窗口标题和窗口样式等信息,同时还可以指定窗口的大小和位置。 CreateWindowEx()
返回一个窗口句柄,用于后续的窗口操作和消息处理。
3. 显示窗口
- 创建窗口后,使用
ShowWindow()
函数使窗口可见。这个函数需要窗口句柄和一个命令参数,如 SW_SHOW,来确定如何显示窗口。 UpdateWindow()
函数通常跟在ShowWindow()
后面调用,以立即更新窗口的客户区。
4. 实现消息循环
创建并显示窗口后,必须运行一个消息循环来检索和分派消息到相应的窗口。消息循环使用 GetMessage()
、TranslateMessage()
和 DispatchMessage()
函数来处理。
GetMessage() 从消息队列中提取消息,TranslateMessage() 进行必要的转换(如键盘输入的转换),而 DispatchMessage() 将消息发送到适当的窗口过程。
5. 响应消息
窗口过程(WNDPROC)函数是消息处理的核心,它根据传入的消息执行相应的操作,如绘制窗口、处理键盘输入等。
每个窗口类都有一个与之关联的窗口过程,该过程定义了窗口如何响应各种消息。
1.2 WNDCLASSEXA类
typedef struct _WNDCLASSEX {
UINT cbSize; // 结构体大小,用于版本控制
UINT style; // 类风格
WNDPROC lpfnWndProc; // 指向窗口过程的指针(回调函数)
int cbClsExtra; // 分配给类的额外内存量
int cbWndExtra; // 分配给窗口实例的额外内存量
HINSTANCE hInstance; // 拥有类的应用程序的实例句柄
HICON hIcon; // 类图标的句柄
HCURSOR hCursor; // 类光标的句柄
HBRUSH hbrBackground; // 背景画刷句柄
LPCTSTR lpszMenuName; // 菜单资源的名称
LPCTSTR lpszClassName; // 类名
HICON hIconSm; // 小图标的句柄,用于关联窗口
} WNDCLASSEX;
各字段说明:
-
cbSize:
- 这个字段的值必须设置为 sizeof(WNDCLASSEX),用于版本控制和兼容性。
-
控制窗口类的样式,例如是否重新绘制窗口背景(CS_HREDRAW和CS_VREDRAW)。
更多样式 - 指向窗口处理函数的指针(回调函数),这是最重要的部分,因为它定义了窗口如何响应各种消息。
- 分别为窗口类和每个窗口实例提供额外的内存空间,通常用于存储快速访问数据。
- 指定类注册所在的应用程序实例的句柄。
- 为窗口类指定一个图标,这是在任务栏和标题栏上显示的大图标。
- 指定窗口类使用的光标类型。
- 指定用于窗口背景的画刷。
- 如果类窗口有菜单,这里指定菜单资源的名称。
- 指定窗口类的名称,这是在创建窗口时引用的。
- 提供一个小图标,这通常显示在窗口的左上角。
style:
lpfnWndProc:
cbClsExtra 和 cbWndExtra:
hInstance:
hIcon:
hCursor:
hbrBackground:
lpszMenuName:
lpszClassName:
hIconSm:
1.3 CreateWindowEx
HWND CreateWindowEx(
DWORD dwExStyle, // 窗口扩展样式
LPCSTR lpClassName, // 窗口类名
LPCSTR lpWindowName, // 窗口标题
DWORD dwStyle, // 窗口基本样式
int X, // 窗口的水平位置
int Y, // 窗口的垂直位置
int nWidth, // 窗口宽度
int nHeight, // 窗口高度
HWND hWndParent, // 父窗口句柄
HMENU hMenu, // 菜单句柄或子窗口ID
HINSTANCE hInstance, // 应用程序实例句柄
LPVOID lpParam // 创建窗口时传递给窗口的附加数据
);
1.3 什么是回调函数
回调函数是一种在程序中被传递并在特定事件或条件发生时执行的函数。简单来说,你可以把回调函数想象为一种“延迟调用”的方法,即你指定一个函数现在不立即执行,而是在将来某个特定的时刻自动执行。
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
// 根据消息类型进行分支处理
return DefWindowProc(hwnd, msg, wParam, lParam);
}
1.4 什么是消息
在 Windows 操作系统中,消息尤为重要。Windows 维护一个消息队列,用于存储待处理的各种事件消息。当事件发生(如用户的鼠标操作、键盘操作或其他系统事件)时,相应的消息会被生成并发送到消息队列中。应用程序通过一个称为消息循环的结构来检查这些消息,并将它们分派到相应的窗口过程(WNDPROC)进行处理。这些消息处理函数根据消息的类型来决定应该如何响应。
二、第一个窗口程序
#include <windows.h>
//显示错误信息
void ShowErrorMessage()
{
char szErrorMessage[512]; // 错误消息缓冲区
DWORD dwResult = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
0,
szErrorMessage,
sizeof(szErrorMessage) / sizeof(WCHAR),
NULL);
if (dwResult > 0) {
// 使用 MessageBox 显示错误消息
MessageBox(NULL, szErrorMessage, "Error", MB_OK | MB_ICONERROR);
}
else {
// 如果无法获取错误消息,使用 MessageBox 显示一个失败通知
MessageBox(NULL, "Failed to retrieve the error message.", "Error", MB_OK | MB_ICONERROR);
}
}
//5.消息处理
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
// 根据消息类型进行分支处理
if (msg == WM_CLOSE)
{
//向消息队列投递WM_QUIT消息
PostQuitMessage(0);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(
HINSTANCE hInstance, // 当前实例的句柄
HINSTANCE hPrevInstance, // 前一个实例的句柄,现在总是为 NULL
LPSTR lpCmdLine, // 命令行参数的字符串
int nCmdShow // 指示程序窗口应如何被显示
)
{
//1.注册窗口
char MyWindowClassName[] = "MyWindowClass"; //窗口类名
WNDCLASSEX wc = {
0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_VREDRAW | CS_HREDRAW; //
wc.lpfnWndProc = WndProc; //窗口过程函数(窗口回调函数->处理消息)
wc.hInstance = hInstance;
wc.hIcon = NULL; //图标
wc.hCursor = NULL; //光标
wc.hbrBackground = CreateSolidBrush(RGB(0,255,0)); //窗口背景颜色刷子
wc.lpszMenuName = NULL; //菜单名称
wc.lpszClassName = MyWindowClassName; //窗口类名
if (RegisterClassEx(&wc) == 0)
{
ShowErrorMessage();
return 0;
}
//2.创建窗口
char MyWindowName[] = "MyWindowClass"; //窗口名称
HWND hwnd = CreateWindowEx(
0,
MyWindowClassName,
MyWindowName,
WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_VISIBLE | WS_OVERLAPPED | WS_SYSMENU,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
if (hwnd == NULL)
{
ShowErrorMessage();
return 0;
}
//3.显示更新窗口
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
//4.消息循环(消息队列)
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, hwnd, 0, 0)) != 0)
{
if (bRet == -1)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
文章评论