Click here to Skip to main content
15,893,588 members
Articles / Programming Languages / C++

Set Audio Endpoints System Wide in Vista/Windows 7

Rate me:
Please Sign up or sign in to vote.
4.43/5 (4 votes)
7 Jan 2011CPOL3 min read 81.2K   2.7K   10  
An article on how to set system wide audio endpoints programmatically.
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "AudioSwitcher.h"
#include <Windows.h>
#include "AudioMain.h"
#include "HandleFinder.h"
#include "UserDefines.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TAudioSwitch *AudioSwitch;
//---------------------------------------------------------------------------
__fastcall TAudioSwitch::TAudioSwitch(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
 void  TAudioSwitch::errorBox(char *errMsg)
 {
	MessageBox(NULL,errMsg, "Fatal Error", MB_OK);
 }
//---------------------------------------------------------------------------
  char *TAudioSwitch::GetPropName(int PropIndex)
  {
	return((Audio->AudioEndPoint+(PropIndex*MAXCHAR)));
  }
//---------------------------------------------------------------------------
  bool	TAudioSwitch::SetDefaultEndpoint(HWND defaultButton,bool Action)
		{
		 // Get 'Set Default' button window style
		 long lResult=GetWindowLong(defaultButton,GWL_STYLE);
		 //Check if 'Set Default' button is enabled
		 if (Action)
			{
			  if (!(lResult & WS_DISABLED))
			   {// If so, set audio endpoint as default
				SendMessage(defaultButton, BM_CLICK, NULL, NULL);
				return true;
			   }
			 }
		   else
		   {
		   if (lResult & WS_DISABLED)
			return true;
			   else
			return false;
			}
	 return false;
 }
	//-----------------------------------------------------------------------------------
 bool	TAudioSwitch::ChangeAudioDevice(int cmdSwitch,int Index)

		{

		HWND	okButton,defaultButton;
		HWND	listView;
 //		HWND    appletWindow;
		LVITEM lvi, *_lvi;
		TOKEN_PRIVILEGES tokenPriv;
		LUID tokenLuid;
		HANDLE tokenHandle;
		HANDLE lView;
		DWORD procID=0;
		AnsiString itemText;

		int lvsize=sizeof(LVITEM);
		char item[MAXCHAR];
		char *_item;
		bool breakFlag=false;

		ZeroMemory(&tokenPriv,sizeof(TOKEN_PRIVILEGES));
		ZeroMemory(&tokenLuid,sizeof(LUID));
		ZeroMemory(&item,MAXCHAR);

		// if it's already running
		if (appletWindow == NULL)
		   appletWindow = FindWindow(NULL,AppletName);

		// if not, run it
		if (appletWindow == NULL)
			appletWindow = findHandle->launchApplet(APPLET,AppletName);
		if (appletWindow != NULL)
		{
			// hide the applet from view
			// user interaction with the soundapplet will mess up the pointers

			//find the Buttons
			okButton = findHandle->findChildWindow(appletWindow,BUTTON,OkButton);
			defaultButton = findHandle->findChildWindow(appletWindow,BUTTON,DefButton);
		 if (okButton && defaultButton) //found buttons so must be the correct Page
			 // TODO add code to manipulate applet pages
		   {
			//find the listview
			listView = findHandle->findChildWindow(appletWindow,LIST_VIEW,NULL);
			//get the listview thread
			GetWindowThreadProcessId(listView,&procID);
			//open the listview process
			lView=OpenProcess(PROCESS_ALL_ACCESS,false,procID);
			if (lView)
			{
			// Ensure you have sufficient privileges to the applet
			if ( !LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tokenLuid ) )
				{
				 errorBox("Insufficient Privileges");
				 return true;
				}
				 tokenPriv.PrivilegeCount = 1;
				 tokenPriv.Privileges[0].Luid = tokenLuid;
				 tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
				 tokenHandle=NULL;
				 if (!OpenProcessToken(lView,TOKEN_ADJUST_PRIVILEGES,&tokenHandle))
				 {
				 errorBox("Unable to get privlege level");
				 return true;
				 }
				// Set access token
				if ( !AdjustTokenPrivileges(tokenHandle,FALSE,&tokenPriv,sizeof(TOKEN_PRIVILEGES),
				(PTOKEN_PRIVILEGES) NULL,  (PDWORD) NULL) )
				{
				 errorBox("Unable to set privlege level");
				return true;
				 }
			 // Allocate virtual memory for ListView
			 LVITEM *_lvi=(LVITEM*)VirtualAllocEx(lView, NULL, lvsize,MEM_COMMIT, PAGE_READWRITE);
			 // Allocate virtual memory for ListView Item String
			 char *_item=(char*)VirtualAllocEx(lView, NULL, MAXCHAR, MEM_COMMIT, PAGE_READWRITE);
			 if (!_item)
			 {

				 errorBox("Unable to allocate process memory");
				return true;
				 }

				  int itemCount=SendMessage(listView,LVM_GETITEMCOUNT, NULL, NULL);
					if (!itemCount)
						{
						 errorBox("Empty List");
						return true;
						}
			ShowWindow( appletWindow, SW_HIDE );
		for (int i=0;i<itemCount;i++)
		{
		ZeroMemory(&lvi,lvsize);
		if (!i)
			{
			 // set the first item in the listview as focused
			lvi.mask=LVIF_STATE;
			lvi.state=LVIS_FOCUSED | LVIS_SELECTED;
			lvi.stateMask=LVIS_FOCUSED | LVIS_SELECTED;
			WriteProcessMemory(lView, _lvi, &lvi, lvsize,NULL);
			SendMessage(listView, LVM_SETITEMSTATE, (WPARAM)lvi.iItem, (LPARAM)_lvi);
			// mess with the keys about to activate the Set Default state
			// no idea why, but it works
			SendMessage(listView, WM_KEYDOWN, VK_DOWN, NULL);
			SendMessage(listView, WM_KEYUP, VK_UP, NULL);
			SendMessage(listView, WM_KEYDOWN, VK_DOWN, NULL);
			SendMessage(listView, WM_KEYUP, VK_UP, NULL);
			 // reset the first item in the listview as focused
			lvi.mask=LVIF_STATE;
			lvi.state=LVIS_FOCUSED | LVIS_SELECTED;
			lvi.stateMask=LVIS_FOCUSED | LVIS_SELECTED;
			WriteProcessMemory(lView, _lvi, &lvi, lvsize,NULL);
			SendMessage(listView, LVM_SETITEMSTATE, (WPARAM)lvi.iItem, (LPARAM)_lvi);
			}
				else 
				{
				 // descend the list one by one
				SendMessage(listView, WM_KEYDOWN, VK_DOWN, NULL);
				SendMessage(listView, WM_KEYUP, VK_DOWN, NULL);
				}
			// Commandline switches given?


				// Get ItemText
				ZeroMemory(&lvi,lvsize);
				lvi.iItem=i;
				lvi.cchTextMax=MAXCHAR;
				lvi.pszText=_item;
				WriteProcessMemory(lView, _lvi, &lvi,lvsize, NULL);
				SendMessage(listView,LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
				ReadProcessMemory(lView, _item, item, MAXCHAR, NULL);
				itemText=item;
				TListItem *EndItem;
				switch (cmdSwitch)
				{
				 case SELECT_LIST:
				  // Set Selected endpoint as default
				  if (i==Index)
					  {
					   breakFlag=true;
					   SendMessage(defaultButton, BM_CLICK, NULL, NULL);
					  }
				  break;
				 case DEFAULT:
				   // Add ItemText to ListView
				  EndItem=Audio->lvEndpoint->Items->Add();
				  EndItem->Caption=itemText;
				  if (SetDefaultEndpoint(defaultButton,false))
					  EndItem->SubItems->Add("Active");
					   else
					  EndItem->SubItems->Add("Not Active");
				  break;
				 case SELECT_CMD:
				  if (itemText.Pos(Selected)>0)
					breakFlag=SetDefaultEndpoint(defaultButton,true);
				  break;
				}
				// Check if the loop continues
			if (breakFlag) break;
			}
		  }
		// Close applet on cmdline conditions
		if (cmdSwitch==SELECT_CMD)
		{
         ShowWindow( appletWindow, SW_RESTORE );
		 SendMessage(okButton, BM_CLICK, NULL, NULL);
		 appletWindow=NULL;
 		}
		// Cleanup
		VirtualFreeEx(lView, _lvi, 0, MEM_RELEASE);
		VirtualFreeEx(lView, _item, 0, MEM_RELEASE);
		CloseHandle(tokenHandle);
		CloseHandle(lView);
		} 
	   else
		{ //didn't find buttons
		 errorBox("Unable to find buttons");
		 return true;
		}
	}
	else 
	{ //didn't find applet window
	 errorBox("Failed to find applet window");
	 return true;
	}
   return false; 
  }

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Retired
France France
Used to own a software development enterprise from the late 70's to the mid 90's, worked as systems analyst/programmer in that enterprise

Comments and Discussions