potisanのプログラミングメモ

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

C++11 WRLでWindows Vista以降のマルチメディアデバイスを操作する

はじめに

Microsoft Visual Studio 2013ではATLとよく似た機能を提供するWRL(Windows Runtime C++ Template Library)を利用することができます。特にExpress版を利用している場合はATLが同梱されないのでWRLが唯一の選択肢となります。

この投稿ではWindows Vista以降で採用された新しいマルチメディアデバイスの扱いの勉強を兼ねてWRLの簡単な使い方を具体例で紹介してみたいと思います。なお、MSDNやサンプルコードを読み込んだわけではないので間違いなどあるかもしれません。見つけたらご指摘頂けると幸いです。関数名なども即席なので同様に。

サンプルコード

先にサンプルコードを示します。

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

#include <memory>
#define STRICT
#include <Windows.h>
#include <wrl.h>
#include <propvarutil.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <functiondiscoverykeys_devpkey.h>

using namespace std;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Details;

// スコープ範囲でのCOMの初期化・解放を自動化します。
// ref. http://msdn.microsoft.com/ja-jp/library/jj822931(v=vs.110).aspx
class CoInitializeWrapper
{
private:
    HRESULT m_hr;
public:
    CoInitializeWrapper(DWORD dwCoInit = 0)
    {
        m_hr = CoInitializeEx(nullptr, dwCoInit);
    }

    ~CoInitializeWrapper()
    {
        if (SUCCEEDED(m_hr))
        {
            CoUninitialize();
        }
    }

    operator HRESULT() const { return m_hr; }
};

template <typename T> struct CoTaskMem_deleter
{
    CoTaskMem_deleter() {}
    void operator() (T* p) { CoTaskMemFree(p); }
};

HRESULT PropertyStoreReadStringValueAlloc(
    IN IPropertyStore* pPropStore,
    IN const PROPERTYKEY& key,
    OUT LPTSTR* ppsz);

LPTSTR PropertyStoreReadStringValueAllocAutoException(
    IN IPropertyStore* pPropStore,
    IN const PROPERTYKEY& key);

int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
    // COMの初期化
    CoInitializeWrapper initialize;
    if (FAILED(initialize)) RaiseException(initialize);

    // MMDeviceEnumeratorの作成
    // IID_PPV_ARGS:
    // http://msdn.microsoft.com/ja-jp/library/windows/desktop/ff485839(v=vs.85).aspx
    ComPtr<IMMDeviceEnumerator> deviceEnumerator;
    HRESULT hr = CoCreateInstance(
        __uuidof(MMDeviceEnumerator),
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&deviceEnumerator));
    if (FAILED(hr)) RaiseException(hr);

    // 規定のオーディオエンドポイントの取得
    // 今回はレンダー(出力)かつマルチメディア用のデバイスを取得。
    // IMMDeviceEnumerator:
    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd371402(v=vs.85).aspx
    ComPtr<IMMDevice> device;
    hr = deviceEnumerator->GetDefaultAudioEndpoint(
        EDataFlow::eRender, ERole::eMultimedia, &device);
    if (FAILED(hr)) RaiseException(hr);

    // デバイスIDの取得
    LPWSTR buffer = nullptr;
    hr = device->GetId(&buffer);
    unique_ptr<WCHAR, CoTaskMem_deleter<WCHAR>> deviceId(buffer);
    if (FAILED(hr)) RaiseException(hr);

    // デバイス状態を取得
    DWORD deviceState = 0;
    hr = device->GetState(&deviceState);
    if (FAILED(hr)) RaiseException(hr);

    // デバイスの各種プロパティを取得
    // PKEY_DeviceInterface_FriendlyName etc.:
    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd370812(v=vs.85).aspx
    ComPtr<IPropertyStore> propStore;
    hr = device->OpenPropertyStore(STGM_READ, &propStore);
    if (FAILED(hr)) RaiseException(hr);
    unique_ptr<WCHAR, CoTaskMem_deleter<WCHAR>> deviceinterface_userfrienlyname(
        PropertyStoreReadStringValueAllocAutoException(propStore.Get(),
        PKEY_DeviceInterface_FriendlyName));
    unique_ptr<WCHAR, CoTaskMem_deleter<WCHAR>> device_description(
        PropertyStoreReadStringValueAllocAutoException(propStore.Get(),
        PKEY_Device_DeviceDesc));
    unique_ptr<WCHAR, CoTaskMem_deleter<WCHAR>> device_userfrienlyname(
        PropertyStoreReadStringValueAllocAutoException(propStore.Get(),
        PKEY_Device_FriendlyName));

    return 0;
}

// IPropertyStoreから文字列プロパティを読み込んで返します。
// 取得した文字列はCoTaskMemFreeで解放して下さい。
HRESULT PropertyStoreReadStringValueAlloc(
    IN IPropertyStore* pPropStore,
    IN const PROPERTYKEY& key,
    OUT LPTSTR* ppsz)
{
    PROPVARIANT propvar;
    PropVariantInit(&propvar);
    HRESULT hr = pPropStore->GetValue(key, &propvar);
    if (SUCCEEDED(hr))
        hr = PropVariantToStringAlloc(propvar, ppsz);
    PropVariantClear(&propvar);
    return hr;
}

// IPropertyStoreから文字列プロパティの読み込んで返します。
// 取得した文字列はCoTaskMemFreeで解放して下さい。
// 内部で関数が失敗した場合はエラーコードを引数にMicrosoft::WRL::Details::RaiseException関数を呼び出します。
LPTSTR PropertyStoreReadStringValueAllocAutoException(
    IN IPropertyStore* pPropStore,
    IN const PROPERTYKEY& key)
{
    LPTSTR buffer = nullptr;
    HRESULT hr = PropertyStoreReadStringValueAlloc(
        pPropStore, key, &buffer);
    if (FAILED(hr))
        RaiseException(hr);
    return buffer;
}

ComPtr (Microsoft::WRL::ComPtr)

WRLが提供するATLのCComPtrに相当するクラスです。参照カウントの管理やスコープ範囲での自動解放等を行ってくれます。

MMDeviceEnumerator

IMMDeviceEnumeratorインターフェイスを実装するクラスです。マルチメディア関係のデバイスの列挙や規定のデバイスの取得を行うことができます。

以下、工事中――