旧版 Windows 多媒体应用程序的设备角色

注意

MMDevice API 支持设备角色。 但是,Windows Vista 中的用户界面不支持此功能。 在将来的 Windows 版本中,可能会实现对设备角色的用户界面支持。 有关详细信息,请参阅 Windows Vista 中的设备角色。

 

旧版 Windows 多媒体 waveOutXxxwaveInXxx 功能无法让应用程序选择用户分配给特定 设备角色音频终结点 设备。 但是,在 Windows Vista 中,核心音频 API 可与 Windows 多媒体应用程序结合使用,以基于设备角色启用设备选择。 例如,在 MMDevice API的帮助下,waveOutXxx 应用程序可以识别分配给角色的音频终结点设备,识别相应的波形输出设备,并调用 waveOutOpen 函数打开设备的实例。 有关 waveOutXxxwaveInXxx的详细信息,请参阅 Windows SDK 文档。

下面的代码示例演示如何获取分配给特定设备角色的呈现终结点设备的波形设备 ID:

//-----------------------------------------------------------
// This function gets the waveOut ID of the audio endpoint
// device that is currently assigned to the specified device
// role. The caller can use the waveOut ID to open the
// waveOut device that corresponds to the endpoint device.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hres)  \
              if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

HRESULT GetWaveOutId(ERole role, int *pWaveOutId)
{
    HRESULT hr;
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    WCHAR *pstrEndpointIdKey = NULL;
    WCHAR *pstrEndpointId = NULL;

    if (pWaveOutId == NULL)
    {
        return E_POINTER;
    }

    // Create an audio endpoint device enumerator.
    hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                          NULL, CLSCTX_INPROC_SERVER,
                          __uuidof(IMMDeviceEnumerator),
                          (void**)&pEnumerator);
    EXIT_ON_ERROR(hr)

    // Get the audio endpoint device that the user has
    // assigned to the specified device role.
    hr = pEnumerator->GetDefaultAudioEndpoint(eRender, role,
                                              &pDevice);
    EXIT_ON_ERROR(hr)

    // Get the endpoint ID string of the audio endpoint device.
    hr = pDevice->GetId(&pstrEndpointIdKey);
    EXIT_ON_ERROR(hr)

    // Get the size of the endpoint ID string.
    size_t  cbEndpointIdKey;

    hr = StringCbLength(pstrEndpointIdKey,
                        STRSAFE_MAX_CCH * sizeof(WCHAR),
                        &cbEndpointIdKey);
    EXIT_ON_ERROR(hr)

    // Include terminating null in string size.
    cbEndpointIdKey += sizeof(WCHAR);

    // Allocate a buffer for a second string of the same size.
    pstrEndpointId = (WCHAR*)CoTaskMemAlloc(cbEndpointIdKey);
    if (pstrEndpointId == NULL)
    {
        EXIT_ON_ERROR(hr = E_OUTOFMEMORY)
    }

    // Each for-loop iteration below compares the endpoint ID
    // string of the audio endpoint device to the endpoint ID
    // string of an enumerated waveOut device. If the strings
    // match, then we've found the waveOut device that is
    // assigned to the specified device role.
    int waveOutId;
    int cWaveOutDevices = waveOutGetNumDevs();

    for (waveOutId = 0; waveOutId < cWaveOutDevices; waveOutId++)
    {
        MMRESULT mmr;
        size_t cbEndpointId;

        // Get the size (including the terminating null) of
        // the endpoint ID string of the waveOut device.
        mmr = waveOutMessage((HWAVEOUT)IntToPtr(waveOutId),
                             DRV_QUERYFUNCTIONINSTANCEIDSIZE,
                             (DWORD_PTR)&cbEndpointId, NULL);
        if (mmr != MMSYSERR_NOERROR ||
            cbEndpointIdKey != cbEndpointId)  // do sizes match?
        {
            continue;  // not a matching device
        }

        // Get the endpoint ID string for this waveOut device.
        mmr = waveOutMessage((HWAVEOUT)IntToPtr(waveOutId),
                             DRV_QUERYFUNCTIONINSTANCEID,
                             (DWORD_PTR)pstrEndpointId,
                             cbEndpointId);
        if (mmr != MMSYSERR_NOERROR)
        {
            continue;
        }

        // Check whether the endpoint ID string of this waveOut
        // device matches that of the audio endpoint device.
        if (lstrcmpi(pstrEndpointId, pstrEndpointIdKey) == 0)
        {
            *pWaveOutId = waveOutId;  // found match
            hr = S_OK;
            break;
        }
    }

    if (waveOutId == cWaveOutDevices)
    {
        // We reached the end of the for-loop above without
        // finding a waveOut device with a matching endpoint
        // ID string. This behavior is quite unexpected.
        hr = E_UNEXPECTED;
    }

Exit:
    SAFE_RELEASE(pEnumerator);
    SAFE_RELEASE(pDevice);
    CoTaskMemFree(pstrEndpointIdKey);  // NULL pointer okay
    CoTaskMemFree(pstrEndpointId);
    return hr;
}

在前面的代码示例中,GetWaveOutId 函数接受设备角色(eConsole、eMultimedia 或 eCommunications)作为输入参数。 第二个参数是一个指针,函数通过该指针为分配给指定角色的波形输出设备写入波形设备 ID。 然后,应用程序可以使用此 ID 调用 waveOutOpen 以打开设备。

前面的代码示例中的 main 循环包含对 waveOutMessage 函数的两个调用。 第一次调用发送一条DRV_QUERYFUNCTIONINSTANCEIDSIZE消息,以检索由 waveOutId 参数标识的波形设备的 终结点 ID 字符串的大小(以字节为单位)。 (终结点 ID 字符串标识在波形设备抽象下运行的音频终结点设备。此调用报告的大小包括字符串末尾终止 null 字符的空间。 程序可以使用大小信息来分配足够大的缓冲区,以包含整个终结点 ID 字符串。

第二次调用 waveOutMessage 发送DRV_QUERYFUNCTIONINSTANCEID消息来检索波形输出设备的设备 ID 字符串。 示例代码将此字符串与音频终结点设备的设备 ID 字符串与指定的设备角色进行比较。 如果字符串匹配,则该函数会将波形设备 ID 写入参数 pWaveOutId指向的位置。 调用方可以使用此 ID 打开具有指定设备角色的波形输出设备。

Windows Vista 支持DRV_QUERYFUNCTIONINSTANCEIDSIZE和DRV_QUERYFUNCTIONINSTANCEID消息。 早期版本的 Windows 不支持它们,包括 Windows Server 2003、Windows XP 和 Windows 2000。

前面的代码示例中的函数获取呈现设备的波形设备 ID,但经过一些修改后,可以对其进行调整以获取捕获设备的波形设备 ID。 然后,应用程序可以使用此 ID 调用 waveInOpen 以打开设备。 若要更改前面的代码示例以获取分配给特定角色的音频捕获终结点设备的波形设备 ID,请执行以下作:

  • 将上述示例中的所有 waveOutXxx 函数调用替换为相应的 waveInXxx 函数调用。
  • 将句柄类型 HWAVEOUT 更改为 HWAVEIN。
  • ERole 枚举常量 eRender 替换为 eCapture。

在 Windows Vista 中,waveOutOpenwaveInOpen 函数始终将创建的音频流分配给默认会话,即会话 GUID 值GUID_NULL标识的进程特定的会话。

与旧版音频 API 的互作性