How to enumerate DirectSound devices
by Joe Plante

This drove me crazy, because I spent a few hours trying to figure out how to do this because there aren't many tutorials dedicated to device enumeration. A lot of them just use the primary sound driver (a.k.a. NULL). It turned out to be incredibly easy. If you haven't used callback prodecures/functions, basically a part of DirectSoundEnumerate will call the callback function with the data you need. Just take a look at my example code. Note the part where I put in "Moo!". You can put anything there, or even NULL if you don't want to pass anything to the callback function. I kept the functions simple since printf is a good common denominator for programs.

You need to include DSound.lib. This example code is designed for a multibyte character build.

#include <Dsound.h>

#include <stdio.h>

int main()
{
   BOOL CALLBACK DSEnumProc(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext );

   // this is all you have to do. You don't even need to have a DirectSound device set up
   //
   // To demonstrate lpContext, I am sending over "Moo!". "Moo!" doesn't do anything in this case
   if ( (DirectSoundEnumerate(DSEnumProc, (LPVOID)"Moo!")) == DS_OK )
   {
      printf("Operation completed successfully.\n");
   }
   else
   {
      printf("Houston, we have a problem.\n");
   }

   system("pause");

   return 42;
}

// The way I understand it: the 4 parameters describe the item. lpContext is a generic parameter
// you can pass. This function runs once for every sound output device it detects
//
// The GUID is what you would need to pass to DirectSound
BOOL CALLBACK DSEnumProc(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext )
{
   printf("lpContext = %s\n", (char *)lpContext);
   printf("Device description = %s\n", lpszDesc);
   printf("Driver name = %s\n", lpszDrvName);
   printf("\n");
   
   return TRUE;
}

A little fancier, perhaps to show how you might use lpContext

#include <Dsound.h>
#include <stdio.h>
#include <vector>
 using std::vector;

//(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext )
struct SoundCardInfo
{
   LPGUID lpGuid;
   char description[100];
};

int main()
{
   BOOL CALLBACK DSEnumProc(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext );

   vector<SoundCardInfo *> soundCardList;

   // this is all you have to do. You don't even need to have a DirectSound device set up
   if ( (DirectSoundEnumerate(DSEnumProc, (LPVOID)&soundCardList)) == DS_OK )
   {
      printf("Operation completed successfully.\n");
   }
   else
   {
      printf("Houston, we have a problem.\n");
   }

   for ( size_t s = 0; s < soundCardList.size(); s++)
   {
      printf("Sound card detected: %s\n", soundCardList.at(s)->description);
   }

   for ( size_t s = 0; s < soundCardList.size(); s++)
   {
      // remember to deallocate the pointers
      delete soundCardList.at(s)->lpGuid;
      delete soundCardList.at(s);
   }

   system("pause");

   return 42;
}

// The way I understand it: the 4 parameters describe the item. lpContext is a generic parameter
// you can pass. This function runs once for every sound output device it detects
//
// The GUID is what you would need to pass to DirectSound
BOOL CALLBACK DSEnumProc(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext )
{
   // this is a list of sound cards and GUIDs
   vector<SoundCardInfo *> *soundCardList = (vector<SoundCardInfo *> *)lpContext;
  
   SoundCardInfo *sci = new SoundCardInfo();

   // move the needed info into the data structure  
   //memcpy(&(sci->guid), lpGUID, sizeof(GUID));
   strcpy(sci->description, lpszDesc);
   sci->lpGuid = NULL;
  
   // NULL is the primary sound driver. Make sure you don't memcpy from NULL. Visual Studio doesn't like it when you do that
   if ( lpGUID != NULL )
   {
      // put the sound card information into the vector of pointers
      sci->lpGuid = new GUID();

      // something went wrong trying to allocate the RAM. Remember that "lp" in lpGUID means "long pointer", which is about the same as *GUID
      if ( sci->lpGuid == NULL )
      {
         return TRUE;
      }
      // copy the GUID over
      memcpy(sci->lpGuid, lpGUID, sizeof(GUID));
   }

   // and add the information to the list
   soundCardList->push_back(sci);

   return TRUE;
}