Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Where Did That Sound Come From?

  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.
Dan's Blog

Difficulty: Easy
Time Required: Less than 1 hour
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware:
Download:

Video Demo

  Download Video

 

 

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:

  • For the person with too much work, use C3PO from Star Wars saying,"We're doomed."
  • For The Simpsons fan, use Nelson the Bully's "Ha Ha" laugh or Homer Simpson's "Doh."
  • For the Warcraft player, use "Leeeeeeroy Jennnnnnkins."
  • For the South Park fan, use Cartman's "Respect My Authoritay" clip.
  • For the disgruntled employee, use any quote from "Office Space."

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.

No Tech 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 Express to Change the System Sounds

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.

 

How Does Windows Store Sound Events?

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:

  • .current folder — Contains a blank registry key whose value is the sound file currently set to play with that event. For the DeviceDisconnect event, the current sound file for this event on my PC is "%SystemRoot%\media\Windows XP Hardware Remove.wav", which maps to "C:\WINDOWS\media\Windows XP Hardware Remove.wav".
  • .Default folder — Contains a blank registry key whose value is the default sound file to play with that event. Since I haven't changed my sound file, it is the same sound file as given in the .current key.
  • other folders — You may have other folders listed under each key (system event). This is because Windows allows you to save "sound schemes," which represents your customized list of sound files. If you have a custom sound scheme, you'll see a directory for each scheme under every sound event.

Reading and Writing Event Sound Files

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.

Visual Basic

'these represent the location in the registry with the user sounds
Private hivePrefix As String = "AppEvents\Schemes\Apps\.Default\"
Private hiveSuffix As String = "\.current"

Visual C#

//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.

Visual Basic

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 = _
CType(My.Computer.Registry.CurrentUser.OpenSubKey(getRegKeyPath(s)).GetValue(""), _
String)
tb.Rows.Add(newRow)
Next
Return tb
End Function

'adds the full registry key including prefix and suffix
Private Function getRegKeyPath(ByVal s As String) As String
Return (hivePrefix & s & hiveSuffix)
End Function

Visual C#

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;
newRow.SoundFile =
(string)Registry.CurrentUser.OpenSubKey(getRegKeyPath(s)).GetValue("") ;
tb.Rows.Add(newRow);
}
return tb;
}

//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.

Visual Basic

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 = _
My.Computer.Registry.CurrentUser.OpenSubKey(getRegKeyPath(row.SoundName), _
True)
key.SetValue("", soundPath, RegistryValueKind.String)
Next
End Sub

Visual C#

