はじめに
前回の投稿ではWRLとMMDevice APIを用いてデバイスとそのプロパティの取得を行いました。今回はそのコードを改変してデバイスの列挙を行います。
サンプルコード
以下にコードを示します。
#pragma comment(lib, "propsys.lib") #include <memory> #include <string> #include <sstream> #include <vector> #define STRICT #include <Windows.h> #include <propvarutil.h> #include <wrl.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 LPWSTR* ppsz); LPWSTR PropertyStoreReadStringValueAllocAutoException( IN IPropertyStore* pPropStore, IN const PROPERTYKEY& key); int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { // COMの初期化 CoInitializeWrapper initialize; if (FAILED(initialize)) RaiseException(initialize); // MMDeviceEnumeratorの作成 ComPtr<IMMDeviceEnumerator> deviceEnumerator; HRESULT hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator)); if (FAILED(hr)) RaiseException(hr); // 全ての状態の出力オーディオエンドポイントを列挙 ComPtr<IMMDeviceCollection> devices; hr = deviceEnumerator->EnumAudioEndpoints( EDataFlow::eAll, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_NOTPRESENT | DEVICE_STATE_UNPLUGGED, &devices); if (FAILED(hr)) RaiseException(hr); // デバイスを列挙してそれぞれの説明を取得 vector<wstring> device_descriptions; UINT uNumDevices; hr = devices->GetCount(&uNumDevices); if (FAILED(hr)) RaiseException(hr); for (UINT i = 0; i < uNumDevices; i++) { ComPtr<IMMDevice> device; hr = devices->Item(i, &device); ComPtr<IPropertyStore> propStore; hr = device->OpenPropertyStore(STGM_READ, &propStore); if (FAILED(hr)) RaiseException(hr); unique_ptr<WCHAR, CoTaskMem_deleter<WCHAR>> device_description( PropertyStoreReadStringValueAllocAutoException(propStore.Get(), PKEY_Device_DeviceDesc)); device_descriptions.push_back(device_description.get()); } // ','で区切って表示 wostringstream s; copy(device_descriptions.begin(), device_descriptions.end(), ostream_iterator<wstring, WCHAR>(s, L",")); MessageBox(HWND_DESKTOP, s.str().c_str(), nullptr, MB_OK); return 0; } // IPropertyStoreから文字列プロパティを読み込んで返します。 // 取得した文字列はCoTaskMemFreeで解放して下さい。 HRESULT PropertyStoreReadStringValueAlloc( IN IPropertyStore* pPropStore, IN const PROPERTYKEY& key, OUT LPWSTR* 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関数を呼び出します。 LPWSTR PropertyStoreReadStringValueAllocAutoException( IN IPropertyStore* pPropStore, IN const PROPERTYKEY& key) { LPTSTR buffer = nullptr; HRESULT hr = PropertyStoreReadStringValueAlloc( pPropStore, key, &buffer); if (FAILED(hr)) RaiseException(hr); return buffer; }
実行結果
スピーカー,マイク,ステレオ ミキサー,
IMMDeviceEnumerator.EnumAudioEndpointsメソッド
サンプルコードにあるように引数としてオーディオエンドポイントの種類(ERole
列挙型のメンバ)とステータスを要求します。
種類に関してはERole::eRender
, ERole::eCapture
, ERole::eAll
の何れも使用することができます。対して、ステータスは省略したつもりで0を指定すると失敗するので注意して下さい。全てのステータスのエンドポイントを列挙するにはサンプルコードのようにDEVICE_STATE_*を論理和演算|
でつなぎます。