通过windows API 实现一个微软自带的音量合成器,用户可以通过当前软件很方便调节其他应用程序的音量, 若用户通过windows自带的音量合成器进行调节 IAudioSessionEvents类可以收到该进程调节音量的回调。
上代码:
CAudioSessionEvent.h
#ifndef CAUDIOSESSIONEVENT_H
#define CAUDIOSESSIONEVENT_H
#include <audiopolicy.h>
#include <psapi.h>
#include <atlbase.h>
#include <mmdeviceapi.h>
#include <functional>
//-----------------------------------------------------------
// Client implementation of IAudioSessionEvents interface.
// WASAPI calls these methods to notify the application when
// a parameter or property of the audio session changes.
//-----------------------------------------------------------
class CAudioSessionEvents : public IAudioSessionEvents
{
LONG _cRef;
std::function<void(float NewVolume, BOOL NewMute, LPCGUID EventContext)> m_volumeChange_cb = nullptr;
public:
CAudioSessionEvents() :
_cRef(1)
{
}
~CAudioSessionEvents()
{
}
void setVolumeChangeCallBack(const std::function<void(float NewVolume, BOOL NewMute, LPCGUID EventContext)> &volumeChange) {
m_volumeChange_cb = volumeChange;
}
private:
// IUnknown methods -- AddRef, Release, and QueryInterface
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid,
VOID **ppvInterface)
{
if (IID_IUnknown == riid)
{
AddRef();
*ppvInterface = (IUnknown*)this;
}
else if (__uuidof(IAudioSessionEvents) == riid)
{
AddRef();
*ppvInterface = (IAudioSessionEvents*)this;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
// Notification methods for audio session events
HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(
LPCWSTR NewDisplayName,
LPCGUID EventContext)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnIconPathChanged(
LPCWSTR NewIconPath,
LPCGUID EventContext)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(
float NewVolume,
BOOL NewMute,
LPCGUID EventContext)
{
if (NewMute)
{
//printf("MUTE\n");
}
else
{
//printf("Volume = %d percent\n", (UINT32)(100*NewVolume + 0.5));
}
if (m_volumeChange_cb) {
m_volumeChange_cb(NewVolume, NewMute, EventContext);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(
DWORD ChannelCount,
float NewChannelVolumeArray[],
DWORD ChangedChannel,
LPCGUID EventContext)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(
LPCGUID NewGroupingParam,
LPCGUID EventContext)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnStateChanged(
AudioSessionState NewState)
{
/*
char *pszState = "?????";
switch (NewState)
{
case AudioSessionStateActive:
pszState = "active";
break;
case AudioSessionStateInactive:
pszState = "inactive";
break;
}
//printf("New session state = %s\n", pszState);
*/
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnSessionDisconnected(
AudioSessionDisconnectReason DisconnectReason)
{
/*
char *pszReason = "?????";
switch (DisconnectReason)
{
case DisconnectReasonDeviceRemoval:
pszReason = "device removed";
break;
case DisconnectReasonServerShutdown:
pszReason = "server shut down";
break;
case DisconnectReasonFormatChanged:
pszReason = "format changed";
break;
case DisconnectReasonSessionLogoff:
pszReason = "user logged off";
break;
case DisconnectReasonSessionDisconnected:
pszReason = "session disconnected";
break;
case DisconnectReasonExclusiveModeOverride:
pszReason = "exclusive-mode override";
break;
}
//printf("Audio session disconnected (reason: %s)\n", pszReason);
*/
return S_OK;
}
};
#endif // CAUDIOSESSIONEVENT_H
CAudioManager.h
#pragma once
#include <vector>
#include <list>
#include <iostream>
#include <memory>
#define interface struct
#include "CAudioSessionEvent.h"
#pragma comment(lib, "ole32.lib")
typedef std::function<void(float NewVolume, BOOL NewMute, LPCGUID EventContext)> AudioChangeCallBack;
typedef struct _ProcessInfo {
std::shared_ptr<TCHAR> szName;
DWORD dwPid = 0;
HICON hIcon;
}ProcessInfo;
class CAudioManager {
public:
CAudioManager();
~CAudioManager();
/*
* @brief 获取音频会话总数
*/
int getEnumSessionsCount();
/*
* @brief 获取音频会话程序信息
*/
HRESULT getSessionNameList(std::vector<ProcessInfo>& vSessionName);
/*
* @brief 获取指定音频会话音量
* @param Pid 进程id
* @param fLevel 音量等级 0.0 ~ 1.0
*/
HRESULT getVolume(DWORD Pid, float& fLevel);
/*
* @brief 设置指定音频会话音量
* @param Pid 进程id
* @param fLevel 音量等级 0.0 ~ 1.0
*/
HRESULT setVolume(DWORD Pid, float fLevel);
/*
* @brief 获取指定音频是否静音
* @param Pid 进程id
* @param mute 1 静音 0 不静音
*/
HRESULT getMute(DWORD Pid, int& mute);
/*
* @brief 设置指定音频是否静音
* @param Pid 进程id
* @param mute 1 静音 0 不静音
*/
HRESULT setMute(DWORD Pid, int mute);
/**
* @brief setVolumeChange 设置音频会话音量变化回调函数
* @param volumeChange
*/
void setVolumeChange(AudioChangeCallBack volumeChange) {
m_volumeChange_cb = volumeChange;
auto iter = m_audiosessionevents_list.begin();
for (int i = 0; i < m_audiosessionevents_list.size(); i++) {
iter->get()->setVolumeChangeCallBack(m_volumeChange_cb);
iter++;
}
}
private:
HRESULT CreateSessionManager();
HRESULT EnumSessions();
private:
CComQIPtr <IAudioSessionManager2> m_psession_manager = nullptr;
CComQIPtr <IMMDeviceEnumerator> m_pEnumerator = nullptr;
CComQIPtr <IMMDevice> m_pDevice = nullptr;
std::list <std::shared_ptr<CAudioSessionEvents>> m_audiosessionevents_list;
std::list <CComQIPtr <IAudioSessionControl>> m_audiosessioncontrol_list;
AudioChangeCallBack m_volumeChange_cb = nullptr;
};
CAudioManager.cpp
#include "CAudioManager.h"
// goto done
#define CHECK_HR(hr) \
if(hr != S_OK){ \
goto done; \
}
#define SAFE_RELEASE(p) \
if ((p)) { \
p->Release(); \
delete (p); \
(p) = NULL; \
}
typedef struct EnumHWndsArg {
std::vector<HWND> *vecHWnds;
DWORD dwProcessId;
}EnumHWndsArg, *LPEnumHWndsArg;
// 判断窗口是否属于目标进程
BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam) {
EnumHWndsArg *pArg = (LPEnumHWndsArg)lParam;
DWORD processId;
// 检索窗口线程标识符
GetWindowThreadProcessId(
hwnd, // 窗口句柄
&processId // 接收 PID 的指针
);
// 如果这个 HWND 属于这个 PID ,则加入到 vecHWnds 数组末尾
if (processId == pArg->dwProcessId) {
pArg->vecHWnds->push_back(hwnd);
}
return TRUE;
}
// 根据 PID 获取 HWND
void GetHWndsByProcessID(DWORD processID, std::vector<HWND> &vecHWnds) {
EnumHWndsArg wi;
wi.dwProcessId = processID;
wi.vecHWnds = &vecHWnds;
// 枚举所有顶级窗口
EnumWindows(
lpEnumFunc, // 回调函数指针
(LPARAM)&wi // 传递给回调函数的值
);
}
CAudioManager::CAudioManager() {
// CoUninitialize();
//HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
//if(hr != S_OK){
// qDebug() << "CoInitializeEx Fail";
//}
CreateSessionManager();
EnumSessions();
}
CAudioManager::~CAudioManager() {
m_audiosessioncontrol_list.clear();
m_audiosessionevents_list.clear();
//CoUninitialize();
}
int CAudioManager::getEnumSessionsCount() {
if (!m_psession_manager)
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
int cbSessionCount = 0;
CComQIPtr <IAudioSessionEnumerator> pSessionList;
CComQIPtr <IAudioSessionControl> pSessionControl;
// Get the current list of sessions.
CHECK_HR(hr = m_psession_manager->GetSessionEnumerator(&pSessionList));
// Get the session count.
CHECK_HR(hr = pSessionList->GetCount(&cbSessionCount));
done:
if (hr != S_OK) {
return -1;
}
return cbSessionCount;
}
HRESULT CAudioManager::getSessionNameList(std::vector<ProcessInfo>& vSessionName) {
if (!m_psession_manager) {
return E_INVALIDARG;
}
HRESULT hr = S_OK;
int cbSessionCount = 0;
CComQIPtr <IAudioSessionEnumerator> pSessionList;
// Get the current list of sessions.
CHECK_HR(hr = m_psession_manager->GetSessionEnumerator(&pSessionList));
// Get the session count.
CHECK_HR(hr = pSessionList->GetCount(&cbSessionCount));
for (int index = 0; index < cbSessionCount; index++)
{
CComQIPtr <IAudioSessionControl> pSessionControl;
DWORD dwPid = 0;
hr = pSessionList->GetSession(index, &pSessionControl);
CComQIPtr<IAudioSessionControl2> pAudioSessionControl2(pSessionControl);
if (pAudioSessionControl2 == NULL) {
continue;
}
hr = pAudioSessionControl2->GetProcessId(&dwPid);
if (hr != S_OK) {
continue;
}
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
std::vector<HWND> vecHWnds; // 进程下的窗体句柄数组
GetHWndsByProcessID(dwPid, vecHWnds);
// 获取 HWND 窗口标题、窗口类名
TCHAR szBuf_title[MAX_PATH];
TCHAR szBuf_class[MAX_PATH];
for (const HWND &h : vecHWnds) {
//TCHAR szDisName[MAX_PATH] = { 0 };
LPWSTR* szDisName;
//::GetWindowModuleFileName(h, szDisName, MAX_PATH);
//GetWindowText(
// h, // 窗口句柄
// szBuf_title, // 接收窗口标题的缓冲区指针
// MAX_PATH // 缓冲区字节大小
//);
//GetClassName(
// h, // 窗口句柄
// szBuf_class, // 接收窗口类名的缓冲区指针
// MAX_PATH // 缓冲区字节大小
//);
//if (wcscmp(szBuf_title, L"") == 0 || wcscmp(szBuf_title, L"") == 0)
// continue;
// 获取进程图标
//HICON hIcon = ::ExtractIcon(GetModuleHandle(NULL), process.szExeFile, 0);
//if (hIcon != nullptr) {
// QPixmap pixmap = QtWin::fromHICON(hIcon);
//}
std::shared_ptr<TCHAR> szName = std::shared_ptr<TCHAR>(new TCHAR[MAX_PATH], std::default_delete<TCHAR>());
DWORD nSize = 260;
//GetProcessImageFileNameW(hProcess, szName.get(), nSize);
::GetModuleFileNameEx(hProcess, NULL, szName.get(), MAX_PATH);
// 获取应用程序信息
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
SHGetFileInfo(szName.get(), 0, &sfi, sizeof(SHFILEINFO), SHGFI_ICON);
// 输出结果
//qDebug() << "szBuf_title = " << ConvertLPWSTRToLPSTR(szBuf_title);
//qDebug() << "szBuf_class = " << ConvertLPWSTRToLPSTR(szBuf_class);
//qDebug() << "--------------------------------------------" << endl;
// 获取标题
if (wcscmp(szBuf_class, L"") != 0) {
ProcessInfo info;
info.dwPid = dwPid;
info.szName = szName;
info.hIcon = sfi.hIcon;
vSessionName.push_back(info);
break;
}
}
}
done:
return hr;
}
HRESULT CAudioManager::getVolume(DWORD Pid, float& fLevel) {
if (!m_psession_manager) {
return E_INVALIDARG;
}
HRESULT hr = S_OK;
int cbSessionCount = 0;
CComQIPtr <IAudioSessionEnumerator> pSessionList;
// Get the current list of sessions.
CHECK_HR(hr = m_psession_manager->GetSessionEnumerator(&pSessionList));
// Get the session count.
CHECK_HR(hr = pSessionList->GetCount(&cbSessionCount));
for (int index = 0; index < cbSessionCount; index++)
{
CComQIPtr < IAudioSessionControl> pSessionControl;
DWORD dwPid = 0;
hr = pSessionList->GetSession(index, &pSessionControl);
CComQIPtr<IAudioSessionControl2> pAudioSessionControl2(pSessionControl);
if (pAudioSessionControl2 == NULL) {
continue;
}
hr = pAudioSessionControl2->GetProcessId(&dwPid);
if (hr != S_OK) {
continue;
}
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
TCHAR szName[MAX_PATH] = { 0 };
LPSTR name;
::GetModuleBaseName(hProcess, NULL, szName, MAX_PATH);
CComQIPtr<ISimpleAudioVolume> pSimpleAudioVolume(pAudioSessionControl2);
if (pSimpleAudioVolume == NULL) {
continue;
}
//if (wcscmp(szName, sessionName) == 0)
if(Pid == dwPid)
{
CComQIPtr<ISimpleAudioVolume> pSimpleAudioVolume(pAudioSessionControl2);
if (pSimpleAudioVolume == NULL) {
continue;
}
hr = pSimpleAudioVolume->GetMasterVolume(&fLevel);
break;
}
}
done:
return hr;
}
HRESULT CAudioManager::setVolume(DWORD Pid, float fLevel) {
if (!m_psession_manager){
return E_INVALIDARG;
}
HRESULT hr = S_OK;
int cbSessionCount = 0;
CComQIPtr <IAudioSessionEnumerator> pSessionList;
// Get the current list of sessions.
CHECK_HR(hr = m_psession_manager->GetSessionEnumerator(&pSessionList));
// Get the session count.
CHECK_HR(hr = pSessionList->GetCount(&cbSessionCount));
for (int index = 0; index < cbSessionCount; index++)
{
CComQIPtr < IAudioSessionControl> pSessionControl;
DWORD dwPid = 0;
hr = pSessionList->GetSession(index, &pSessionControl);
CComQIPtr<IAudioSessionControl2> pAudioSessionControl2(pSessionControl);
if (pAudioSessionControl2 == NULL) {
continue;
}
hr = pAudioSessionControl2->GetProcessId(&dwPid);
if (hr != S_OK) {
continue;
}
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
TCHAR szName[MAX_PATH] = { 0 };
LPSTR name;
::GetModuleBaseName(hProcess, NULL, szName, MAX_PATH);
CComQIPtr<ISimpleAudioVolume> pSimpleAudioVolume(pAudioSessionControl2);
if (pSimpleAudioVolume == NULL) {
continue;
}
//if (wcscmp(szName, sessionName) == 0)
if(Pid == dwPid)
{
CComQIPtr<ISimpleAudioVolume> pSimpleAudioVolume(pAudioSessionControl2);
if (pSimpleAudioVolume == NULL){
continue;
}
GUID guid;
guid.Data1 = Pid;
hr = pSimpleAudioVolume->SetMasterVolume(fLevel, &guid);
break;
}
}
done:
return hr;
}
HRESULT CAudioManager::getMute(DWORD Pid, int& mute) {
if (!m_psession_manager) {
return E_INVALIDARG;
}
HRESULT hr = S_OK;
int cbSessionCount = 0;
CComQIPtr <IAudioSessionEnumerator> pSessionList;
// Get the current list of sessions.
CHECK_HR(hr = m_psession_manager->GetSessionEnumerator(&pSessionList));
// Get the session count.
CHECK_HR(hr = pSessionList->GetCount(&cbSessionCount));
for (int index = 0; index < cbSessionCount; index++)
{
CComQIPtr < IAudioSessionControl> pSessionControl;
DWORD dwPid = 0;
hr = pSessionList->GetSession(index, &pSessionControl);
CComQIPtr<IAudioSessionControl2> pAudioSessionControl2(pSessionControl);
if (pAudioSessionControl2 == NULL) {
continue;
}
hr = pAudioSessionControl2->GetProcessId(&dwPid);
if (hr != S_OK) {
continue;
}
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
TCHAR szName[MAX_PATH] = { 0 };
LPSTR name;
::GetModuleBaseName(hProcess, NULL, szName, MAX_PATH);
CComQIPtr<ISimpleAudioVolume> pSimpleAudioVolume(pAudioSessionControl2);
if (pSimpleAudioVolume == NULL) {
continue;
}
//if (wcscmp(szName, sessionName) == 0)
if(Pid == dwPid)
{
CComQIPtr<ISimpleAudioVolume> pSimpleAudioVolume(pAudioSessionControl2);
if (pSimpleAudioVolume == NULL) {
continue;
}
hr = pSimpleAudioVolume->GetMute(&mute);
break;
}
}
done:
return hr;
}
HRESULT CAudioManager::setMute(DWORD Pid, int mute) {
if (!m_psession_manager) {
return E_INVALIDARG;
}
HRESULT hr = S_OK;
int cbSessionCount = 0;
CComQIPtr <IAudioSessionEnumerator> pSessionList;
// Get the current list of sessions.
CHECK_HR(hr = m_psession_manager->GetSessionEnumerator(&pSessionList));
// Get the session count.
CHECK_HR(hr = pSessionList->GetCount(&cbSessionCount));
for (int index = 0; index < cbSessionCount; index++)
{
CComQIPtr < IAudioSessionControl> pSessionControl;
DWORD dwPid = 0;
hr = pSessionList->GetSession(index, &pSessionControl);
CComQIPtr<IAudioSessionControl2> pAudioSessionControl2(pSessionControl);
if (pAudioSessionControl2 == NULL) {
continue;
}
hr = pAudioSessionControl2->GetProcessId(&dwPid);
if (hr != S_OK) {
continue;
}
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
TCHAR szName[MAX_PATH] = { 0 };
LPSTR name;
::GetModuleBaseName(hProcess, NULL, szName, MAX_PATH);
CComQIPtr<ISimpleAudioVolume> pSimpleAudioVolume(pAudioSessionControl2);
if (pSimpleAudioVolume == NULL) {
continue;
}
//if (wcscmp(szName, sessionName) == 0)
if (Pid == dwPid)
{
CComQIPtr<ISimpleAudioVolume> pSimpleAudioVolume(pAudioSessionControl2);
if (pSimpleAudioVolume == NULL) {
continue;
}
hr = pSimpleAudioVolume->SetMute(mute, NULL);
break;
}
}
done:
return hr;
}
HRESULT CAudioManager::CreateSessionManager()
{
HRESULT hr = S_OK;
// Create the device enumerator.
CHECK_HR(hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**)&m_pEnumerator));
// Get the default audio device.
CHECK_HR(hr = m_pEnumerator->GetDefaultAudioEndpoint(
eRender, eConsole, &m_pDevice));
// Get the session manager.
CHECK_HR(hr = m_pDevice->Activate(
__uuidof(IAudioSessionManager2), CLSCTX_ALL,
NULL, (void**)&m_psession_manager));
done:
return hr;
}
HRESULT CAudioManager::EnumSessions()
{
if (!m_psession_manager){
return E_INVALIDARG;
}
HRESULT hr = S_OK;
int cbSessionCount = 0;
CComQIPtr <IAudioSessionEnumerator> pSessionList;
// Get the current list of sessions.
CHECK_HR(hr = m_psession_manager->GetSessionEnumerator(&pSessionList));
// Get the session count.
CHECK_HR(hr = pSessionList->GetCount(&cbSessionCount));
for (int index = 0; index < cbSessionCount; index++)
{
CComQIPtr <IAudioSessionControl> pSessionControl;
CHECK_HR(hr = pSessionList->GetSession(index, &pSessionControl));
std::shared_ptr<CAudioSessionEvents> AudioSessionEvent = std::make_shared<CAudioSessionEvents>();
pSessionControl->RegisterAudioSessionNotification(AudioSessionEvent.get());
m_audiosessionevents_list.push_back(AudioSessionEvent);
m_audiosessioncontrol_list.push_back(pSessionControl);
}
done:
return hr;
}