Windows API程序|使用Win32 API制作带音频播放的窗口程序

Windows API程序|使用Win32 API制作带音频播放的窗口程序

📅实验时间:2026‑09‑23
🛠开发环境:Visual Studio 2022、Windows10/Windows11、C语言 Win32 API
✨实现功能:灰色背景窗口、居中文字、手型光标、隐藏最大化/最小化/关闭按钮、MCI播放wav背景音乐、ESC按键关闭窗口、资源释放与错误提示

一、实验目标

本次练习使用C语言与Windows Win32 API编写简单桌面GUI程序,理解Windows消息驱动模型,掌握窗口完整开发流程:窗口类注册、窗口创建、消息循环、窗口回调处理。
程序需要完成下面全部功能:

  1. 创建标题为我的第一个Win窗口的窗口,窗口大小650×320;
  2. 灰色客户区背景,窗口正中央显示一行中文文本;
  3. 鼠标移入窗口客户区显示手型光标;
  4. 标题栏只保留标题,不显示最小化、最大化、关闭按钮;
  5. 使用MCI多媒体接口播放外部hellowin.wav音频作为背景音乐;
  6. 按下键盘ESC键关闭窗口;程序退出时释放音频资源;
  7. 增加音频打开失败错误弹窗提示。

二、前置知识

Windows应用程序和控制台程序不一样,控制台程序入口是main(),Win32窗口程序入口函数为WinMain。
Windows是消息驱动机制:操作系统发生事件(鼠标移动、按键按下、窗口重绘)就会生成消息,程序通过消息循环不停获取消息,分发到窗口过程函数WndProc,在回调函数内部对不同消息做对应处理。

核心组件:

  1. WNDCLASSEX结构体:定义窗口类,设置背景色、光标、图标、窗口过程回调函数;
  2. RegisterClassEx():向操作系统注册自定义窗口类;
  3. CreateWindowEx():根据注册好的窗口类,生成窗口实例;
  4. 消息循环GetMessage‑TranslateMessage‑DispatchMessage:程序的心脏,接收并转发系统消息;
  5. WndProc窗口过程回调函数:处理WM_PAINT绘图、WM_KEYDOWN键盘、WM_DESTROY销毁窗口等各类消息;
  6. MCI接口:mciSendString多媒体命令接口,用来打开、播放、关闭wav音频,需要链接winmm.lib库。

⚠注意:MCI原生只支持PCM编码的wav文件,其他压缩格式wav无法正常播放。

三、项目文件结构

Project1
└── x64/Debug
    ├─ Project1.exe      //编译生成可执行程序
    ├─ Project1.pdb
    └─ hellowin.wav      //wav音频文件,放到exe同一目录

音频文件必须放在程序运行输出目录,或者代码写完整绝对路径,否则会报音频打开失败。

四、详细操作步骤

步骤1:新建VS空项目

  1. 打开 Visual Studio 2022,点击【创建新项目】,选择 空项目(C++),点击下一步;
  2. 设置项目名称(例:Project1),选择保存路径,取消“将解决方案和项目放在同一目录中”,点击创建。

步骤2:添加C++源文件

  1. 在右侧【解决方案资源管理器】,右键源文件 →【添加】→【新建项】;
  2. 选择C++文件(.cpp),文件名填写main.cpp,点击添加;
  3. 将完整源代码复制粘贴到main.cpp文件中。

步骤3:准备wav音频文件

  1. 准备PCM编码的wav音频,命名为hellowin.wav;
  2. 找到项目输出目录:项目目录\x64\Debug\;
  3. 将hellowin.wav复制粘贴到Debug文件夹,和生成的exe放在同一级目录。

也可以直接使用绝对路径写在代码open指令内,注意Windows路径反斜杠需要写两个\\。

步骤4:确认多媒体库链接

代码头部已经写了:

#pragma comment(lib, "winmm.lib")

这条指令作用:编译时自动链接多媒体库,不需要手动去项目属性配置链接器输入。

步骤5:修改源码编码,预防中文乱码

  1. VS顶部菜单栏:【文件】→【高级保存选项】;
  2. 编码选择 Unicode(UTF‑8带签名)‑代码页65001,点击确定保存文件。

步骤6:编译运行程序

  1. 平台选择x64(不要选x86);
  2. 点击菜单栏【生成】→【生成解决方案】;
  3. 编译无报错后,点击本地Windows调试器运行程序。

✅预期运行效果:
弹出灰色窗口,标题“我的第一个Win窗口”;窗口中间居中显示中文;鼠标进入窗口光标变为手型;窗口右上角没有最小化、最大化、关闭按钮;自动播放wav背景音乐;按下键盘ESC键,窗口关闭,音频资源释放。

五、完整源代码

#include <windows.h>
#include <mmsystem.h>

//链接多媒体库,提供MCI音频播放支持
#pragma comment(lib, "winmm.lib")

//窗口过程函数声明,处理全部窗口消息
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

//WinMain:Windows GUI程序入口,等价控制台main
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    const TCHAR szClassName[] = TEXT("MyWinClass");
    HWND hWnd;
    MSG msg;
    WNDCLASSEX wndClass;

    //1.填充窗口类结构体
    wndClass.cbSize = sizeof(WNDCLASSEX);
    wndClass.style = CS_HREDRAW | CS_VREDRAW; //窗口大小改变自动重绘
    wndClass.lpfnWndProc = WndProc;           //绑定消息回调函数
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;           //当前程序实例句柄
    wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndClass.hCursor = LoadCursor(NULL, IDC_HAND); //手型光标
    wndClass.hbrBackground = (HBRUSH)(COLOR_GRAYTEXT + 1); //灰色背景
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = szClassName;
    wndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    //注册窗口类
    RegisterClassEx(&wndClass);

    //2.创建窗口
    //WS_CAPTION:仅标题栏;不添加WS_SYSMENU,去掉右上角三个系统按钮
    hWnd = CreateWindowEx(
        0,
        szClassName,
        TEXT("我的第一个Win窗口"),
        WS_CAPTION,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        650,
        320,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    //MCI打开wav音频,设置别名music
    MCIERROR mciRet = mciSendString(TEXT("open D:\\Users\\1\\source\\repos\\Project1\\x64\\Debug\\hellowin.wav alias music"), NULL, 0, NULL);
    if (mciRet != 0)
    {
        TCHAR errBuf[256];
        mciGetErrorString(mciRet, errBuf, 256);
        MessageBox(hWnd, errBuf, TEXT("Audio Open Error"), MB_ICONERROR);
    }
    else
    {
        mciSendString(TEXT("set music notify on"), NULL, 0, NULL);
        mciSendString(TEXT("play music"), NULL, 0, NULL);
    }

    //消息循环,程序主循环
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    //程序退出,关闭释放音频资源
    mciSendString(TEXT("close music"), NULL, 0, NULL);
    return (int)msg.wParam;
}

