设备属性(核心音频 API)

在枚举音频终结点设备的过程中,客户端应用程序可以询问终结点对象的设备属性。 设备属性在 MMDevice API 的 IPropertyStore 接口实现中公开。 给定终结点对象的 IMMDevice 接口引用后,客户端可通过调用 IMMDevice::OpenPropertyStore 方法来获取终结点对象属性存储的引用。

客户端可以读取这些属性,但不应进行设置。 属性值以 PROPVARIANT 结构进行存储。

终结点管理器为终结点设置基本设备属性。 终结点管理器是负责检测音频终结点设备是否存在的 Windows 组件。

以下列表中的每个 PKEY_Xxx 属性标识符都是 PROPERTYKEY 类型的常量,该常量在头文件 Functiondiscoverykeys_devpkey.h 中定义。 所有音频终结点设备都具有这些设备属性。

properties 说明
PKEY_DeviceInterface_FriendlyName 终结点设备所连接的音频适配器的友好名称(例如,“XYZ 音频适配器”)。
PKEY_Device_DeviceDesc 终结点设备的设备说明(例如“扬声器”)。
PKEY_Device_FriendlyName 终结点设备的友好名称(例如,“扬声器(XYZ 音频适配器)”)。
PKEY_Device_InstanceId 存储音频终结点设备实例标识符。 也可以通过 IMMDevice::GetId 方法来获取该值。 有关此属性的详细信息,请参阅 终结点 ID 字符串DEVPKEY_Device_InstanceId
PKEY_Device_ContainerId 存储实现音频终结点的 PnP 设备的容器标识符。 有关此属性的详细信息,请参阅 DEVPKEY_Device_ContainerId

某些音频终结点设备可能还具有未在前面列表中出现的其他属性。 有关其他属性的详细信息,请参阅音频终结点属性

有关 PROPERTYKEY 的详细信息,请参阅 Windows 属性系统文档

下面的代码示例将打印系统中所有音频呈现终结点设备的显示名称:

//-----------------------------------------------------------
// This function enumerates all active (plugged in) audio
// rendering endpoint devices. It prints the friendly name
// and endpoint ID string of each endpoint device.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hres)  \
              if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);

void PrintEndpointNames()
{
    HRESULT hr = S_OK;
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDeviceCollection *pCollection = NULL;
    IMMDevice *pEndpoint = NULL;
    IPropertyStore *pProps = NULL;
    LPWSTR pwszID = NULL;

    hr = CoCreateInstance(
           CLSID_MMDeviceEnumerator, NULL,
           CLSCTX_ALL, IID_IMMDeviceEnumerator,
           (void**)&pEnumerator);
    EXIT_ON_ERROR(hr)

    hr = pEnumerator->EnumAudioEndpoints(
                        eRender, DEVICE_STATE_ACTIVE,
                        &pCollection);
    EXIT_ON_ERROR(hr)

    UINT  count;
    hr = pCollection->GetCount(&count);
    EXIT_ON_ERROR(hr)

    if (count == 0)
    {
        printf("No endpoints found.\n");
    }

    // Each loop prints the name of an endpoint device.
    for (ULONG i = 0; i < count; i++)
    {
        // Get pointer to endpoint number i.
        hr = pCollection->Item(i, &pEndpoint);
        EXIT_ON_ERROR(hr)

        // Get the endpoint ID string.
        hr = pEndpoint->GetId(&pwszID);
        EXIT_ON_ERROR(hr)
        
        hr = pEndpoint->OpenPropertyStore(
                          STGM_READ, &pProps);
        EXIT_ON_ERROR(hr)

        PROPVARIANT varName;
        // Initialize container for property value.
        PropVariantInit(&varName);

        // Get the endpoint's friendly-name property.
        hr = pProps->GetValue(
                       PKEY_Device_FriendlyName, &varName);
        EXIT_ON_ERROR(hr)

        // GetValue succeeds and returns S_OK if PKEY_Device_FriendlyName is not found.
        // In this case vartName.vt is set to VT_EMPTY.      
        if (varName.vt != VT_EMPTY)
        {
            // Print endpoint friendly name and endpoint ID.
            printf("Endpoint %d: \"%S\" (%S)\n", 
                    i, varName.pwszVal, pwszID);
        }

        CoTaskMemFree(pwszID);
        pwszID = NULL;
        PropVariantClear(&varName);
        SAFE_RELEASE(pProps)
        SAFE_RELEASE(pEndpoint)
    }
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pCollection)
    return;

Exit:
    printf("Error!\n");
    CoTaskMemFree(pwszID);
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pCollection)
    SAFE_RELEASE(pEndpoint)
    SAFE_RELEASE(pProps)
}

前面的代码示例中的 FAILED 宏在头文件 Winerror.h 中定义。

在前面的代码示例中,PrintEndpointNames 函数中的 for 循环体调用了 IMMDevice::GetId 方法,以获取音频终结点设备的终结点 ID 字符串,该音频终结点设备由 IMMDevice 接口实例表示。 相对于系统中的所有其他音频终结点设备,该字符串可唯一标识设备。 客户端可以通过调用 IMMDeviceEnumerator::GetDevice 方法,在稍后时间或不同进程中使用终结点 ID 字符串来创建音频终结点设备实例。 客户端应将终结点 ID 字符串的内容视为不透明。 也就是说,客户端不应尝试解析字符串的内容来获取有关设备的信息。 原因是字符串格式并未定义,在不同的 MMDevice API 实现中可能会发生变化。

在前面的代码示例中,通过 PrintEndpointNames 函数获得的友好设备名称和终结点 ID 字符串与 DirectSound 在设备枚举过程中提供的友好设备名称和终结点 ID 字符串完全相同。 有关详细信息,请参阅传统音频应用程序的音频事件

在前面的代码示例中,PrintEndpointNames 函数调用 CoCreateInstance 函数,为系统中的音频终结点设备创建一个枚举器。 除非调用程序先前调用了 CoInitializeCoInitializeEx 函数来初始化 COM 库,否则 CoCreateInstance 调用将失败。 有关 CoCreateInstanceCoInitializeCoInitializeEx 的详细信息,请参阅 Windows SDK 文档。

有关 IMMDeviceEnumeratorIMMDeviceCollectionIMMDevice 接口的更多信息,请参阅 MMDevice API

音频终结点设备