public void SetSystemSound(RegSound.RegSoundDataTable sounds, 
string soundPath)
{
//loop through all sounds
foreach (RegSound.RegSoundRow row in sounds)
{
//Set key and value
RegistryKey key =
Registry.CurrentUser.OpenSubKey(getRegKeyPath(row.SoundName), true);
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.

Visual Basic

Public Sub SaveSystemSound(
ByVal sounds As RegSound.RegSoundDataTable,
ByVal savePath As String)
'Save Sound DataSet
sounds.WriteXml(savePath)
End Sub

Visual C#

public void SaveSystemSound(
RegSound.RegSoundDataTable sounds, string savePath)
{
//Save Sound DataSet
sounds.WriteXml(savePath);
}

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.

Visual Basic

Public Sub RestoreSystemSound(ByVal savePath As String)
'Restore Sound DataSet
Dim sounds As RegSound.RegSoundDataTable = _
New RegSound.RegSoundDataTable
sounds.ReadXml(savePath)
For Each row As RegSound.RegSoundRow In sounds
'Set Key
Dim key As RegistryKey = _
My.Computer.Registry.CurrentUser.OpenSubKey( _
getRegKeyPath(row.SoundName), True)
key.SetValue("", row.SoundFile, RegistryValueKind.String)
Next
End Sub

Visual C#

public void RestoreSystemSound(string savePath)
{
//Restore Sound DataSet
RegSound.RegSoundDataTable sounds =
new RegSound.RegSoundDataTable();
sounds.ReadXml(savePath);

foreach (RegSound.RegSoundRow row in sounds)
{
//Set Key
RegistryKey key =
Registry.CurrentUser.OpenSubKey(
getRegKeyPath(row.SoundName), true);
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.

Visual Basic

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

Else
soundFile = "%SystemRoot%\\media\\" + soundFile
End If
My.Computer.Audio.Play(soundFile)
End If
End Sub

Visual C#

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;
sp.Play();
}
}

Creating the User Interface

To create the user interface, we'll begin by adding some controls to our form including:

  • A ToolStrip control for the Backup, Restore, Select, and Apply Changes buttons.
  • A DataGridView that we can drag and drop by clicking "Data > Show Data Sources," and dragging and dropping the RegSound DataGridView control.
  • Two OpenFileDialog controls, one to choose where to restore from and another to choose the sound file to replace.
  • One SaveFileDialog control to choose where to backup the current system sounds.

After adding in some nice IconBuffet icons that you get for registering Express, your UI should look something like this:

 

Loading Data

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.

Visual Basic

Private Sub frmMainForm_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
GetRegistrySounds()
End Sub

Private Sub GetRegistrySounds()
'Call the RegistryWrapper Class
sounds = myReg.GetSystemSound
RegSoundDataGridView.DataSource = sounds
End Sub

Visual C#

private void frmMainMenu_Load(object sender, EventArgs e)
{
GetRegistrySounds();
}
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.

Visual Basic

Private Sub RegSoundDataGridView_CellContentClick( _
ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
Handles RegSoundDataGridView.CellContentClick
'Represents col #3 the "Play" column
If (e.ColumnIndex = 2) Then
Dim cell As DataGridViewTextBoxCell = _
CType(RegSoundDataGridView.Rows(e.RowIndex).Cells((e.ColumnIndex - 1)), _
DataGridViewTextBoxCell)
'Play Sound
myReg.PlayRegistrySound(cell.Value.ToString)
End If
End Sub

Visual C#

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];
//Play Sound
myReg.PlayRegistrySound(cell.Value.ToString());
}
}

Conclusion

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.


Daniel Fernandez is the Lead Product Manager for Visual Studio Express in the developer division at Microsoft.  He has been with Microsoft since July 2001, previously working as a Developer Evangelist in the Mid-Atlantic district. Prior to joining Microsoft, he worked as a developer at several consulting firms including IBM Global Services specializing in web-based and mobile application development.

Follow the Discussion

  • nikhilnikhil

    Hello i am nikhil,i liked the tutorial abt chaning sound scheme in windows by u.

    I want to ask if i can add a new event for example..when i click on my computer -- a sound comes up like -- my computer opening ....i want sounds to be applicatio specific...can u help me in doing tat please thank u ...my email id ::

    nikhil_20036@yahoo.com

  • nikhil14bnikhil14b

    Helo,

    thanx for the tip...

    can i know if i can add my own customized sounds..like when windows start copying file frm net or frm one location to other ...i wanna play some sound..

    can i do that ???

  • DaveDave

    Do you know if there is any way to add an event? I have a friend who would like Word to say, "That's brilliant," whenever she saves a document. (She's very modest.) But Save does not seem to be a recognized event.

  • Bill AuclairBill Auclair

    Just wondering if there is any way to configure Windows to play event sounds using a URL instead of a filename and path.  I run a sound clip database, and would like to be able to extend this functionality to my users, so that they can assign clips from my site to event sounds automatically, without having to first download the files to their local system.  Is there any way that something like this could be done?

    -Bill Auclair

    bauclair@auclaironline.com

  • Clint RutkasClint I'm a "developer"

    @Bill Auclair you could do something like this

    System.Media.SoundPlayer sp = new SoundPlayer();

    sp.Stream(// stream to the website ...

  • goodrajugoodraju

    Hey,

    I would also like to know how to Add custom events like the above people.  Adding sounds while files are being copied and other things.  I would also like to know how to add sound gor Dialogue Boxes that pop up like Confirm Delete File, or for Confirm Replace files Etc. Thanks and hope to hear from you soon.

    goodraju@gmail.com

  • Clint RutkasClint I'm a "developer"

    @goodraju hooking into more global events will allow you to do just that.  And most of that is configurable via the control panel built into Windows.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.