potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

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

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

#include <format>

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

#include <wil/com.h>

// 既定のマルチメディア出力オーディオエンドポイントのボリューム情報インターフェイスを取得します。
wil::com_ptr<IAudioEndpointVolume> GetDefaultMultimediaRenderAudioEndpointVolume()
{
    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()));

    wil::com_ptr<IAudioEndpointVolume> audioEndpointVolume;
    THROW_IF_FAILED(device->Activate(
        __uuidof(IAudioEndpointVolume),
        CLSCTX_INPROC_SERVER,
        nullptr,
        audioEndpointVolume.put_void()));

    return std::move(audioEndpointVolume);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    auto coinit{ wil::CoInitializeEx(COINIT_APARTMENTTHREADED) };

    auto audioEndpointVolume{ GetDefaultMultimediaRenderAudioEndpointVolume() };

    // 音量の現在地と範囲を取得する
    float volumeLevel;
    float volumeMin;
    float volumeMax;
    float volumeInc;
    THROW_IF_FAILED(audioEndpointVolume->GetMasterVolumeLevel(&volumeLevel));
    THROW_IF_FAILED(audioEndpointVolume->GetVolumeRange(&volumeMin, &volumeMax, &volumeInc));

    auto msg{ std::format(L"音量レベル:{} Db\n最小値:{} Db\n最大値:{} Db\n増加量:{} Db",
        volumeLevel, volumeMin, volumeMax, volumeInc) };
    ::MessageBoxW(nullptr, msg.c_str(), nullptr, MB_OK);

    return 0;
}

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;
}

C++20&WIL&Win API MMDevice APIでミュートを切り替える

Windows Vistaからはマスター音量(システム全体の音量)の変更にCOMベースのMMDevice API (Windows Multimedia Device API)が用いらます。ここではミュートを切り替えるコードを記載します。

#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>

#include <wil/com.h>

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    auto coinit{ wil::CoInitializeEx(COINIT_APARTMENTTHREADED) };

    // デバイス列挙オブジェクトを取得する
    wil::com_ptr<IMMDeviceEnumerator> deviceEnumerator{
        wil::CoCreateInstance<MMDeviceEnumerator, IMMDeviceEnumerator>() };

    // TODO:ここでデバイス列挙オブジェクトを処理する
    //

    // 既定のマルチメディア出力デバイス(スピーカー)を取得する
    wil::com_ptr<IMMDevice> device;
    THROW_IF_FAILED(deviceEnumerator->GetDefaultAudioEndpoint(
        EDataFlow::eRender,
        ERole::eMultimedia,
        device.put()));

    // TODO:ここで既定のマルチメディア出力デバイス(スピーカー)を処理する
    //

    // オーディオエンドポイントのボリュームオブジェクトを作成する
    wil::com_ptr<IAudioEndpointVolume> audioEndpointVolume;
    THROW_IF_FAILED(device->Activate(
        __uuidof(IAudioEndpointVolume),
        CLSCTX_INPROC_SERVER,
        nullptr,
        audioEndpointVolume.put_void()));

    // TODO:ここでボリュームオブジェクトを処理する
    //

    // ミュート設定を反転する
    BOOL mute;
    THROW_IF_FAILED(audioEndpointVolume->GetMute(&mute));
    THROW_IF_FAILED(audioEndpointVolume->SetMute(!mute, nullptr));

    return 0;
}

C++20&Win API デバイス情報要素の文字列プロパティ(概要、クラス、クラスGUID、フレンドリーネーム、ハードウェアID)を列挙・整理する

バイス情報要素の文字列プロパティ(概要、クラス、クラスGUID、フレンドリーネーム、ハードウェアID)を列挙するコードです。再利用可能なコードをUtility.hppへ移動しています。

文字列プロパティ(概要、クラス、クラスGUID、フレンドリーネーム、ハードウェアID)を列挙する

Main.cpp

#pragma comment(lib, "SetupAPI.lib")
#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <SetupAPI.h>

#include "Utility.hpp"

#include <iostream>
#include <format>

