Sometimes You Gotta Make a Little Noise
Importing the Windows MultiMedia Library
The ability to import an external API library is supported by the DllImportAttribute. In Visual Basic, importing an external library also is manifested with the synonymous Declare modifier. That is, Visual Basic .NET supports the Declare statement. To import the winmm.dll PlaySound method, we can reference the library—which does an implicit LoadLibrary and FreeLibrary in the background—using the following Declare statement:
Private Declare Function PlaySound Lib "winmm.dll" ( _ ByVal fileName As String, ByVal hmod As IntPtr, _ ByVal flags As PlaySoundFlags) As Integer
You will have to perform modest translations between managed types and API types on a case by case basis, but the preceding Declare statement is correct for PlaySound.
Wrapping the PlaySound API Method
Public Shared Sub Play(ByVal fileName As String) Try PlaySound(fileName, IntPtr.Zero, PlaySoundFlags.SND_FILENAME Or _ PlaySoundFlags.SND_ASYNC) Catch Debug.WriteLine("Can't play sound file") End Try End Sub
The IntPtr class and the IntPtr.Zero constant come from the InteropServices namespace. Sometimes, we need a pointer that may not be a native type in a particular .NET language, such as VB.NET. PlaySound supports a null module pointer—as opposed to, for example, the actual address of a module pointer—which is expressed using IntPtr.Zero.
Playing Sounds Asynchronously
The winmm.dll PlaySound method supports synchronous and asynchronous playback of media. If we send PlaySoundFlags.SND_SYNC, our code will have to wait for the API call to complete before we re-attain control. Passing PlaySoundFlags.SND_ASYNC means that the call returns immediately and the media is played in a background thread.
Testing the Wrapper CodeFor your convenience, the entire example listing is provided here complete with a test method, beep (see Listing 5). Assuming your Windows directory is Windows and not WINNT and the chord.wav file exists, you should hear the chord.wav file if you call the Sounds.Beep static method. To compare the difference between asynchronous and synchronous behavior, pick a longer .wav file and switch between passing SND_ASYNC and SND_SYNC to see a difference in how the code responds.
Listing 5: The Complete Sounds class.
Imports System Imports System.Diagnostics Imports System.IO Imports System.Runtime.InteropServices Public Class Sounds <Flags()> _ Public Enum PlaySoundFlags SND_SYNC = 0 SND_ASYNC = 1 SND_FILENAME = &H20000 SND_RESOURCE = &H40004 End Enum Private Declare Function PlaySound Lib "winmm.dll" (ByVal _ fileName As String, _ ByVal hmod As IntPtr, ByVal flags As PlaySoundFlags) As Integer Public Shared Sub Play(ByVal fileName As String) Try PlaySound(fileName, IntPtr.Zero, PlaySoundFlags.SND_FILENAME Or _ PlaySoundFlags.SND_ASYNC) Catch Debug.WriteLine("Can't play sound file") End Try End Sub Public Shared Sub Beep() Try Const soundFile As String = "c:\winnt\media\chord.wav" If (File.Exists(soundFile)) Then Play(soundFile) Catch ex As Exception Debug.WriteLine(ex.Message) Throw End Try End Sub End Class
A large amount of information can be hidden in a small amount of code. In this article, you learned a little bit about the .NET attributes DllImportAttribute and FlagsAttribute that support the Declare modifier and bitwise Or-behavior for enumerated types. We talked some about InteropServices, and you now have a very simple media player that may add some additional depth to your applications. Enjoy.
Paul Kimmel is the VB Today columnist, has written several books on .NET programming, and is a software architect. You may contact him at email@example.com if you need assistance.
Page 2 of 2