Introduction
To achieve some things in the .NET Framework, one must make platform invocation calls to the underlying OS, or use one of the thinly-wrapped COM objects exposed as a class library. This example shows how to play .WAV sounds from a file picker, or play a user-defined sound by enumerating the Windows registry. Since Microsoft did not include a method for playing sounds in the Framework, we must make a PInvoke
call to the PlaySound()
function located in the Winmm.dll library. The PlaySound function is extremely simple to use as you'll see below.
The PlaySound function
bool PlaySound(
LPCSTR pszSound,
HMODULE hmod,
DWORD fdwSound)
For this example, we play a sound directly from a filename, therefore we need only specify values for
pszSound
and
fdwSound;
The
hmod
parameter must be set to
NULL
unless playing a sound from a resource.
Partial Source
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace PlaySound
{
public class Form1 : System.Windows.Forms.Form
{
...
private RegistryKey key1;
private RegistryKey key2;
private PropertyCollection events;
[DllImport("winmm.dll", SetLastError=true,
CallingConvention=CallingConvention.Winapi)]
static extern bool PlaySound(
string pszSound,
IntPtr hMod,
SoundFlags sf );
[Flags]
public enum SoundFlags : int
{
SND_SYNC = 0x0000,
SND_ASYNC = 0x0001,
SND_NODEFAULT = 0x0002,
SND_MEMORY = 0x0004,
SND_LOOP = 0x0008,
SND_NOSTOP = 0x0010,
SND_NOWAIT = 0x00002000,
SND_ALIAS = 0x00010000,
SND_ALIAS_ID = 0x00110000,
SND_FILENAME = 0x00020000,
SND_RESOURCE = 0x00040004
}
...
private void buttonPlay_Click(object sender, System.EventArgs e)
{
int err = 0;
try
{
if (!PlaySound( tbFileName.Text, IntPtr.Zero,
SoundFlags.SND_FILENAME | SoundFlags.SND_ASYNC ))
MessageBox.Show(this,
"Unable to find specified sound file or " +
"default Windows sound");
}
catch
{
err = Marshal.GetLastWin32Error();
if (err != 0)
MessageBox.Show( this,
"Error " + err.ToString(),
"PlaySound() failed",
MessageBoxButtons.OK,
MessageBoxIcon.Error );
}
}
private void buttonBrowse_Click(object sender, System.EventArgs e)
{
string sysRoot = System.Environment.SystemDirectory;
OpenFileDialog dlg = new OpenFileDialog();
dlg.AddExtension = true;
dlg.Filter = "Wave files (*.wav)|*.wav|All files (*.*)|*.*" ;
dlg.InitialDirectory = sysRoot + @"\..\Media";
if(dlg.ShowDialog(this) == DialogResult.OK)
{
tbFileName.Text = dlg.FileName;
}
}
...
private void PopulateDropDown()
{
events = GetUserDefinedSounds();
if (events.Keys.Count == 0)
cbUserSound.Enabled = false;
else
{
foreach (string key in events.Keys)
{
cbUserSound.Items.Add(key);
}
}
}
private PropertyCollection GetUserDefinedSounds()
{
string rootKey = "AppEvents\\Schemes\\Apps\\.Default";
PropertyCollection coll = new PropertyCollection();
try
{
key1 = Registry.CurrentUser.OpenSubKey(rootKey, false);
foreach (string subKey in key1.GetSubKeyNames())
{
key2 = key1.OpenSubKey(subKey + "\\.Current", false);
if (key2 != null)
if (key2.GetValue(null).ToString().Length > 0)
coll.Add(subKey, key2.GetValue(null).ToString());
}
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Yikes!",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
key1.Close();
key2.Close();
}
return coll;
}
...
}
}
You probably noticed the SetLastError=true
parameter in the DllImport call. This simply states that the runtime marshaler calls GetLastError
for us in the event something went wrong with our PInvoke
call.
Points of Interest
This code has only been tested on Windows 2000 and XP SP1 using Framework version 1.1, however it should also work as far back as Windows 98. Oh yes, the project file is VS.NET 2003 - sorry.
History
Version 1.0 - 07.25.03 - First version.