2013-09-10_200213

如上图所示,Windows7下有一个很有趣的功能,就是可以给单独的进程调节音量。出于好奇,在网上翻了下资料,原来这个功能要归功于Windows7上新的音频接口——Core Audio APIs。这套API是用COM写,各种接口也比较多。但是如果我们的目的只是控制单个进程的音量,那还是很简单的。接下来的代码就是控制进程音量的函数了。

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioSessionManager2 = __uuidof(IAudioSessionManager2);
const IID IID_IAudioSessionControl2 = __uuidof(IAudioSessionControl2);
const IID IID_ISimpleAudioVolume = __uuidof(ISimpleAudioVolume);

BOOL SetProcessVolume(ULONG target_pid, float level)
{
    CComPtr imm_dev_enumor;

    HRESULT hr = imm_dev_enumor.CoCreateInstance(
        CLSID_MMDeviceEnumerator, NULL,
        CLSCTX_ALL);

    if (FAILED(hr)) {
        return FALSE;
    }

    CComPtr imm_dev;
    hr = imm_dev_enumor->GetDefaultAudioEndpoint(eRender, eMultimedia, &imm;_dev.p);
    if (FAILED(hr)) {
        return FALSE;
    }

    CComPtr session_mgr2;
    hr = imm_dev->Activate(IID_IAudioSessionManager2, CLSCTX_ALL, NULL, (void **)&session;_mgr2.p);
    if (FAILED(hr)) {
        return FALSE;
    }

    CComPtr session_enumor;
    hr = session_mgr2->GetSessionEnumerator(&session;_enumor.p);
    if (FAILED(hr)) {
        return FALSE;
    }

    int count;
    if (FAILED(hr)) {
        return FALSE;
    }

    hr = session_enumor->GetCount(&count;);
    for (int i = 0; i < count; i++) {
        CComPtr session_ctrl;
        hr = session_enumor->GetSession(i, &session;_ctrl.p);
        if (FAILED(hr)) {
            continue;
        }

        CComPtr session_ctrl2;
        hr = session_ctrl->QueryInterface(IID_IAudioSessionControl2, (void **)&session;_ctrl2.p);
        if (FAILED(hr)) {

        }

        ULONG pid;
        hr = session_ctrl2->GetProcessId(&pid;);
        if (FAILED(hr)) {
            continue;
        }


        if (pid != target_pid) {
            continue;
        }

        CComPtr simple_vol;
        hr = session_ctrl2->QueryInterface(IID_ISimpleAudioVolume, (void **)&simple;_vol.p);
        if (FAILED(hr)) {
            continue;
        }

        simple_vol->SetMasterVolume(level, NULL);
    }

    return TRUE;
}