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

Play Windows sound events defined in system Control Panel

By , 11 Aug 2002
 

Sample Image - windowssoundevents.jpg

Introduction

Windows plays sounds if you click somewhere, or if an error occurs. If you logoff, Windows plays a logoff sound. Did you ever want to use something similar in your application, e.g. if you'd like to play a certain sound if your application does something special?

The .NET framework support no sounds at this time, so we have to use the API method PlaySound located in the winmm.dll. We have to include this method into our C# Project like this.

[DllImport("winmm.dll", EntryPoint="PlaySound",CharSet=CharSet.Auto)]
private static extern int PlaySound(String pszSound, int hmod, int falgs);

First problem fixed...

Sound events

The sound events are stored in the registry, this is done by user. So the settings can be found under HKEY_CURRENT_USER. Windows supports Sections, this means every application can create/use it's own section. We're able to add a new Section for our application. But Windows knows also the Sound Schemes, which can be chosen and altered by the user. Because of this it's not very easy to read the sound events in registry, but it's possible.

Every sound event is based on :

  • .default Wave file defined by the vendor of the application.
  • .current Wave file currently used for this sound event.
  • HKEY_CURRENT_USER\AppEvents\EventLabels\<EVENTNAME> Description used to display sound event.

With this knowledge, we can rebuild the control panel view of the system sounds. We're also able to add our own sound events to registry. All we have to do is create some registry keys at the right position.

How to play sound events

The PlaySound method supports the playing of normal wave files stored on the disk. But it also supports playing sound events from registry. The registry key, which defines the sound event, is the verb we need to send to PlaySound method. Otherwise we have to send the file path to the method. There are different parameter flags used, defined in a C# Enum.

public enum SND
{
    SND_SYNC         = 0x0000  ,/* play synchronously (default) */
    SND_ASYNC        = 0x0001 , /* play asynchronously */
    SND_NODEFAULT    = 0x0002 , /* silence (!default) if sound not found */
    SND_MEMORY       = 0x0004 , /* pszSound points to a memory file */
    SND_LOOP         = 0x0008 , /* loop the sound until next sndPlaySound */
    SND_NOSTOP       = 0x0010 , /* don't stop any currently playing sound */
    SND_NOWAIT       = 0x00002000, /* don't wait if the driver is busy */
    SND_ALIAS        = 0x00010000 ,/* name is a registry alias */
    SND_ALIAS_ID     = 0x00110000, /* alias is a pre d ID */
    SND_FILENAME     = 0x00020000, /* name is file name */
    SND_RESOURCE     = 0x00040004, /* name is resource name or atom */
    SND_PURGE        = 0x0040,  /* purge non-static events for task */
    SND_APPLICATION  = 0x0080 /* look for application specific association */
}

Playing a wave file located on the disk looks like this, pszSound is the file path.

public static void PlaySound(String pszSound)
{
    if(File.Exists(pszSound))
    {
        PlaySound(pszSound,0,
          (int) (SND.SND_ASYNC | SND.SND_FILENAME | SND.SND_NOWAIT));
    }
}

Otherwise, if we play a sound event, it looks like this, pszSound is the event verb.

public static void PlaySoundEvent(String pszSound)
{
    PlaySound(pszSound,0,
      (int) (SND.SND_ASYNC | SND.SND_ALIAS | SND.SND_NOWAIT));
}

How to display sound events

The next step is to fill a TreeView control like the one filled in the control panel. We have to read all the application sections, in the default scheme. After that, we could load sound events for each section. If you click a TreeNode, we play the sound defined by this sound event, sounds nice?

First we need to open the AppEvents\Schemes\Apps key located under HKEY_CURRENT_USER. After that, we could read all subkeys into a string array.

RegistryKey rkey = Registry.CurrentUser;
RegistryKey rkey1=rkey.OpenSubKey("AppEvents\Schemes\Apps");
string[] sections = rkey1.GetSubKeyNames();

Now we've got the names of the application sections, next we read the display string for each section stored in the default value of the key.

