Click here to Skip to main content
Click here to Skip to main content

Changing your Windows audio device programmatically using VC++

By , 23 Dec 2008
 

Introduction

Have you ever wanted to switch the active audio output device (sound playback) on your Windows computer programmatically? Have you noticed that Microsoft (by design) has made it almost impossible to do this? Well, here is a solution to that problem. This is a Managed C++ solution (.NET), but the method used here can be adapted easily to other languages.

Background

Before going further, a few things should be stated:

  1. Precisely because Microsoft did not want you to do this, this solution is by definition a kludge, a workaround.
  2. Any third party application which outputs to the sound playback audio device, such as the Windows Media Player, has to be re-launched after the audio device has changed, because they usually consult the Registry and configure themselves only once at application launch time. Your own applications don't have to mimic this limitation (wink).
  3. I have only tested this on XP. I'm not aware of any reason why the solution will not work under Vista, but verifying the solution's operability under Vista is left as an exercise for the reader.
  4. Because we are switching between audio devices, and the actual audio devices available on your machine are almost certainly different from my machine, you must supply a bit of machine dependent information to get the solution to work on your own computer.

OK, having those four caveats out of the way, here is the general idea. We programmatically launch the "Sounds and Audio Devices Properties" applet and manipulates the panel using the messaging system. The panel actually appears on the screen briefly, but that's not a problem. Changing the audio device this way is better (in my opinion) than directly modifying the Registry, which is always error prone because it's never exactly clear which Registry keys are involved.

This general method can, of course, be extended to manipulate any applet or application, it's not limited to just the "Sound and Audio Devices Properties" applet. The solution is small enough that its adaptation to other applets (.cpl) and applications (.exe) should be obvious.

Using the Code

Let's get started. The first thing you need to do is determine which two audio devices you want to switch between. Launch the "Sounds and Audio Devices Properties" applet from the Control Panel, go to the "Audio" tab, and click on the "Sound playback" combobox. A drop down list appears showing the audio output devices available on your machine for sound playback. Write down the two devices you desire to manipulate (exactly, all capitalization and punctuation must be accurate).

On my machine, I selected "Realtek HD Audio output" and "CreamWare Play/Rec 1".

At the top of the file ACMDefines.h, replace my selections with your own:

#define AUDIO_DEVICE_1 _T("Realtek HD Audio output")
#define AUDIO_DEVICE_2 _T("CreamWare Play/Rec 1")

Now, build the solution, and it should work on your machine.

OK, let's explain what's going on.

Launching an applet (.cpl) from managed code is easy enough, it's a one-liner:

Process::Start("rundll32.exe","shell32.dll,Control_RunDLL mmsys.cpl");

The applet "mmsys.cpl" is located in the system32 directory with all the other applets found in the Control Panel, nothing surprising there. I deliberately did not launch the applet showing the "Audio" tab immediately, in order to demonstrate in the example code how we can select tabs programmatically. But, we could change the above line to:

Process::Start("rundll32.exe","shell32.dll,Control_RunDLL mmsys.cpl,,2");

The result would be that the applet window would appear with the "Audio" tab already selected, so the process of selecting the desired tab would not have to be done in the code. But, if you desire to utilize my solution as a jig for adapting it to your own specific needs, it's better to show all the steps involved.

Once the applet has successfully launched, we obtain a handle to the applet window:

appletWindow = FindWindow(NULL,APPLET_WINDOW_TITLE);

From there, it's simply a question of sending messages to the applet emulating the keyboard activity. The concept is simple enough. The difficulty is getting the handles to the various controls in the applet window. First, we need to snoop around in the applet window with Spy++ (inside Visual Studio), to discover the types and labels of the controls we wish to manipulate. So, launch the applet, open Spy++, and look for the applet window. You should see something like the following:

Window xxxxxxxx "Sounds and Audio Devices Properties" #xxxxx (Dialog)