//窗口回调过程,处理各类窗口消息
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;
    TCHAR szText[] = TEXT("大家好,这是我的第一个Windows API程序!");

    switch (msg)
    {
    //窗口重绘消息,绘制居中文字
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        GetClientRect(hWnd, &rect);
        //单行、水平居中、垂直居中绘制文本
        DrawText(hdc, szText, lstrlen(szText), &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        EndPaint(hWnd, &ps);
        return 0;

    //窗口销毁,发送退出消息终止消息循环
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    //键盘按下消息:ESC键销毁窗口
    case WM_KEYDOWN:
        if (wParam == VK_ESCAPE)
        {
            DestroyWindow(hWnd);
        }
        return 0;
    }
    //其余未处理消息交给系统默认处理
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

六、实验常见问题与完整解决方案

🎵问题1:音乐播放不成功,弹出Audio Open Error报错弹窗

现象:程序运行直接弹出音频错误提示框,没有背景音乐。

产生原因分为4种:

  1. 文件路径错误;Windows字符串路径必须写双反斜杠\\,单斜杠\会被识别为转义字符;
  2. hellowin.wav不在x64/Debug输出目录,exe找不到音频文件;
  3. wav音频编码不是PCM编码,MCI接口不支持压缩格式wav;
  4. 文件名大小写不匹配,例如代码写hellowin.wav,实际文件是Hellowin.wav。

✅解决办法:

  1. 优先把wav复制到exe同一Debug文件夹;写绝对路径时确认路径,路径分隔符必须是\\;
  2. 查看音频属性,确认编码为PCM;如果不是,使用格式工厂转换音频编码为PCM‑wav;
  3. 核对文件名大小写,严格和代码内名称保持一致;
  4. 利用弹窗返回的错误信息定位,弹窗会直接输出MCI错误描述。

🀄问题2:窗口中文乱码,C4819编译器警告,中文变成方框问号

现象:窗口上中文文本显示乱码,VS输出窗口出现警告C4819: 该文件包含不能在当前代码页(936)中表示的字符。

原因:源代码保存编码为UTF‑8无BOM,VS编译器默认使用GBK(936代码页)解析源码,中文字符解析失败。

✅解决办法:

  1. VS菜单栏【文件】→【高级保存选项】;
  2. 编码选择:Unicode(UTF‑8带签名)‑代码页65001,点击确定保存文件;
  3. 全部关闭文件,重新打开,再次编译运行。

不要使用UTF‑8无BOM,VS的MSVC编译器对无BOM的UTF‑8源码中文兼容性很差。

🔨问题3:LNK1104链接错误,无法打开Project1.exe

现象:编译报错 LNK1104 无法打开文件“x64\Debug\Project1.exe”。

原因:上一次运行的窗口程序没有正常关闭,进程还驻留在内存,exe文件被进程占用,VS不能覆盖生成新可执行文件。

✅解决办法:

  1. Ctrl+Shift+Esc打开任务管理器;
  2. 在进程列表找到Project1.exe,右键结束任务;
  3. 返回VS,点击【生成】→【重新生成解决方案】。

🎶问题4:音频只播放一遍,不能循环播放

现象:背景音乐只播放一次之后静音。

原因:代码开启set music notify on,但是窗口回调函数WndProc没有处理MM_MCINOTIFY播放完成消息,音频播放结束后没有重新发起play命令。

✅拓展解决方案(实现循环播放):
在WndProc的switch增加MM_MCINOTIFY消息分支:

case MM_MCINOTIFY:
    if(wParam == MCI_NOTIFY_SUCCESSFUL)
    {
        //播放完成,重新播放,实现循环
        mciSendString(TEXT("play music notify on"),NULL,0,NULL);
    }
    return 0;

注意:需要把set music notify on,play指令带上notify参数。

⌨问题5:按下ESC键窗口没有关闭

现象:敲击键盘ESC,程序窗口毫无反应。

原因:窗口没有获得焦点,键盘消息不会发送到此窗口;或者代码写错虚拟键值VK_ESCAPE。

✅解决办法:点击窗口灰色区域激活窗口焦点再按ESC;检查代码判断条件if(wParam == VK_ESCAPE),不要写错宏名。

🖼问题6:右上角还存在关闭按钮

现象:窗口右上角仍然看见关闭×按钮。

原因:创建窗口CreateWindowEx的窗口样式参数,多写了WS_SYSMENU。

✅解决办法:窗口样式只保留WS_CAPTION,删除WS_SYSMENU宏。

七、知识点总结

  1. Windows GUI程序基于消息驱动,WinMain是程序入口,消息循环是程序主体,所有交互都依靠消息分发。
  2. WNDCLASSEX用来定义窗口类,注册之后才能创建窗口实例;窗口的绘制工作写在WM_PAINT消息分支。
  3. CreateWindowEx的窗口样式参数非常关键:去掉WS_SYSMENU就可以移除右上角最小化最大化关闭按钮。
  4. MCI多媒体接口适合简单音频播放,注意必须程序退出调用close释放音频资源,避免资源泄露。
  5. Windows开发很容易遇到编码、文件路径、进程占用类问题,要充分利用弹窗报错、编译器警告信息定位问题。

📌拓展改进方向:实现音频循环播放;增加按钮控件;增加播放暂停控制;异常处理进一步完善。


复制全部内容直接粘贴到CSDN编辑器,全部Markdown语法完全兼容。如果你需要,我还可以帮你把实验报告的小结也提取出来。

posted @ 2026-09-24 18:13  才知风自由  阅读(5)  评论(0)    收藏  举报