// open reg key
for(int i=0;i<sections.Length;i++)
{
    // read section name
    rkey1=rkey.OpenSubKey(
        @"AppEvents\Schemes\Apps\" + sections[i]);
    // read section name
    try 
    {
        string sectionName = rkey1.GetValue("").ToString();
    } 
    catch {}
}

Now we read the subkeys of the section key, these are the sound event verbs.

// read subkeys
string[] subsections = rkey1.GetSubKeyNames();
for(int j=0;j<subsections.Length;j++)
{
    rkey1=rkey.OpenSubKey(
        @"AppEvents\EventLabels\" + subsections[j]);
    try 
    {
        string eventName = rkey1.GetValue("").ToString();
    } 
    catch {}
}

Finish our work and close all resources allocated.

rkey1.Close();
rkey.Close();

If you like, you could fill this information into a TreeView, like I did it in my demo application. If you click on a TreeNode, you can play the sound event.

Have phun with it.

License

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

About the Author

Chris Richner
Software Developer (Senior) Zeit AG
Switzerland Switzerland
Member
Biography
  • 1996 - 1998 PC Board PPL, HTML, DHTML, Javascript and ASP
  • 1999 - 2001 coding Centura against Sql Database (Centura,MSSQL,Oracle)
  • 2002 - 2004 C# Windows Forms
  • 2005 - 2006 C# ASP.NET, Windows Forms
  • 2006 - 2009 C#, WCF, WF, WPF
  • 2010 - 2012 C#, Dynamics CRM, Sharepoint, Silverlight
  • 2013 - now C#, WCF DS (OData), WF, WPF
Interests
  • family & friends
  • chilaxing ,)
  • coding

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   
GeneralNice tutememberkoonkii9 Feb '10 - 1:36 
Nice tutorial, but for those looking for a quick and simple way to play system sounds, just see MessageBeep()
GeneralAdding Screensaver Event SoundsmemberBrillo216 Nov '05 - 10:39 
I would like my screensaver (C:\WINDOWS\Lightspeed!.scr) to play the sound (C:\WINDOWS\Media\screensaver open.wav) when activated and the sound (C:\WINDOWS\Media\screensaver close.wav) when deactivated.
 
How can I do this?
 
Thank you for your advise.
GeneralPlay Explorer Navigating soundmemberMike Wild in New Zealand27 Jan '03 - 17:16 
The sample works great, but when selecting items that aren't Windows sounds the default sound always gets played. I'm trying to play the "Navigating" sound you get when clicking on a link - does anybody have any advice on how to get this to work, other than reading the registry entries and calling PlaySound with the .wav file rather than passing the "Navigating" alias?
GeneralRe: Play Explorer Navigating soundmemberSStory26 Jan '04 - 12:08 
Me too Mike.. I had my own code and get the same results.
Had hoped this would work but I get the same problem you do...
 
with my code or another.
 
I can't call my own custom events or even those of MSN Messenger.
 
The windows ones are read from the same control panel app...thus guaranteed to work.
 
if you have a solution please email me.
 

Shane
 
Shane
GeneralRe: Play Explorer Navigating soundmemberSStory26 Jan '04 - 12:45 
Mike after hours I have the solution....
 
see this
http://www.codeproject.org/audio/soundalerts.asp#xx424980xx
 
check out one of the first responses to the article at the bottom.
 
The key is to name the main key the same name as your program.
 
call sndplaysound with
 
SND_APPLICATION OR SND_NODEFAULT
 
works great on my app.
 
HTH,
 
Shane
 
Shane
GeneralPlay sound (.wav) file from a resource . . .memberstantheman4 Oct '02 - 6:11 
Greetings all,
 
This is a great article, but what I want to know is how to play a sound from a Visual Studio.NET Resource file.
I know how to play the (.wav) files if they are not in a resource, but I can’t figure out how to generate a proper IntPtr in C# to get the DLL to play the resource.
 
Also, who can tell me what the proper use is of:
 
SND_APPLICATION = 0x0080 /* look for application specific association */
 
Anyone?
 
Thanks all,
Stan Shankman
stantheman@visi.com

 
...................................
GeneralRe: Play sound (.wav) file from a resource . . .memberJerry Maguire4 Oct '02 - 11:01 
Hi,
 
did you seek MSDN for this issue ?
 
.:Greets from Jerry Maguire:.
GeneralRe: Play sound (.wav) file from a resource . . .memberJerry Maguire15 Oct '02 - 8:29 
The IntPtr type is designed to be an integer whose size is platform-specific. That is, an instance of this type is expected to be 32-bits on 32-bit hardware and operating systems, and 64-bits on 64-bit hardware and operating systems.
 
The IntPtr type can be used by languages that support pointers, and as a common means of referring to data between languages that do and do not support pointers.
 
IntPtr objects can also be used to hold handles. For example, instances of IntPtr are used extensively in the System.IO.FileStream class to hold file handles.
 
The IntPtr type is CLS-compliant, while the UIntPtr type is not. Only the IntPtr type is used in the common language runtime. The UIntPtr type is provided mostly to maintain architectural symmetry with the IntPtr type.
 
found in Framework Help File.
 
.:Greets from Jerry Maguire:.
GeneralRe: Play sound (.wav) file from a resource . . .memberBigRedwood6 Nov '02 - 13:17 
Try this...
 

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Runtime.InteropServices;
 
namespace Embedded_Wave_Resource
{
///
/// Summary description for Form1.
///

public class Form1 : System.Windows.Forms.Form
{
public enum SND
{
SND_SYNC = 0x0000 ,/* Play synchronously (default) */
SND_ASYNC = 0x0001 , /* Play asynchronously */
SND_NODEFAULT = 0x0002 , /* Silence (default if no sound is found) */
SND_MEMORY = 0x0004 , /* pszSound pointer to a memory file */
SND_LOOP = 0x0008 , /* Loop the sound until next PlaySound() called */
SND_NOSTOP = 0x0010 , /* Don't stop any currently playing sound */
SND_NOWAIT = 0x00002000, /* Don't wait on busy driver */
SND_ALIAS = 0x00010000 ,/* Name is a registry alias */
SND_ALIAS_ID = 0x00110000, /* Alias is a pre d ID */
SND_FILENAME = 0x00020000, /* Name is file name */
SND_RESOURCE = 0x00040004, /* Name is resource name */
SND_PURGE = 0x0040, /* Purge non-static events for task */
SND_APPLICATION = 0x0080 /* Look for application specific association */
}
///
/// Required designer variable.
///

private System.ComponentModel.Container components = null;
[DllImport("winmm", SetLastError=true)]
public static extern bool PlaySound(byte[] data, IntPtr hMod, UInt32 dwFlags);
private byte[] bStrWav;
 
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
 
//
// TODO: Add any constructor code after InitializeComponent call
//
 
//Use reflection assembly to expose embedded wav resources
//
//Make sure that you change the Build Action on the project's wav file to "Ebedded Resource".
//Do this by accessing the wav file's properties. In this example, the wav file is called mywav.wav.
System.Reflection.Assembly thisExe;
thisExe = System.Reflection.Assembly.GetExecutingAssembly();
Stream streamWav = thisExe.GetManifestResourceStream("Embedded_Wave_Resource.mywav.wav");
//Create byte arrays for Win32 API call PlaySound()
bStrWav = new byte[streamWav.Length];
streamWav.Read(bStrWav, 0, (int)streamWav.Length);
 
PlaySound(bStrWav, this.Handle, (int) (SND.SND_ASYNC | SND.SND_MEMORY));
}
 
///
/// Clean up any resources being used.
///

protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
 
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///

private void InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(266, 86);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Name = "Form1";
this.Text = "Example (Embedded Wav)";
 
}
#endregion
 
///
/// The main entry point for the application.
///

[STAThread]
static void Main()
{
Application.Run(new Form1());
}
}
}

GeneralRe: Play sound (.wav) file from a resource . . .memberstantheman8 Nov '02 - 2:27 
Fabulous!
Would you recommend this method of playing a sound resource in the future? I mean, I have heard that DirectX 9.0 will have managed support for sound – so my guess is that this DLL method will soon become obsolete. But I would like your comments on it.
 
One more question;
I have been told repeatedly, that if I want to use an embedded resource font in an application, I will have to first write the font to disk and then call it in again with a Private font method. (This sounds like a hack to me.) Anyway, you seem like a real (embedded) aficionado, so, I’ll ask you. - Do you know of a better way to work with embedded fonts?
 
Thanks a way (way) bunch,
Stan Shankman

 
...................................

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 12 Aug 2002
Article Copyright 2002 by Chris Richner
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid