Activating an audio endpoint when not connected causes the device to report incorrect capabilities

The other day we had a major OEM report an issue with the WASAPI. In particular when they found that when they called “IAudioEndpointVolume::QueryHardwareSupport” it would occasionally return unexpected results for USB web cam devices. Most of the time calling this method would return a full list of capabilities for the device. Occasionally this method reported that the device did not have any capabilities.

Initially I wasn’t able to reproduce the problem. I tried everything I could think of including re-writing their test application. Finally, they were able to identify a set of steps that reproduced the issue every time. They sent me two separate applications. The first one enumerated all of the endpoints on the system and called “IMMDevice::Activate” on each one. The second application called the “IAudioEndpointVolume::QueryHardwareSupport” method.

Here are the steps:

1)  Unplug the webcam device and reboot the machine.
2)  Run app. 1 calling “IMMDevice::Activate” for all available endpoints / devices.
3)  Plug in the web cam device.
4)  Run the second application calling “IAudioEndpointVolume::QueryHardwareSupport”.

I would expect that “QueryHardwareSupport” would return “3”. In this very specific case I saw a return of “0”.

I looked very closely at the application that called “Activate”. I noticed that “DEVICE_STATEMASK_ALL” was specified when the application was enumerating the devices / endpoints. The application then enabled each of the devices / endpoints retuned in the enumeration. Every activation succeeded without error.

The problem suddenly became clear; you should not attempt to activate a device / endpoint that is not attached to the system. Attempting to do so will invalidate the endpoint. The underlying WASAPI code appears to query the HW for its capabilities when you call “IMMDevice::Activate”. Since the HW is not present, the driver returns that the device has no capabilities. This has the effect of invalidating the device. This invalidated state is cached by the subsystem. When you plug in a device, “IAudioEndpointVolume::QueryHardwareSupport” returns the cached capabilities.

Bottom line, don’t use “DEVICE_STATEMASK_ALL” if you are going to activate the enumerated endpoints. You can get just the active endpoints by using “DEVICE_STATE_ACTIVE”. If for some reason you want to activeate a jack that is unplugged you can or “DEVICE_STATE_ACTIVE” with “DEVICE_STATE_UNPLUGGED”. Try to avoid using “DEVICE_STATE_DISABLED”, “DEVICE_STATE_NOTPRESENT” and “DEVICE_STATEMASK_ALL” if you are going to call “IMMDevice::Activate” on the enumerated endpoints.

Comments