Установить уровень громкости WMP

Что я хочу сделать, это установить уровень громкости проигрывателя Windows Media. По умолчанию громкость увеличивается / уменьшается на 10%, например, при нажатии на пункт меню "Вниз" или "Вверх" ("Пуск" -> "Громкость" -> "Вверх"), но, на мой взгляд, этого недостаточно, особенно если, например, звонить кому-то в слушать музыку).

Медиаплеер должен оставаться самостоятельным приложением.
В настоящее время я использую небольшой инструмент, который отправляет команды приложения через SendMessage в плеер с параметрами, как видно в Spy++.

Я думал о трех способах достижения моей цели:

  • использование WASAPI для получения аудиосеанса медиаплеера и динамической установки уровня громкости
  • отправка событий мыши вверх / вниз на ползунок громкости управления хостом медиаплеера по точкам
  • получение экземпляра управления медиаплеером через IWMPPlayer4
  • включая управление медиаплеером в моем приложении WPF на хосте форм Windows (не рекомендуется из-за потери независимости)

Точка 2 кажется довольно уродливой из-за того факта, что элемент управления медиаплеера является элементом COM и имеет настолько далеко, что spy++ отображает только один дескриптор, а это означает, что мне нужно будет определить точное положение ползунка громкости и отправить очень точные события мыши. Дополнительно, я не знаю, будет ли это работать вообще.

Точка 3 предполагает, что можно получить экземпляр элемента COM с помощью дескриптора. Поскольку я еще не работал с элементами COM, я не знаю, возможно ли это.
Обновление: можно получить экземпляр удаленного медиаплеера, используя IWMPPlayer4 интерфейс. Хотя я должен посмотреть, можно ли изменить настройки.

Пункт 1 производит на меня впечатление, что это было бы возможно без особых усилий. Хотя я столкнулся бы со следующей проблемой: идентификация аудио-сессии медиа-плееров. Перечисляя их, используя IAudioSessionManager2 и отображение имени с помощью

IAudioSessionControl2 ctrl2 = NULL;
// ...
hr = ctrl2->GetDisplayName(&name);

if (FAILED(hr))
{
    SafeRelease(ctrl);
    SafeRelease(ctrl2);
    continue;
}

String ^sessionName = gcnew String(name);
Console::WriteLine("Session name: '" + sessionName + "'");

печатает в большинстве случаев строку emtpy, за исключением Mozilla Firefox и системных звуков (другие процессы, возможно, сами не установили имя сеанса => выбрано имя по умолчанию и GetDisplayName возвращает пустую строку).

Обновление 2: как отметил Саймон Мурье, можно сравнить идентификаторы процессов, чтобы получить правильные ISimpleAudioVolume экземпляр, и он работает, насколько это касается WMP, чтобы принять изменения. Указанный экземпляр приобретается следующим образом:

IMMDeviceEnumerator *pEnumerator = NULL;
ISimpleAudioVolume *pVolume = NULL;
IMMDevice *pDevice = NULL;
IAudioSessionManager2 *pManager = NULL;
IAudioSessionEnumerator *pSessionEnumerator = NULL;
int sessionCount = 0;

CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
    CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
pEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia, &pDevice);
pDevice->GetState(&deviceState);
pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**)&pManager);
pManager->GetSessionEnumerator(&pSessionEnumerator);
pSessionEnumerator->GetCount(&sessionCount);

for (int i = 0; i < sessionCount; i++)
{
    IAudioSessionControl *ctrl = NULL;
    IAudioSessionControl2 *ctrl2 = NULL;
    DWORD processId = 0;

    hr = pSessionEnumerator->GetSession(i, &ctrl);

    if (FAILED(hr))
    {
        continue;
    }

    hr = ctrl->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&ctrl2);

    if (FAILED(hr))
    {
        SafeRelease(ctrl);
        continue;
    }

    hr = ctrl2->GetProcessId(&processId);

    if (FAILED(hr))
    {
        SafeRelease(ctrl);
        SafeRelease(ctrl2);
        continue;
    }

    if (processId == wmpProcessId)
    {
        hr = ctrl2->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&pVolume);
        SafeRelease(ctrl);
        SafeRelease(ctrl2);
        break;
    }

    SafeRelease(ctrl);
    SafeRelease(ctrl2);
}

При подъёме ISimpleAudioVolume экземпляр через IAudioClient необходимо предоставить идентификатор сеанса, чтобы об изменениях громкости сообщалось подписчикам событий. Возможно ли это с помощью этого подхода?

Хотя я знаю, что добавить элемент управления медиаплеера в мое приложение было бы самым простым способом, я бы не хотел использовать эту опцию, если это возможно.

1 ответ

Решение

Я не знаю, что могло произойти во время моей первоначальной попытки установить уровень громкости медиаплеера, но работает следующий код (исключая большинство обработок исключений):

HRESULT                 hr;
IMMDeviceEnumerator     *pEnumerator = NULL;
ISimpleAudioVolume      *pVolume = NULL;
IMMDevice               *pDevice = NULL;
IAudioSessionManager2   *pManager = NULL;
IAudioSessionEnumerator *pSessionEnumerator = NULL;
int                      sessionCount = 0;
int                      wmpProcess = GetWmpProcessId(); // Aquire WMPs process id

// Get the device enumerator and initialize the application for COM
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
         __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);

// Get the default device
hr = pEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender,
         ERole::eMultimedia, &pDevice);

// Get the session 2 manager
hr = pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL,
         NULL, (void**)&pManager);

// Get the session enumerator
hr = pManager->GetSessionEnumerator(&pSessionEnumerator);

// Get the session count
hr = pSessionEnumerator->GetCount(&sessionCount);

// Loop through all sessions
for (int i = 0; i < sessionCount; i++)
{
    IAudioSessionControl *ctrl = NULL;
    IAudioSessionControl2 *ctrl2 = NULL;
    DWORD processId = 0;

    hr = pSessionEnumerator->GetSession(i, &ctrl);

    if (FAILED(hr))
    {
        continue;
    }

    hr = ctrl->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&ctrl2);

    if (FAILED(hr))
    {
        SafeRelease(ctrl);
        continue;
    }

    //Identify WMP process
    hr = ctrl2->GetProcessId(&processId);

    if (FAILED(hr))
    {
        SafeRelease(ctrl);
        SafeRelease(ctrl2);
        continue;
    }

    if (processId != wmpProcess)
    {
        SafeRelease(ctrl);
        SafeRelease(ctrl2);
        continue;
    }

    hr = ctrl2->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&pVolume);

    if (FAILED(hr))
    {
        Error(hr, "Failed to get ISimpleAudioVolume.");

        SafeRelease(ctrl);
        SafeRelease(ctrl2);
        continue;
    }

    // Set the master volume
    hr = pVolume->SetMasterVolume(1.0, NULL);

    if (FAILED(hr))
    {
        Error(hr, "Failed to set the master volume.");
        SafeRelease(ctrl);
        SafeRelease(ctrl2);
        SafeRelease(pVolume);
        continue;
    }

    SafeRelease(ctrl);
    SafeRelease(ctrl2);
    SafeRelease(pVolume);
}
Другие вопросы по тегам