0

我想要做的是设置 Windows Media Player 的音量级别。默认情况下,当单击向下或向上菜单项(播放 -> 音量 -> 向上)时,音量会降低 10%,但在我看来,这还不够好(尤其是在与某人进行 Skype 通话时听音乐)。

媒体播放器应该是一个独立的应用程序。
目前我正在使用一个小工具,通过 SendMessage 向播放器发送应用程序命令,其参数如 spy++ 中所示。

我想了三种方法来实现我的目标:

  • 使用 WASAPI 获取媒体播放器的音频会话并动态设置音量级别
  • 按点向媒体播放器主机控件的音量滑块发送鼠标向下/向上事件
  • 通过 IWMPPlayer4 获取媒体播放器控件的实例
  • 在 Windows 窗体主机中的 WPF 应用程序中包含媒体播放器控件(由于失去独立性而不是首选)

第 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 + "'");

大多数时候打印一个空字符串,除了 Mozilla Firefox 和系统声音(其他进程可能没有自己设置会话名称 => 选择默认名称并GetDisplayName返回一个空字符串)。

更新 2: 正如 Simon Mourier 指出的那样,人们可以比较进程 id 以获得正确的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必须提供一个会话 id 才能将音量变化报告给事件订阅者。这可能使用这种方法吗?

虽然我知道向我的应用程序添加媒体播放器控件是最简单的方法,但如果可能,我不想使用此选项。

4

1 回答 1

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);
}
于 2015-09-21T14:57:27.100 回答