Windows API 获取所有进程音量并调节

 通过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;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值