int main()
{
    std::wcout.imbue(std::locale("", std::locale::ctype));

    auto DevInfoHandle{ unique_SetupDiGetClassDevsW(
        nullptr,
        nullptr,
        nullptr,
        DIGCF_ALLCLASSES) };

    // ループ中で繰り返し使用するバッファーの用意
    std::wstring DevDesc;
    std::wstring DevClass;
    std::wstring DevClassGuid;
    std::wstring DevFriendlyName;
    std::wstring DevHardwareId;

    std::wcout << L"ClassGuid, DevInst, SPDRP_DEVICEDESC, SPDRP_CLASS, SPDRP_CLASSGUID, SPDRP_FRIENDLYNAME, SPDRP_HARDWAREID" << std::endl;
    for (auto& DevInfoData : SetupDiCreateDeviceInfoDataList(DevInfoHandle.get()))
    {
        SetupDiGetDeviceRegistryPropertyAsStringW(DevDesc,
            DevInfoHandle.get(), &DevInfoData, SPDRP_DEVICEDESC);
        SetupDiGetDeviceRegistryPropertyAsStringW(DevClass,
            DevInfoHandle.get(), &DevInfoData, SPDRP_CLASS);
        SetupDiGetDeviceRegistryPropertyAsStringW(DevClassGuid,
            DevInfoHandle.get(), &DevInfoData, SPDRP_CLASSGUID);
        SetupDiGetDeviceRegistryPropertyAsStringW(DevFriendlyName,
            DevInfoHandle.get(), &DevInfoData, SPDRP_FRIENDLYNAME);
        SetupDiGetDeviceRegistryPropertyAsStringW(DevHardwareId,
            DevInfoHandle.get(), &DevInfoData, SPDRP_HARDWAREID);

        std::wcout
            << std::format(L"{}, {}, {}, {}, {}, {}, {}",
                GUIDToWString(DevInfoData.ClassGuid),
                DevInfoData.DevInst,
                DevDesc.c_str(),
                DevClass.c_str(),
                DevClassGuid.c_str(),
                DevFriendlyName.c_str(),
                DevHardwareId.c_str())
            << std::endl;
    }
}

Utility.hpp

//-----------------------------------------------------------------------------
// Utility.hpp
// 再利用可能なコードを定義します。
//-----------------------------------------------------------------------------

#pragma once

#include <bit>
#include <memory>
#include <string>
#include <numeric>
#include <list>

//-----------------------------------------------------------------------------
// GUID -> wstring
//-----------------------------------------------------------------------------

std::wstring GUIDToWString(const GUID& guid)
{
    std::wstring s(38, L'\0');
    if (FAILED(::StringFromGUID2(guid, s.data(), 38)))
        return {};
    return std::move(s);
}

//-----------------------------------------------------------------------------
// Setup API wrappers
//-----------------------------------------------------------------------------

struct HDEVINFO_deleter {
    void operator() (HDEVINFO handle) const noexcept {
        ::SetupDiDestroyDeviceInfoList(handle);
    }
};
using unique_HDEVINFO = std::unique_ptr<std::remove_pointer_t<HDEVINFO>, HDEVINFO_deleter>;

unique_HDEVINFO unique_SetupDiGetClassDevsW(
    const GUID* ClassGuid,
    LPCWSTR Enumerator,
    HWND hWndParent,
    DWORD Flags) noexcept
{
    return unique_HDEVINFO(::SetupDiGetClassDevsW(ClassGuid, Enumerator, hWndParent, Flags));
}

std::list<SP_DEVINFO_DATA> SetupDiCreateDeviceInfoDataList(HDEVINFO DeviceInfoSet)
{
    std::list<SP_DEVINFO_DATA> DevInfoDatas;
    for (DWORD i = 0; i < std::numeric_limits<DWORD>::max(); i++)
    {
        SP_DEVINFO_DATA DevInfoData{ sizeof(SP_DEVINFO_DATA) };
        if (!SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DevInfoData))
            break;
        DevInfoDatas.emplace_back(DevInfoData);
    }
    return std::move(DevInfoDatas);
}

BOOL SetupDiGetDeviceRegistryPropertyAsStringW(
    std::wstring& result,
    HDEVINFO DeviceInfoSet,
    PSP_DEVINFO_DATA DeviceInfoData,
    DWORD Property)
{
    DWORD RequiredSize;
    DWORD PropertyRegDataType;
    if (!::SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData,
        Property, &PropertyRegDataType, nullptr, 0, &RequiredSize)
        && ::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        return FALSE;
    }
    if (PropertyRegDataType != REG_SZ)
    {
        ::SetLastError(ERROR_INVALID_DATATYPE);
        return FALSE;
    }
    result.resize(RequiredSize);
    return ::SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData,
        Property, nullptr, std::bit_cast<PBYTE>(result.data()), result.size(), nullptr);
}

クラスを列挙する

//-----------------------------------------------------------------------------
// Utility.hpp
// 再利用可能なコードを定義します。
//-----------------------------------------------------------------------------

#pragma once

#include <bit>
#include <memory>
#include <string>
#include <numeric>
#include <list>

