Роли устройств для устаревших приложений Мультимедиа Windows

Примечание.

API MMDevice поддерживает роли устройств. Однако пользовательский интерфейс в Windows Vista не реализует поддержку этой функции. Поддержка пользовательского интерфейса для ролей устройств может быть реализована в будущей версии Windows. Дополнительные сведения см. в статье "Роли устройств" в Windows Vista.

 

Устаревшие функции Windows мультимедиа waveOutXx иwaveInXxx не позволяют приложению выбрать устройство аудио конечной точки, назначенное пользователю определенной роли устройства. Однако в Windows Vista основные API аудио можно использовать в сочетании с мультимедийным приложением Windows для включения выбора устройств на основе роли устройства. Например, с помощью API MMDevice приложение waveOutXxx может определить устройство конечной точки звука, назначенное роли, определить соответствующее устройство вывода волн и вызвать функцию waveOutOpen, чтобы открыть экземпляр устройства. Дополнительные сведения о waveOutXxx и waveInXxx см. в документации по пакету SDK для Windows.

В следующем примере кода показано, как получить идентификатор устройства волны для устройства отрисовки конечной точки, назначенной определенной роли устройства:

//-----------------------------------------------------------
// 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) в качестве входного параметра. Второй параметр — это указатель, с помощью которого функция записывает идентификатор устройства волны для устройства вывода волны, назначенного указанной роли. Затем приложение может вызвать waveOutOpen с этим идентификатором, чтобы открыть устройство.

Основной цикл в предыдущем примере кода содержит два вызова функции waveOutMessage . Первый вызов отправляет сообщение DRV_QUERYFUNCTIONINSTANCEIDSIZE для получения размера в байтах строки идентификатора конечной точки устройства волны, определяемого параметром waveOutId . (Строка идентификатора конечной точки определяет устройство звуковой конечной точки, которое лежит в основе абстракции устройства волны.) Размер, сообщаемый этим вызовом, включает пробел для завершающего символа NULL в конце строки. Программа может использовать сведения о размере для выделения буфера, достаточно большого размера, чтобы содержать всю строку идентификатора конечной точки.

Второй вызов waveOutMessage отправляет сообщение DRV_QUERYFUNCTIONINSTANCEID, чтобы получить строку идентификатора устройства вывода волны. Пример кода сравнивает эту строку с строкой идентификатора устройства аудио конечной точки с указанной ролью устройства. Если строки совпадают, функция записывает идентификатор устройства волны в расположение, на которое указывает параметр pWaveOutId. Вызывающий объект может использовать этот идентификатор для открытия выходного устройства волны с указанной ролью устройства.

Windows Vista поддерживает сообщения DRV_QUERYFUNCTIONINSTANCEIDSIZE и DRV_QUERYFUNCTIONINSTANCEID. Они не поддерживаются в более ранних версиях Windows, включая Windows Server 2003, Windows XP и Windows 2000.

Функция в приведенном выше примере кода получает идентификатор устройства волны для устройства отрисовки, но с несколькими изменениями его можно адаптировать для получения идентификатора устройства волны для устройства записи. Затем приложение может вызвать waveInOpen с этим идентификатором, чтобы открыть устройство. Чтобы изменить приведенный выше пример кода, чтобы получить идентификатор устройства волны для конечной точки записи аудиозаписи, назначенной определенной роли, сделайте следующее:

  • Замените все вызовы функции waveOutXxx в предыдущем примере соответствующими вызовами функции waveInXxx .
  • Измените тип дескриптора HWAVEOUT на HWAVEIN.
  • Замените константу перечисления ERole eRender на eCapture.

В Windows Vista функции waveOutOpen и waveInOpen всегда назначают звуковые потоки, создаваемые сеансом по умолчанию, — сеанс, определенный по значению GUID сеанса, GUID_NULL.

Взаимодействие с устаревшими API аудио