potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

C++20&WIL&Win API MMDevice APIでスピーカー(既定のマルチメディア出力)の表示名を取得する

Windows Vistaからはマスター音量(システム全体の音量)の変更にCOMベースのMMDevice API (Windows Multimedia Device API)が用いらます。ここではスピーカー(既定のマルチメディア出力)の表示名を取得するコードを記載します。

#define STRICT
#define NOMINMAX
#include <Windows.h>
// MMDeviceAPI
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <functiondiscoverykeys_devpkey.h>
// PropertySystem
#pragma comment(lib, "propsys.lib")
#include <propvarutil.h>
// WIL
#include <wil/com.h>

// 既定のマルチメディア出力オーディオエンドポイントを取得します。
wil::com_ptr<IMMDevice> GetDefaultMultimediaRenderAudioEndpoint()
{
    wil::com_ptr<IMMDeviceEnumerator> deviceEnumerator{
        wil::CoCreateInstance<MMDeviceEnumerator, IMMDeviceEnumerator>() };

    wil::com_ptr<IMMDevice> device;
    THROW_IF_FAILED(deviceEnumerator->GetDefaultAudioEndpoint(
        EDataFlow::eRender,
        ERole::eMultimedia,
        device.put()));

    return std::move(device);
}

// 既定のマルチメディア出力オーディオエンドポイントのプロパティストアを取得します。
wil::com_ptr<IPropertyStore> GetDefaultMultimediaRenderAudioEndpointPropertyStore(
    DWORD stgmAccess)
{
    auto device{ GetDefaultMultimediaRenderAudioEndpoint() };
    wil::com_ptr<IPropertyStore> props;
    THROW_IF_FAILED(device->OpenPropertyStore(stgmAccess, props.put()));
    return std::move(props);
}

// プロパティストアから値を文字列として取得します。
wil::unique_cotaskmem_string GetPropVariantValueAsString(
    IPropertyStore* propStore,
    REFPROPERTYKEY key)
{
    wil::unique_prop_variant propvar;
    wil::unique_cotaskmem_string value;
    THROW_IF_FAILED(propStore->GetValue(key, &propvar));
    THROW_IF_FAILED(PropVariantToStringAlloc(propvar, value.put()));
    return std::move(value);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    auto coinit{ wil::CoInitializeEx(COINIT_APARTMENTTHREADED) };
    auto propStore{ GetDefaultMultimediaRenderAudioEndpointPropertyStore(STGM_READ) };
    auto friendlyName{ GetPropVariantValueAsString(propStore.get(), PKEY_Device_FriendlyName) };

    ::MessageBoxW(nullptr, friendlyName.get(), L"", MB_OK);

    return 0;
}