//-----------------------------------------------------------------------------
// GUID -> wstring
//-----------------------------------------------------------------------------

std::wstring GUIDToWString(const GUID& guid)
{
    std::wstring s(38, L'\0');
    if (FAILED(::StringFromGUID2(guid, s.data(), 38)))
        return {};
    return std::move(s);
}

//-----------------------------------------------------------------------------
// Setup API wrappers
//-----------------------------------------------------------------------------

struct HDEVINFO_deleter {
    void operator() (HDEVINFO handle) const noexcept {
        ::SetupDiDestroyDeviceInfoList(handle);
    }
};
using unique_HDEVINFO = std::unique_ptr<std::remove_pointer_t<HDEVINFO>, HDEVINFO_deleter>;

unique_HDEVINFO unique_SetupDiGetClassDevsW(
    const GUID* ClassGuid,
    LPCWSTR Enumerator,
    HWND hWndParent,
    DWORD Flags) noexcept
{
    return unique_HDEVINFO(::SetupDiGetClassDevsW(ClassGuid, Enumerator, hWndParent, Flags));
}

std::list<SP_DEVINFO_DATA> SetupDiCreateDeviceInfoDataList(HDEVINFO DeviceInfoSet)
{
    std::list<SP_DEVINFO_DATA> DevInfoDatas;
    for (DWORD i = 0; i < std::numeric_limits<DWORD>::max(); i++)
    {
        SP_DEVINFO_DATA DevInfoData{ sizeof(SP_DEVINFO_DATA) };
        if (!SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DevInfoData))
            break;
        DevInfoDatas.emplace_back(DevInfoData);
    }
    return std::move(DevInfoDatas);
}

BOOL SetupDiGetDeviceRegistryPropertyAsStringW(
    std::wstring& result,
    HDEVINFO DeviceInfoSet,
    PSP_DEVINFO_DATA DeviceInfoData,
    DWORD Property)
{
    DWORD RequiredSize;
    DWORD PropertyRegDataType;
    if (!::SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData,
        Property, &PropertyRegDataType, nullptr, 0, &RequiredSize)
        && ::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        return FALSE;
    }
    if (PropertyRegDataType != REG_SZ)
    {
        ::SetLastError(ERROR_INVALID_DATATYPE);
        return FALSE;
    }
    result.resize(RequiredSize);
    return ::SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData,
        Property, nullptr, std::bit_cast<PBYTE>(result.data()),
        static_cast<DWORD>(result.size()), nullptr);
}

クラスに対応するデバイス情報要素の概要を列挙する

#pragma comment(lib, "SetupAPI.lib")
#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <SetupAPI.h>

#include "Utility.hpp"

#include <iostream>
#include <set>

int main()
{
    std::wcout.imbue(std::locale("", std::locale::ctype));

    auto DevInfoHandle{ unique_SetupDiGetClassDevsW(
        nullptr,
        nullptr,
        nullptr,
        DIGCF_ALLCLASSES) };

    std::set<std::wstring> DevClassSet;
    std::wstring Buffer; // ループ中で繰り返し使用するバッファー
    for (auto& DevInfoData : SetupDiCreateDeviceInfoDataList(DevInfoHandle.get()))
    {
        SetupDiGetDeviceRegistryPropertyAsStringW(Buffer,
            DevInfoHandle.get(), &DevInfoData, SPDRP_CLASS);
        DevClassSet.emplace(Buffer.c_str());
    }

    // クラスの列挙
    for (const auto& DevClass : DevClassSet)
    {
        wcout << DevClass << endl;
    }
}

C++20&Win API Setup APIでシステムに存在するデバイスのクラス・インターフェイスのクラスと説明を列挙する

Setup APIでシステムに存在するデバイスのクラス・インターフェイスのクラスと説明を列挙するコードです。

単純に列挙する

#pragma comment(lib, "SetupAPI.lib")

#include <bit>
#include <format>
#include <string>

#define STRICT
#include <Windows.h>
#include <SetupAPI.h>
#include <InitGUID.h>
#include <devpkey.h>

// デバイスの文字列型プロパティを取得します。
std::wstring GetDevicePropertyString(
    HDEVINFO          DeviceInfoSet,
    PSP_DEVINFO_DATA  DeviceInfoData,
    const DEVPROPKEY* PropertyKey,
    DWORD             Flags)
{
    DEVPROPTYPE PropType;
    DWORD Size;
    if (!SetupDiGetDevicePropertyW(
        DeviceInfoSet, DeviceInfoData, PropertyKey, &PropType,
        nullptr, 0, &Size, 0)
        && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        return {};
    }
    std::wstring buffer(Size - 1, L'\0');
    if (!SetupDiGetDevicePropertyW(
        DeviceInfoSet, DeviceInfoData, PropertyKey, &PropType,
        std::bit_cast<PBYTE>(buffer.data()), Size, &Size, 0))
    {
        return {};
    }
    return std::move(buffer);
}

