Enumerating Sound Devices

[The feature associated with this page, DirectSound, is a legacy feature. It has been superseded by WASAPI and Audio Graphs. Media Casting have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use Media Casting instead of DirectSound, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]

If your application is simply going to play sounds through the user's preferred playback device, there is no need to enumerate the available devices. When you create the device object by calling the DirectSoundCreate8 function, you can specify a default device. For more information, see Creating the Device Object.

Enumeration is necessary in the following situations:

  • Your application needs capabilities that may not be available on all devices.

  • Your application requires two or more devices.

  • You wish to offer the user a choice of devices.

Enumeration serves three purposes:

  • It reports what hardware is available.

  • It supplies a GUID for each device.

  • It enables you to create a temporary device object for each device as it is enumerated, so that you can check the capabilities of the device.

To enumerate devices, you must first set up a callback function that will be called once for each device on the system. You can do anything you want within this function, and you can give it any name, but you must declare it in the same form as the DSEnumCallback prototype. The callback function must return TRUE if enumeration is to continue, or FALSE otherwise - for instance, after finding a device with the capabilities you need.

The following callback function adds the name of each enumerated device to a combo box and stores its GUID as the item data. Values for the first three parameters are supplied by the device driver. The fourth parameter is passed on from the DirectSoundEnumerate function; this parameter can be used to pass any 32-bit value, and in this case is the window handle of the combo box. Macros defined in Windowsx.h are used to add strings and data to the combo box.

    BOOL CALLBACK DSEnumProc(LPGUID lpGUID, 
             LPCTSTR lpszDesc,
             LPCTSTR lpszDrvName, 
             LPVOID lpContext )
    {
      HWND hCombo = (HWND)lpContext;
      LPGUID lpTemp = NULL;
     
      if (lpGUID != NULL)  //  NULL only for "Primary Sound Driver".
      {
        if ((lpTemp = (LPGUID)malloc(sizeof(GUID))) == NULL)
        {
            return(TRUE);
        }
        memcpy(lpTemp, lpGUID, sizeof(GUID));
      }
     
      ComboBox_AddString(hCombo, lpszDesc);
      ComboBox_SetItemData(hCombo, 
          ComboBox_FindString(hCombo, 0, lpszDesc),
          lpTemp );
      free(lpTemp);
      return(TRUE);
    }

The enumeration is set in motion when the dialog box containing the combo box is initialized. Assume that hCombo is the handle of the combo box and hDlg is the handle of the dialog box.

    if (FAILED(DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumProc,
        (VOID*)&hCombo)))
    {
      EndDialog(hDlg, TRUE);
      return(TRUE);
    }

In this case, the address of the combo box handle is passed into DirectSoundEnumerate, which in turn passes it to the callback function. This parameter can be any 32-bit value that you want to have access to within the callback.

The first device enumerated is always called the Primary Sound Driver, and the lpGUID parameter of the callback is NULL. This device represents the preferred playback device set by the user in Control Panel. It is enumerated separately to make it easy for the application to add "Primary Sound Driver" to a list when presenting the user with a choice of devices. The primary device is also enumerated with its proper name and GUID.