Where Did That Sound Come From?
- Posted: Oct 31, 2006 at 1:06PM
- 7 comments
Loading user information from Channel 9
Something went wrong getting user information from Channel 9
Loading user information from MSDN
Something went wrong getting user information from MSDN
Loading Visual Studio Achievements
Something went wrong getting the Visual Studio Achievements
|This April Fools' Day article walks you through how you can programmatically change the Windows system sound events to mess with a co-worker or family member. Like many other pranks, this one requires you to have physical access to your victim's computer at a time when they're not around.|
Time Required: Less than 1 hour
This April Fools' Day article walks you through how you can programmatically change the Windows system sound events to mess with a co-worker or family member. Like many other pranks, this one requires you to have physical access to your victim's computer at a time when they're not around. As a best practice, I recommend that you back up your registry before running any code to manipulate registry settings, especially if you've never programmed the registry before.
Microsoft Windows enables users to set sounds for system events, such as when Windows starts up, when a device is connected, when an error occurs, when a window is maximized, and more. What this article walks through is adding humorous or annoying system sounds for your victim for *every* Windows system sound. As with all pranks, you should find a way to personalize the sounds for your victim. Here are some ideas:
If you're not sure where to find wave audio clips, you can find a good set of sound clips on sites like http://www.reelwavs.com/and http://www.funwavs.com/ among others. Below I'll show you two different ways to change system sounds, from requiring no code at all, to building a utility created with Express, to automate changing the system sounds.
With your victim's machine unlocked, you can change their system sounds simply by opening the Control Panel and selecting "Sounds." This will display the image below, which you can scroll through. Click "Browse" to assign a sound file to each system event. Then watch as just about every possible event triggers that sound file.
Using Visual Basic or Visual C# Express, you can take this a step further and create a utility that can automatically change all of the system sound files, back up and restore sounds settings, and test sounds.
The first thing we want to do is find out where the system sound files are stored. Since most Windows settings are stored in the registry, that seems like a logical place to start looking. To look inside the registry, click Start > Run, then type "regedit" to open the Registry Editor. You can search the registry by clicking "Edit > Find," and since I know that the Windows Startup sound contains startup.wav, I simply search the registry for that sound. What you'll find is the registry hive that represents the system sounds, as shown below:
The way this works is that each folder under Schemes/Apps/.Default represents an event you can map sounds to. For example, if you've ever unplugged a USB device, you know that there is a system sound associated with that event, the DeviceDisconnect event. Underneath a specific event, like the DeviceDisconnect event, there will be a list of folders: .current, .Default, and one folder for any sound schemes they may have saved.
As you can see in the picture above, a specific system event has the following structure:
Now that I know where the registry keys are located and what I want to do, I'm going to create a DataSet that represents the Windows sound event and the path to the sound file to play when that event occurs. To do this, I'm going to start a new Windows Forms project and select "Add New Item..." from the Solution Explorer window, and then select the DataSet template. To represent a sound event as a DataSet, I'll add two text fields, "SoundName" and "SoundFile," as shown below:
Next, I'll add a class file, RegistryWrapper.cs, which will wrap all of my registry functions, including reading and writing data. We'll walk through the major tasks we want to achieve here including registry tasks like reading, writing, backing up, restoring, and even playing a sound event.
Reading Sound Events from the Registry
Since I know the exact location of the sound events in the registry, I'm going to declare two variables in the RegistryWrapper class to store the registry prefix and suffix, so I won't have to declare the full path *everywhere* in my application.
'these represent the location in the registry with the user sounds
Private hivePrefix As String = "AppEvents\Schemes\Apps\.Default\"
Private hiveSuffix As String = "\.current"
//these represent the location in the registry with the user sounds
string hivePrefix = @"AppEvents\Schemes\Apps\.Default\";
string hiveSuffix = @"\.current";
Next, we'll add a method called GetSystemSound() that returns a RegSoundDataTable containing the SoundName and SoundFile values for the registry. The first thing we do is get the list of all the subkeys for the path we specified above by calling the GetSubKeyNames method. This will return a list of all of the sound events. Next, we loop through each sound event, creating a new row for the DataTable while setting the SoundName to the current sound event and the SoundFile to the registry key value that contains the location of the sound. Note that when we call the GetValue method to get the sound file, we need to pass in the name of the key, which in our case is blank so we pass in "". We'll also add a helper function that simply concatenates the two variables declared earlier.
Public Function GetSystemSound() As RegSound.RegSoundDataTable
'Get the subkey key
Dim values() As String = Registry.CurrentUser.OpenSubKey(hivePrefix).GetSubKeyNames
Dim tb As RegSound.RegSoundDataTable = New RegSound.RegSoundDataTable
For Each s As String In values
'Loop through rows
Dim newRow As RegSound.RegSoundRow = tb.NewRegSoundRow
newRow.SoundName = s
newRow.SoundFile = _
'adds the full registry key including prefix and suffix
Private Function getRegKeyPath(ByVal s As String) As String
Return (hivePrefix & s & hiveSuffix)
public RegSound.RegSoundDataTable GetSystemSound()
//Get the subkey key
string values = Registry.CurrentUser.OpenSubKey(hivePrefix).GetSubKeyNames();
RegSound.RegSoundDataTable tb = new RegSound.RegSoundDataTable();
foreach (string s in values)
//Loop through rows
RegSound.RegSoundRow newRow = tb.NewRegSoundRow();
newRow.SoundName = s;
//adds the full registry key including prefix and suffix
private string getRegKeyPath(string s)
return hivePrefix + s + hiveSuffix;
Writing Sound Events to the Registry
To set all of the sound events to a specific sound, we'll create another method that takes a RegSound DataTable and the sound file we'll use for the change. We loop through every row in the DataTable and set the registry key value for the sound by calling the SetValue method. To call the SetValue method, we need to know the key name (in our case its blank or ""), the value to set it to (in our case the sound file path), and the RegistryKind, which describes the type or kind of value being stored (in our case a string) in the registry.
Public Sub SetSystemSound(ByVal sounds As RegSound.RegSoundDataTable,
ByVal soundPath As String)
'loop through all sounds
For Each row As RegSound.RegSoundRow In sounds
'Set key and value
Dim key As RegistryKey = _
key.SetValue("", soundPath, RegistryValueKind.String)
public void SetSystemSound(RegSound.RegSoundDataTable sounds,
//loop through all sounds
foreach (RegSound.RegSoundRow row in sounds)
//Set key and value
RegistryKey key =
key.SetValue("", soundPath, RegistryValueKind.String);
Back Up Current Sound Settings
Since your victim may be upset you changed his or her sounds, we'll add a way to save their current sound settings to the local hard drive where you can restore them later. To do this, we'll add the SaveSystemSound method that takes the DataTable you want to save and the file location to save to. We can use the WriteXml method on the DataTable object to save the DataTable as an XML file.
Public Sub SaveSystemSound(
ByVal sounds As RegSound.RegSoundDataTable,
ByVal savePath As String)
'Save Sound DataSet
public void SaveSystemSound(
RegSound.RegSoundDataTable sounds, string savePath)
//Save Sound DataSet
Restoring Saved Sound Settings
Now let's add a method to restore the sound settings from the previous step. In this case, we just need to know where the location of the saved DataTable is and call the
ReadXml method to read the data we stored previously. Once we have the data we can loop through each sound event and call the
setValue method to replace the current sound.
Public Sub RestoreSystemSound(ByVal savePath As String)
'Restore Sound DataSet
Dim sounds As RegSound.RegSoundDataTable = _
For Each row As RegSound.RegSoundRow In sounds
Dim key As RegistryKey = _
key.SetValue("", row.SoundFile, RegistryValueKind.String)
public void RestoreSystemSound(string savePath)
//Restore Sound DataSet
RegSound.RegSoundDataTable sounds =
foreach (RegSound.RegSoundRow row in sounds)
RegistryKey key =
key.SetValue("", row.SoundFile, RegistryValueKind.String);
Playing a Registry Sound Event
Finally, we'll add a way to play a registry sound. Since some sound files assume a path to the media folder under System root, we need to add a quick check to see if the file has any backslashes ("\"), which we'll use as a test to see if the sound file contains both a path and sound file. If it doesn't, we'll append the path to the sound file and play it.
Public Sub PlayRegistrySound(ByVal soundFile As String)
'play sound if there is an associated file
If (soundFile <> "") Then
'add default path if there isn't one
Dim a As Integer = soundFile.IndexOf(Microsoft.VisualBasic.ChrW(92))
If (a <> 0) Then
soundFile = "%SystemRoot%\\media\\" + soundFile
public void PlayRegistrySound(string soundFile)
//play sound if there is an associated file
if (soundFile != "")
SoundPlayer sp = new SoundPlayer();
//add default path if there isn't one
int a = soundFile.IndexOf('\\');
if (a != 0)
soundFile = "%SystemRoot%\\media\\" + soundFile;
sp.SoundLocation = soundFile;
Creating the User Interface
To create the user interface, we'll begin by adding some controls to our form including:
After adding in some nice IconBuffet icons that you get for registering Express, your UI should look something like this:
Finally, we're going to put it all together by adding a couple of functions to the Windows Form. The first thing we'll want to do is add two form variables, one to represent the RegistryWrapper we wrote before and another to store the RegSoundDataTable data. To populate the DataTable, well call the GetRegistrySounds method, which in turn calls the GetSystemSound method we created earlier. We'll call the GetRegistrySounds method when the form loads, when we restore the sounds, or when we apply changes so that the DataGridView will display the current registry sounds.
Private Sub frmMainForm_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Private Sub GetRegistrySounds()
'Call the RegistryWrapper Class
sounds = myReg.GetSystemSound
RegSoundDataGridView.DataSource = sounds
private void frmMainMenu_Load(object sender, EventArgs e)
private void GetRegistrySounds()
//Call the RegistryWrapper Class
sounds = myReg.GetSystemSound();
regSoundDataGridView.DataSource = sounds;
Formatting the DataGridView
To add a nice UI, we'll format the DataGridView control by changing some of the properties, like setting the AlternatingRowsDefaultCellStyle property to a different color, changing the DefaultCellStyle font to Arial 10, and turning off adding, editing, and deleting.
We'll also add an image column with a "play" image so that we can here the currently associated sound. To do this, you can right-click on the DataGridView and select "Edit Columns" to bring up the Edit Column dialog box as shown below. Here we'll add a new column called "Play," set the column type to DataGridViewImageColumn, set the Image property to our sound image, and set the ImageLayout property to "Zoom" so that the image will fit the cell contents.
Finally, we'll want to add code to play a sound when someone clicks the sound image, and we can do this by adding the following code to the DataGridView CellContentClick event. As you can see below, we only play a sound if they clicked the third column (index start at 0 so the third is #2). To play a sound, we'll need the sound path, that we get by creating a DataGridViewTextBoxCell for the SoundFile column and reading its value.
Private Sub RegSoundDataGridView_CellContentClick( _
ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
'Represents col #3 the "Play" column
If (e.ColumnIndex = 2) Then
Dim cell As DataGridViewTextBoxCell = _
CType(RegSoundDataGridView.Rows(e.RowIndex).Cells((e.ColumnIndex - 1)), _
private void regSoundDataGridView_CellContentClick(
object sender, DataGridViewCellEventArgs e)
//Represents col #3 the "Play" column
if (e.ColumnIndex == 2)
DataGridViewTextBoxCell cell = (DataGridViewTextBoxCell)
regSoundDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex - 1];
Reading and writing data out of the registry is pretty easy, as is saving structured data like a DataSet. It's almost too easy to find the right sound file on the Internet and of course make hilarity ensue when your poor unsuspecting victim tries to figure out what's wrong with their PC. If you want to extend this application, a couple of additions would be to add the ability to change sounds beyond the system sound, like logon sounds for MSN Messenger, blocked pop-ups by Internet Explorer, and a host of other sound files you can map your PC.