#include <iostream>

void main()
{
    std::wcout.imbue(std::locale("", std::locale::ctype));

    // 現在システムに存在する全てのクラス・インターフェイスを列挙
    std::wcout << L"DIGCF_ALLCLASSES | DIGCF_PRESENT" << std::endl;
    HDEVINFO DevInfoHandle = SetupDiGetClassDevs(
        nullptr, nullptr, nullptr,
        DIGCF_ALLCLASSES | DIGCF_PRESENT);
    SP_DEVINFO_DATA DevInfoData{ sizeof(SP_DEVINFO_DATA) };
    for (DWORD i = 0; SetupDiEnumDeviceInfo(DevInfoHandle, i, &DevInfoData); i++)
    {
        auto ClassName{ GetDevicePropertyString(
            DevInfoHandle,
            &DevInfoData,
            &DEVPKEY_Device_Class,
            0) };
        auto DeviceDesc{ GetDevicePropertyString(
            DevInfoHandle,
            &DevInfoData,
            &DEVPKEY_Device_DeviceDesc,
            0) };
        std::wcout
            << std::format(L"{} - {}", ClassName, DeviceDesc)
            << std::endl;
    }
    SetupDiDestroyDeviceInfoList(DevInfoHandle);
}

分類して列挙する

#include <bit>
#include <format>
#include <string>

#pragma comment(lib, "SetupAPI.lib")
#define STRICT
#include <Windows.h>
#include <SetupAPI.h>
#include <InitGUID.h>
#include <devpkey.h>

// デバイスの文字列型プロパティを取得します。
std::wstring GetDevicePropertyString(
    HDEVINFO          DeviceInfoSet,
    PSP_DEVINFO_DATA  DeviceInfoData,
    const DEVPROPKEY* PropertyKey,
    DWORD             Flags)
{
    DEVPROPTYPE PropType;
    DWORD Size;
    if (!SetupDiGetDevicePropertyW(
        DeviceInfoSet, DeviceInfoData, PropertyKey, &PropType,
        nullptr, 0, &Size, 0)
        && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        return {};
    }
    std::wstring buffer(Size - 1, L'\0');
    if (!SetupDiGetDevicePropertyW(
        DeviceInfoSet, DeviceInfoData, PropertyKey, &PropType,
        std::bit_cast<PBYTE>(buffer.data()), Size, &Size, 0))
    {
        return {};
    }
    return std::move(buffer);
}

#include <iostream>
#include <map>
#include <vector>

int main()
{
    // std::wcoutの日本語文字化けを回避
    std::wcout.imbue(std::locale("", std::locale::ctype));

    // 現在システムに存在する全てのクラス・インターフェイスを列挙
    // クラス毎に説明をまとめる
    std::wcout << "DIGCF_ALLCLASSES | DIGCF_PRESENT" << std::endl;
    HDEVINFO DevInfoHandle = SetupDiGetClassDevs(
        nullptr, nullptr, nullptr,
        DIGCF_ALLCLASSES | DIGCF_PRESENT);
    SP_DEVINFO_DATA DevInfoData = { sizeof SP_DEVINFO_DATA };
    std::map<std::wstring, std::vector<std::wstring>> ClassDescMap;
    for (DWORD i = 0; SetupDiEnumDeviceInfo(DevInfoHandle, i, &DevInfoData); i++)
    {
        auto ClassName{ GetDevicePropertyString(
            DevInfoHandle, &DevInfoData, &DEVPKEY_Device_Class, 0) };
        auto DeviceDesc{ GetDevicePropertyString(
            DevInfoHandle, &DevInfoData, &DEVPKEY_Device_DeviceDesc, 0) };
        ClassDescMap[ClassName].push_back(DeviceDesc);
    }
    SetupDiDestroyDeviceInfoList(DevInfoHandle);

    // 列挙したクラス毎の説明を出力
    for (const auto& ClassDescPair : ClassDescMap)
    {
        std::wcout << ClassDescPair.first.c_str() << std::endl;
        for (const auto& Desc : ClassDescPair.second)
        {
            std::wcout << std::format(L"\t{}", Desc.c_str()) << std::endl;
        }
    }

    return 0;
}