Установить уровень громкости 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);
}