Windows API窗口程序设计作业报告
一、项目概述
本次小组作业基于 Visual Studio 2022 和 C++ 开发了一个桌面窗口应用程序。作业要求程序具有灰色背景,窗口居中显示指定文字,单次播放背景音乐,并可通过按键动态改变窗口样式和鼠标指针形状。
具体实现功能如下:
- 窗口初始状态为标准样式(包含最小化、最大化、关闭按钮),背景色为灰色。
- 窗口客户区居中显示文字“大家好,这是我的第一个Windows API程序!”。
- 程序启动时异步单次播放背景音乐。
- 初始状态下鼠标进入窗口指针为标准箭头。
- 按下键盘空格键后,窗口样式改变为仅有标题栏和关闭按钮,同时鼠标指针变为手型。
二、效果展示
请在此处插入两张程序运行截图进行对比。


三、核心技术点解析
1. 窗口背景与初始设置
在注册窗口类时,使用 GetStockObject(GRAY_BRUSH) 将窗口背景设置为灰色。同时,将 wc.hCursor 设为 NULL,以便后续通过 WM_SETCURSOR 消息完全接管鼠标指针的动态变化。
2. 居中文本绘制
在 WM_PAINT 消息中,使用 DrawText 函数,配合 DT_CENTER | DT_VCENTER | DT_SINGLELINE 标志,并设置 SetBkMode(hdc, TRANSPARENT) 保证文字背景透明,从而实现文字的完美居中。
3. 单次异步播放背景音乐
引入 <mmsystem.h> 并链接 winmm.lib。在 WM_CREATE 中使用 PlaySound 函数。为了实现单次播放且不阻塞主程序运行,参数设置为 SND_FILENAME | SND_ASYNC。
4. 动态修改窗口样式与鼠标指针
在 WM_KEYDOWN 中监听空格键。按下空格后:
- 使用 SetWindowLongPtr 获取并修改窗口的 GWL_STYLE,通过位运算剔除 WS_THICKFRAME、WS_MINIMIZEBOX 和 WS_MAXIMIZEBOX,保留 WS_CAPTION 和 WS_SYSMENU。
- 调用 SetWindowPos 并附加 SWP_FRAMECHANGED 强制系统重绘非客户区。
- 改变全局状态变量 g_bIsHandCursor 为 TRUE,并调用 SetCursor 立即更新指针。
- 配合 WM_SETCURSOR 消息,当鼠标处于 HTCLIENT 区域时,根据全局状态动态设置光标(初始为 IDC_ARROW,改变后为 IDC_HAND)。
四、开发问题与解决记录
-
链接错误 LNK2019 和 LNK1120
由于Visual Studio默认创建的是控制台应用程序,链接器会寻找main函数而不是WinMain。在遇到属性页配置未生效的情况下,最终采用在代码顶部添加 #pragma comment(linker, "/SUBSYSTEM:WINDOWS") 直接指挥链接器,顺利解决了外部符号无法解析的错误。 -
鼠标指针动态变化失效
最初尝试在按键时调用 SetCursor,但鼠标移动后会被 WNDCLASS 中预设的光标覆盖。查阅 MSDN 后,改为拦截 WM_SETCURSOR 消息,判断 LOWORD(lParam) == HTCLIENT 时动态设置光标,彻底解决了该问题。 -
窗口样式改变无效
发现在最大化状态下修改窗口样式看不出变化。解决方案是在修改前先调用 ShowWindow(hWnd, SW_RESTORE) 还原窗口,同时使用 SetWindowLongPtr 代替旧版的 SetWindowLong,确保了在64位系统下的兼容性。
五、完整源代码
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")
BOOL g_bIsHandCursor = FALSE;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszClassName = L"FirstWin32App";
wc.hCursor = NULL;
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"注册窗口类失败!", L"错误", MB_OK | MB_ICONERROR);
return 0;
}
int screenW = GetSystemMetrics(SM_CXSCREEN);
int screenH = GetSystemMetrics(SM_CYSCREEN);
int winW = 800, winH = 600;
int posX = (screenW - winW) / 2;
int posY = (screenH - winH) / 2;
HWND hWnd = CreateWindow(
L"FirstWin32App",
L"Windows API程序设计",
WS_OVERLAPPEDWINDOW,
posX, posY, winW, winH,
NULL, NULL, hInstance, NULL
);
if (hWnd == NULL) {
return 0;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_CREATE:
PlaySound(L"bgm.wav", NULL, SND_FILENAME | SND_ASYNC);
break;
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT) {
if (g_bIsHandCursor) {
SetCursor(LoadCursor(NULL, IDC_HAND));
} else {
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
return TRUE;
}
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(0, 0, 0));
DrawText(hdc, L"大家好,这是我的第一个Windows API程序!", -1, &rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(hWnd, &ps);
break;
}
case WM_KEYDOWN:
if (wParam == VK_SPACE) {
ShowWindow(hWnd, SW_RESTORE);
LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
style &= ~(WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
SetWindowLongPtr(hWnd, GWL_STYLE, style);
SetWindowPos(hWnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
g_bIsHandCursor = TRUE;
SetCursor(LoadCursor(NULL, IDC_HAND));
}
break;
case WM_DESTROY:
PlaySound(NULL, NULL, 0);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

浙公网安备 33010602011771号