直接枚举进程无法获取正在播放音频的进程,因为windows不通过enumprocesses等api暴露“是否播放音频”属性;真正反映音频活动的是core audio api中的音频会话(audio session),需通过iaudiosessionmanager2枚举活跃会话并筛选audiosessionstateactive状态,再结合getprocessid和queryfullprocessimagename获取对应进程路径。

为什么直接枚举进程无法拿到正在播放音频的进程
Windows 不会把“是否在播放音频”作为进程的公开属性暴露给 EnumProcesses 或 GetProcessImageFileName 这类 API。你看到的只是所有进程,没法区分哪个在放音乐、哪个在播视频、哪个只是静音挂着。真正能反映音频活动的是 Windows Core Audio API 中的「音频会话」(Audio Session)——每个活跃的音频流都绑定在一个会话上,而会话自带所属进程 ID 和可读名称。
用 IAudioSessionManager2 枚举所有活跃音频会话
核心路径是:获取系统默认音频终结点 → 创建 IAudioSessionManager2 → 用 GetSessionEnumerator 拿到所有会话 → 遍历并过滤出状态为 AudioSessionStateActive 的会话。
关键注意点:
-
IAudioSessionManager2必须通过IMMDevice::Activate获取,不能直接 CoCreate;设备类型要选eRender(播放设备),eCapture是录音,别混用 - 每个
IAudioSessionControl2可以调用GetProcessId拿到 PID,再用OpenProcess+GetModuleFileNameEx(或更可靠的QueryFullProcessImageName)获取进程路径 - 会话可能属于服务进程(如
svchost.exe)或已退出但会话未清理干净的僵尸项,需检查OpenProcess是否返回INVALID_HANDLE_VALUE
示例片段(省略 COM 初始化和错误检查):
IMMDeviceEnumerator* pEnumerator = nullptr;
CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
IMMDevice* pDevice = nullptr;
pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
IAudioSessionManager2* pSessionManager = nullptr;
pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, nullptr,
(void**)&pSessionManager);
ISessionEnumerator* pSessionEnumerator = nullptr;
pSessionManager->GetSessionEnumerator(&pSessionEnumerator);
int count = 0;
pSessionEnumerator->GetCount(&count);
for (int i = 0; i GetSession(i, &pSession);
AudioSessionState state;
pSession->GetState(&state);
if (state == AudioSessionStateActive) {
DWORD pid = 0;
pSession->GetProcessId(&pid); // 这就是你要的 PID
// 接着用 pid 查进程名
}
}
获取进程名时优先用 QueryFullProcessImageName 而非 GetModuleFileNameEx
GetModuleFileNameEx 在低完整性进程(如 Chrome 渲染器、Edge 子进程)或启用 PPL(Protected Process Light)的系统上常失败,返回空或报错 ERROR_ACCESS_DENIED。而 QueryFullProcessImageName 权限要求更低,且支持从任意进程句柄中安全提取镜像路径。
实操建议:
- 先用
OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid)—— 注意不是PROCESS_QUERY_INFORMATION,后者在 Win10+ 上被限制更严 - 再调
QueryFullProcessImageName,传入0作为标志位(表示获取完整路径) - 若仍失败(比如 PID 已退出),跳过该会话,不要硬 fallback 到
EnumProcesses查表,那会漏掉大量现代浏览器/多媒体应用
常见干扰项和静默陷阱
拿到 PID 后别急着显示名字。以下情况会导致结果不直观或误判:
- 同一个进程(如
chrome.exe)可能有多个活跃音频会话(不同标签页独立音频上下文),PID 相同但会话名不同,需按会话单独列出 -
SystemSounds类型会话(如系统提示音)PID 是 4(System),别当成普通用户进程处理 - 某些 UWP 应用(如 Xbox Game Bar、Your Phone)会话名是包名(如
Microsoft.XboxGamingOverlay_8wekyb3d8bbwe!App),不是传统 exe 名,需解析IAudioSessionControl2::GetIconPath或查注册表映射 - 后台音频(如 Spotify 后台播放)可能被系统归为
BackgroundCapable会话,但状态仍是Active,无需额外判断
实际调试时,建议先用 Windows Audio Session API Viewer(微软官方小工具)对照验证,避免自己代码把静音但未关闭的会话(AudioSessionStateInactive)也误抓进来。
最麻烦的不是拿不到数据,而是音频会话生命周期短、跨进程边界、权限碎片化——一个稳定输出,得同时兜住 COM 初始化、设备激活、会话状态轮询、进程信息降权查询四层逻辑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











