使用系统设备枚举器

[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayerIMFMediaEngine媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayerIMFMediaEngineMedia Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]

系统设备枚举器提供了一种统一的方法,用于按类别枚举在用户的系统上注册的筛选器。 此外,它区分各个硬件设备,即使同一筛选器支持它们。 这对于使用 Windows 驱动程序模型 (WDM) 和 KSProxy 筛选器的设备特别有用。 例如,用户可能有多个 WDM 视频捕获设备,所有这些设备都受同一筛选器支持。 系统设备枚举器将它们视为单独的设备实例。

系统设备枚举器的工作原理是为特定类别(例如音频捕获或视频压缩)创建枚举器。 类别枚举器为类别中的每个设备返回唯一的名字对象。 类别枚举器自动包括类别中任何相关的即插即用设备。 有关类别的列表,请参阅 筛选类别

若要使用系统设备枚举器,请执行以下操作:

  1. 通过调用 CoCreateInstance 创建系统设备枚举器。 CLSID_SystemDeviceEnum CLSID) (类标识符。
  2. 通过使用所需类别的 CLSID 调用 ICreateDevEnum::CreateClassEnumerator 来获取类别枚举器。 此方法返回 IEnumMoniker 接口指针。 如果类别为空 (或不存在) ,该方法将返回S_FALSE而不是错误代码。 如果是这样,则返回的 IEnumMoniker 指针为 NULL ,取消引用它将导致异常。 因此,在调用 CreateClassEnumerator 时显式测试S_OK,而不是调用通常的 SUCCEEDED 宏。
  3. 使用 IEnumMoniker::Next 方法枚举每个名字对象。 此方法返回 IMoniker 接口指针。 当 Next 方法到达枚举的末尾时,它还会返回S_FALSE,因此再次检查S_OK。
  4. 若要检索设备的友好名称 (例如,若要在用户界面) 中显示,请调用 IMoniker::BindToStorage 方法。
  5. 若要创建并初始化用于管理设备的 DirectShow 筛选器,请在名字对象上调用 IMoniker::BindToObject 。 调用 IFilterGraph::AddFilter 将筛选器添加到图中。

下图演示了此过程。

枚举设备

以下示例演示如何枚举安装在用户系统上的视频压缩器。 为简洁起见,该示例执行最小错误检查。

// Create the 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;
}

// Obtain a class enumerator for the video compressor category.
IEnumMoniker *pEnumCat = NULL;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat, 0);

if (hr == S_OK) 
{
    // Enumerate the monikers.
    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))
        {
            // To retrieve the filter's friendly name, do the following:
            VARIANT varName;
            VariantInit(&varName);
            hr = pPropBag->Read(L"FriendlyName", &varName, 0);
            if (SUCCEEDED(hr))
            {
                // Display the name in your UI somehow.
            }
            VariantClear(&varName);

            // To create an instance of the filter, do the following:
            IBaseFilter *pFilter;
            hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
                (void**)&pFilter);
            // Now add the filter to the graph. 
            //Remember to release pFilter later.
            pPropBag->Release();
        }
        pMoniker->Release();
    }
    pEnumCat->Release();
}
pSysDevEnum->Release();

设备名字对象

对于设备名字对象,可以将名字对象传递给 IFilterGraph2::AddSourceFilterForMoniker 方法,以便为设备创建捕获筛选器。 有关示例代码,请参阅该方法的文档。

IMoniker::GetDisplayName 方法返回名字对象的显示名称。 尽管显示名称是可读的,但你通常不会将其显示给最终用户。 改为从属性包获取友好名称,如前所述。

IMoniker::P arseDisplayName 方法或 MkParseDisplayName 函数可用于为给定筛选器类别创建默认设备名字对象。 使用格式 @device:*:{category-clsid}为 的显示名称,其中 category-clsid 是类别 GUID 的字符串表示形式。 默认名字对象是设备枚举器为该类别返回的第一个名字对象。