Click on the "+" to the left of this entry to expand the list of child windows. One of the child entries is 'Window xxxxxxxx "&Apply" Button'. There are two important pieces of information here. One, it's a control of type BUTTON. Two, the button's text (or label) is "&Apply" (note the ampersand which doesn't actually appear in the GUI).

In the example code, we obtain the handle to this control (and all subsequent controls) with a call to findChildWindow():

applyButton = findChildWindow(appletWindow,BUTTON,APPLY_BUTTON);

BUTTON is defined in ACMDefines.h as:

#define BUTTON _T("Button")

and APPLY_BUTTON is defined as:

#define APPLY_BUTTON _T("&Apply")

OK, so now we have the handle to the "Apply" button. The next thing we need to do is select he "Audio" tab. In Spy++, you will see an entry like:

Window xxxxxxxx '"' SysTabControl32

The type is SysTabControl, but it has no label. The fact that it has no label isn't a problem, because there is only one control of type "SysTabControl32" in the applet window. We obtain the handle to this control the same way we obtained the handle to the "Apply" button:

tabControl = findChildWindow(appletWindow,SYS_TAB_CTRL,NULL);

Once we have the handle, we emulate the keyboard activity (ctrl-up/down/right/left) to select the "Audio" tab:

SendMessage(tabControl,WM_KEYDOWN,VK_CONTROL,0); //ctrl key down
SendMessage(tabControl,WM_KEYDOWN,VK_RIGHT,0); //right arrow key down
SendMessage(tabControl,WM_KEYUP,VK_RIGHT,0); //right arrow key up
SendMessage(tabControl,WM_KEYDOWN,VK_RIGHT,0); //right arrow key down
SendMessage(tabControl,WM_KEYUP,VK_RIGHT,0); //right arrow key up
SendMessage(tabControl,WM_KEYUP,VK_CONTROL,0); //ctrl key up

Once we have the audio tab showing, we look for the handle of the audio device selection we want (AUDIO_DEVICE_1):

comboBox = findChildWindow(appletWindow,COMBO_BOX,AUDIO_DEVICE_1);

If the handle isn't found, we look for the handle to AUDIO_DEVICE_2. An assumption is made in the example code that one of these two audio devices will always be selected, and hence will always be the selected item in the combo box. If your machine has more than two audio devices, and neither of the two devices you have selected is the default selected item, then you must first look for the handle to the default selected item and from there select one of the two devices of interest. The example code doesn't do this, it assumes one of the two devices is already selected.

Once we have the handle to the currently selected item in the combobox, we simply send it a message to change to the new audio device:

SendMessage(comboBox,CB_SELECTSTRING,(WPARAM)NONE,(LPARAM)&AUDIO_DEVICE_2);

Then, we message the "Apply" button to apply the change:

SendMessage(applyButton,BM_CLICK,0,0); //apply the change

and close the applet window:

SendMessage(appletWindow,WM_CLOSE,0,0); //close the applet

Points of Interest

If you wish to launch a regular application (.exe) and manipulate it using emulated keystrokes, there exists several methods to do this. Here is just one possibility:

protected: bool launchApplication()
{
    HINSTANCE returnCode;
    returnCode = ShellExecute((HWND)errorBox->getWindowHandle(), //window
                "open", //verb - action
                "C:\\Program Files\\SomeProgram.exe", //program file specification
                "", //parameters (none)
                "C:\\Program Files", //directory where opened
                SW_SHOWMINNOACTIVE); //window minimized, no focus
    return((returnCode > (HINSTANCE)32 && returnCode != 
        (HINSTANCE SE_ERR_NOASSOC) ? true : false);
}

Only the launching method is different for .exe files, everything else is the same as for .cpl files.

As you can see, the concept is extremely simple. The value of this coding example is the compact and generic method it demonstrates for acquiring the handles to the various controls in the applet from Managed C++.

Clearly, this example is specific to a particular task and applet. For that reason, I have not tried to reduce everything to its most abstract state, but rather just presented a clear example of a method which can be used as a guide to design your own specific solutions to similar problems.

History

  • 23 Dec 2008: Updated source code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

odlumb
Software Developer (Senior)
United States United States
Member
Brian Odlum is a retired software engineer who spent twenty years in the industry. He learned more than a dozen programming languages and worked with all of them in a variety of programming environments and operating systems.
 
He now considers himself a serious composer of computer music, dabbles in video game level development, and likes to spend at least three months of every year living in a foreign country.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralProblems Selecting "Sound Recording" Comboboxmember53rg10026 Apr '10 - 8:40 
Brian,
 
I'm having problems in the selection of the second combo-box(Sound Recording) in the Audio tab!
I've checked it in Spy++, and in this case we have 2 combo-boxes, and your code only deals with the 1st one (related to Sound Playback).
My problem is that I've no way to access to each one separately because its impossible to distinguish the two combo-boxes in code, they don't have different instance names...!
 
Do you have any idea, how can i solve this problem?
 
thanks in advance
sérgio
GeneralRef. difficulties in usingmemberFlavioAR12 Apr '10 - 14:25 
Could implement the code!
 
thanks Brian Odlum, thanks! after more than two weeks trying to just getting a very simple way by their example, and had searched almost all the google. In this area (system) we must have insistent!
 
here is my thank you!
 
FlavioThumbs Up | :thumbsup: Smile | :)
QuestionRef. difficulties in usingmemberFlavioAR12 Apr '10 - 14:04 
Brian Odlum hello!
 
My name is Flavio (Brazil), congratulate him on his excellent article, and such experience in software. I'm a student developer and am having trouble using the command "Process:: Start (" rundll32.exe ", arg);" in C #. Could you guide me in some way?Confused | :confused:
Thank you in advance.
 
Greetings
Flavio
GeneralWindows 7memberarunsankarm8 Dec '09 - 1:13 
I could see that in support for SetupPreferredAudioDevice key is not there in windows 7. Does anybody know an alternative for this key in Windows 7, to make the audio device default at the time of installation(Or is there any other way of switching the audio device programatically in windows 7)?
GeneralSolution for vistamemberummarbhutta10 Sep '09 - 1:05 
Check out this solution for Vista
http://forum.codeproblem.com/c/how-to-set-default-audio-device-programmaticaly-in-c-on-windows-vista/msg20/#msg20[^]
Generalinclude filesmemberopt1cs9821 Jun '09 - 16:01 
Noobie here...I downloaded VC++ Express and received this error when I went to build: "fatal error RC1015: cannot open include file 'afxres.h'" I do have Microsoft SDKs v6.0a, but it doesn't have this include file. Any tips would be appreciated.
Answeradaptationmembersnoepie26 Apr '09 - 20:29 
To the writer, tnx very much wasted quite some time before stumbling on your shortcut. Invaluable. i've adapted your kludge to work with windows 7 (and vista obviously) since under 7 setting the audio endpoint takes effect immediately, you can choose analog/Digital in the aplplet, and the Realtek sound panel needs proccess memory interaction.
 
You care for the source?
QuestionIs this an easier way to change the default audio device?member Randor 17 Dec '08 - 9:30 
Have you tried something like this?
 
#define AUDIO_DEVICE_1 _T("Realtek HD Audio output")
#define AUDIO_DEVICE_2 _T("CreamWare Play/Rec 1")
HKEY hKey;
TCHAR szBuf[MAX_PATH];
DWORD dwResult;
RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Multimedia\\Sound Mapper"),0,KEY_WRITE, &hKey);
RegSetValueEx(hKey,_T("Playback"),0,REG_SZ,(LPBYTE)AUDIO_DEVICE_1,_tcslen(AUDIO_DEVICE_1) * sizeof(TCHAR));
RegCloseKey(hKey);
 
The same technique for default recording device using the subkey "Record". Not sure but I think you would need to broadcast a message maybe WM_DEVICECHANGE or MM_MIXM_CONTROL_CHANGE so applications are aware that the default audio device has changed.
 
Best Wishes,
-David Delaune
AnswerRe: Is this an easier way to change the default audio device?memberodlumb17 Dec '08 - 11:12 
No question that manipulating the Registry directly is faster/smaller/easier. However, as mentioned in the article, you can't always be sure of which Registry keys to change, it's usually more than one. But more importantly, the article is not intended to be just an alternate way to change the current audio device. It's intended to be an example of how to launch applets and programs (ANY applets/programs) and manipulate them programatically in the absense of a normal programming interface. I guess I left too much to the reader in terms of abstracting the general method from the specific solution. Wink | ;)
 
There are three kinds of people - those who can count, and those who can't!

AnswerRe: Is this an easier way to change the default audio device?memberJim Crafton17 Dec '08 - 16:48 
Another drawback to doing it your way is that unless MS has explicitly defined these as the standard keys to use, then there's a good chance it could break with the next version of Windows. Apparently there's a number of cases (see The Old New Thing blog) where programmers have done things like this (hard coded registry keys) and caused various problems.
 
¡El diablo está en mis pantalones! ¡Mire, mire!
 
Real Mentats use only 100% pure, unfooled around with Sapho Juice(tm)!
 
SELECT * FROM User WHERE Clue > 0
0 rows returned

Save an Orange - Use the VCF!
VCF Blog

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 23 Dec 2008
Article Copyright 2008 by odlumb
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid