System Device Enumerator の使い方

System Device Enumerator は、ユーザーのシステムに登録されているフィルタをカテゴリごとに列挙するための統一された方法を提供する。さらに、同じフィルタが複数のデバイスをサポートしている場合でも、それぞれのハードウェア デバイスを区別できる。このことは、Windows Driver Model (WDM) および KSProxy フィルタを使うデバイスの場合に特に有用である。たとえば、ユーザーが複数の WDM ビデオ キャプチャ デバイスを持ち、それらすべてが同じフィルタによってサポートされることがある。System Device Enumerator は、これらを別々のデバイス インターフェイスとして扱う。

System Device Enumerator は、オーディオ キャプチャやビデオ圧縮など、特定カテゴリ向けの列挙子を作成することで機能する。カテゴリ列挙子は、そのカテゴリ内の各デバイスに対する一意のモニカを返す。また、関連のあるプラグ アンド プレイ デバイスを自動的にカテゴリに含める。カテゴリのリストについては、「フィルタ カテゴリ」を参照すること。

System Device Enumerator を使うには、次の処理を行う。

  1. CoCreateInstance を呼び出して System Device Enumerator を作成する。クラス識別子 (CLSID) は CLSID_SystemDeviceEnum である。
  2. 目的のカテゴリの CLSID を使って ICreateDevEnum::CreateClassEnumerator を呼び出し、カテゴリ列挙子を入手する。このメソッドは IEnumMoniker インターフェイス ポインタを返す。カテゴリが空である (または存在しない) 場合、メソッドはエラー コードではなく S_FALSE を返す。この場合、返される IEnumMoniker ポインタは NULL であり、参照解除すると例外が発生する。したがって、CreateClassEnumerator を呼び出すときは、通常の SUCCEEDED マクロを呼び出すのではなく、S_OK であるかどうかを明示的にテストすること。
  3. IEnumMoniker::Next メソッドを使って各モニカを列挙する。このメソッドは IMoniker インターフェイス ポインタを返す。Next メソッドが列挙の最後に達すると、S_FALSE も返すので、もう一度 S_OK であるかどうかをチェックする。
  4. デバイスのフレンドリ名を取得するには (たとえば、ユーザー インターフェイスに表示するため)、IMoniker::BindToStorage メソッドを呼び出す。
  5. デバイスを管理する DirectShow フィルタを作成し、初期化するには、モニカに対して IMoniker::BindToObject を呼び出す。グラフにフィルタを追加するには、IFilterGraph::AddFilter を呼び出す。

次の図にこの処理を示す。

デバイスの列挙

次の例は、ユーザーのシステムにインストールされているビデオ コンプレッサを列挙する方法を示している。簡略化のため、このコードでは最小限のエラー チェックしか行わない。

// System Device Enumerator を作成する。
HRESULT hr;
ICreateDevEnum *pSysDevEnum = NULL;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
    IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
{
    return hr;
}

// ビデオ コンプレッサ カテゴリのクラス列挙子を取得する。
IEnumMoniker *pEnumCat = NULL;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat, 0);

if (hr == S_OK) 
{
    // モニカを列挙する。
    IMoniker *pMoniker = NULL;
    ULONG cFetched;
    while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
    {
        IPropertyBag *pPropBag;
        hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, 
            (void **)&pPropBag);
        if (SUCCEEDED(hr))
        {
            // フィルタのフレンドリ名を取得するには、次の処理を行う。
            VARIANT varName;
            VariantInit(&varName);
            hr = pPropBag->Read(L"FriendlyName", &varName, 0);
            if (SUCCEEDED(hr))
            {
                // なんらかの方法で UI に名前を表示する。
            }
            VariantClear(&varName);

            // フィルタのインスタンスを作成するには、次の処理を行う。
            IBaseFilter *pFilter;
            hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
                (void**)&pFilter);
            // ここでグラフにフィルタを追加する。
            // 後で忘れずに pFilter を解放すること。
            pPropBag->Release();
        }
        pMoniker->Release();
    }
    pEnumCat->Release();
}
pSysDevEnum->Release();

デバイス モニカ

IMoniker::GetDisplayName メソッドはモニカの表示名を返す。デバイス モニカの場合、表示名を IFilterGraph2::AddSourceFilterForMoniker に渡すと、デバイスのキャプチャ フィルタを作成できる。

LPOLESTR strName = NULL;
IBaseFilter pSrc = NULL;
hr = pMoniker->GetDisplayName(NULL, NULL, &strName);
if (SUCCEEDED(hr))
{
    // フィルタ グラフ マネージャに IFilterGraph2 を問い合わせる。
    IFilterGraph2 *pFG2 = NULL;
    hr = pGraph->QueryInterface(IID_IFilterGraph2, (void**)&pFG2);
    if (SUCCEEDED(hr))
    {
        hr = pFG2->AddSourceFilterForMoniker(pMoniker, 0, L"Source", &pSrc);
        pFG2->Release();
    }
    CoTaskMemFree(strName);
}

// 成功したら、忘れずに pSrc を解放すること。

表示名は読み取り可能だが、通常はエンド ユーザーに表示しない。前に説明したように、代わりにプロパティ バッグからフレンドリ名を取得する。

特定のフィルタ カテゴリのデフォルト デバイス モニカを作成するときは、IMoniker::ParseDisplayName メソッドまたは MkParseDisplayName 関数を使える。表示名には @device:*:{category-clsid} を使う。category-clsid はカテゴリ GUID の文字列表現である。デフォルト モニカは、そのカテゴリのデバイス列挙子が返す最初のモニカである。

たとえば、次のコードはビデオ キャプチャ カテゴリのデフォルト モニカを作成する。

// ビデオ キャプチャ カテゴリ。
WCHAR szMon[] = L"@device:*:{860BB310-5D01-11d0-BD3B-00A0C911CE86}";
IBindCtx *pBindCtx;
hr = CreateBindCtx(0, &pBindCtx);

ULONG chEaten = 0;
IMoniker *pMoniker = 0;
hr = MkParseDisplayName(pBindCtx, szMon, &chEaten, &pMoniker);
pBindCtx->Release();
if (SUCCEEDED(hr))
{
    // 表示名を取得するか、DirectShow フィルタにバインドする。
    pMoniker->Release();
}