Re: Playing sound from resource file via sndPlaySound API

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



Mike,

I have a question about your Class module. If I copy the code into a Class
module I get a syntax error for

Attribute VB_Name = "Sound"

and the other Attributes.

Is there something missing in your example or should they be declared
another way?

--
Norm

Don't blame me, my programming is
self-taught and my teacher was not
very experienced. :-)

normfowler_don't use_@xxxxxxxxxxx
"MikeD" <nobody@xxxxxxxxxxx> wrote in message
news:%232xD$UJqHHA.2288@xxxxxxxxxxxxxxxxxxxxxxx

"Darhl Thomason" <darhlt@xxxxxxxxxxxxxxxxxxxxxxxxxxxx> wrote in message
news:e5zuyUIqHHA.960@xxxxxxxxxxxxxxxxxxxxxxx
I read the help on the sndPlaySound API, and also on the Resource File. I
believe I have things set up properly because I can pull bitmaps out of my
resource file. The help files also referenced the ATM sample project
which I have looked at as well.

You'd be better off using PlaySound rather than sndPlaySound...for a
number of reasons. First, PlaySound supports playing *directly* from the
resource. IOW, you don't need to create a temp file or "buffer" the
resource into a string variable. Also, my experience has been that
certain sounds just won't play (or worse, cause the app to crash) if the
sound is from a resource and you're using sndPlaySound. That might just be
the problem you're having. I've NEVER had a problem using PlaySound to
play sounds from a resource. The only caveat is that it won't work in the
IDE. This is because you need to provide an instance handle, and when in
the IDE the instance handle is that of the IDE and the resource isn't an
IDE resource.

Here's a complete class module that I use. Some of it you won't care
about (such as the Enum type for certain resource IDs that I use). This
is also capable of playing sounds from a resource DLL. This is a separate
DLL that can be shared among your apps. However, this DLL need to be
created with VC++. The documentation for using this class module is pretty
much all in the BeginPlaySound method. It pretty much describes everything
you need to know. One thing that I'm not sure is mentioned in any comments
is that the sound resources MUST have a type of "WAVE".

There will probably be quite a bit of line-wrapping that you'll need to
correct when you copy and paste this. Sorry 'bout that.


-----BEGIN
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "Sound"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False

Option Explicit

Private Declare Function PlaySound Lib "WINMM.DLL" Alias "PlaySoundA"
(ByVal lpszName As Any, ByVal hModule As Long, ByVal dwFlags As Long) As
Long
'Private Declare Function sndPlaySound Lib "WINMM.DLL" Alias
"sndPlaySoundA" (ByVal lpszSoundName As Any, ByVal uFlags As Long) As Long

Private m_sSoundBuffer As String

Enum SoundFlagConstants
'Constants for the dwFlags argument
SND_SYNC = &H0 ' Control does not return until sound
completes
SND_ASYNC = &H1 ' Control returns immediately after being
playback
SND_NODEFAULT = &H2 ' Don't play default sound if lpszSoundName
is invalid
SND_LOOP = &H8 ' Continously loops the sound. SND_ASYNC
MUST ALSO BE SPECIFIED, otherwise control will never return
SND_NOSTOP = &H10 ' Do not stop playback of a sound already
playing
SND_ALIAS = &H10000 ' Name is a sound event in either WIN.INI
or Registry
SND_FILENAME = &H20000 ' Name is a file name
End Enum

'Private Const SND_ALIAS = &H10000 ' name is a WIN.INI [sounds]
entry
'Private Const SND_ALIAS_ID = &H110000 ' name is a WIN.INI [sounds]
entry identifier
'Private Const SND_ALIAS_START = 0 ' must be > 4096 to keep
strings in same section of resource file
'Private Const SND_APPLICATION = &H80 ' look for application
specific association
'Private Const SND_ASYNC = &H1 ' play asynchronously
'Private Const SND_FILENAME = &H20000 ' name is a file name
'Private Const SND_LOOP = &H8 ' loop the sound until next
sndPlaySound
'Private Const SND_MEMORY = &H4 ' lpszSoundName points to a
memory file
'Private Const SND_NODEFAULT = &H2 ' silence not default, if
sound not found
'Private Const SND_NOSTOP = &H10 ' don't stop any currently
playing sound
'Private Const SND_NOWAIT = &H2000 ' don't wait if the driver
is busy
'Private Const SND_PURGE = &H40 ' purge non-static events
for task
'Private Const SND_RESERVED = &HFF000000 ' In particular these flags
are reserved
'Private Const SND_RESOURCE = &H40004 ' name is a resource name or
atom
'Private Const SND_SYNC = &H0 ' play synchronously
(default)
'Private Const SND_TYPE_MASK = &H170007
'Private Const SND_VALID = &H1F ' valid flags /
;Internal /
'Private Const SND_VALIDFLAGS = &H17201F ' Set of valid flag bits.
Anything outside

'This flag determines if sounds are on or off. This is set to True or
False
'by the class's Enabled property. This is useful so you can provide the
user with
'an option to play sounds or to not play sounds. This flag can be
overridden when
'invoking the Sounds.BeginPlaySound method. See comments in that
procedure for details.
Private m_bSoundsEnabled As Boolean


'These are constants for Resource IDs in the shared sound DLL file.
'Resource IDs for sounds in an application-specific sounds DLL file
'MUST start at 201. This allows the BeginPlaySound method to
automatically
'determine which DLL file to use. The path to each DLL file must be
assigned
'to the corresponding properties of this class. Any sound resource
compiled
'into the same component as this class can use any Resource ID, as the
'BeginPlaySound method has an optional parameter to specify whether the
'the resource is compiled in the component or exists externally, although
'it is highly recommended the same ID numbering convention be used
regardless.
Enum SoundIDConstants
ssUhOh = 101
ssQuestion = 102
ssMenuClick = 103
ssOptionClick = 103
ssThankYou = 104
ssButtonClick = 105
ssBummer = 106
ssHelpMe = 107
End Enum

'These functions are used for loading sound resources from other
'modules (i.e. a resource-only DLL file)
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA"
(ByVal lpLibFileName As String) As Long
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As
Long) As Long
Private Declare Function LoadResource Lib "kernel32" (ByVal hInstance As
Long, ByVal hResInfo As Long) As Long
Private Declare Function FreeResource Lib "kernel32" (ByVal hResData As
Long) As Long
Private Declare Function FindResource Lib "kernel32" Alias "FindResourceA"
(ByVal hInstance As Long, ByVal lpName As String, ByVal lpType As String)
As Long
Private Declare Function LockResource Lib "kernel32" (ByVal hResData As
Long) As Long
'Private Declare Function SizeofResource Lib "kernel32" (ByVal hInstance
As Long, ByVal hResInfo As Long) As Long

Private m_sSharedResourceFileName As String
Private m_sAppResourceFileName As String

Public Property Let AppResourceFileName(NewFileName As String)

m_sAppResourceFileName = NewFileName

End Property

Property Let Enabled(OnOrOff As Boolean)

'Assigns whether sounds are on or off
m_bSoundsEnabled = OnOrOff

End Property

Property Get Enabled() As Boolean

'Returns whether sounds are on or off
Enabled = m_bSoundsEnabled

End Property

Public Function BeginPlaySound(ByVal ResourceId As SoundIDConstants,
Optional ByVal Flags As SoundFlagConstants = SND_ASYNC Or SND_NODEFAULT,
Optional ByVal WaveName As String = "", Optional ByVal ExternalDLL As
Boolean = False, Optional ByVal Override As Boolean = False) As Boolean

'ResourceId is the index number assigned to the sound in the resource
file.
'To make things easier, create constants for each index number. See
the
'General Declarations section of this class module for more
information.
'This is the only required argument.

'Flags is an optional argument to pass the SND_ constants.
'If not specified, the default is to play the sound asynchronously
(control
'returns immediately) and no default sound (if iResourceId is invalid
or file
'is not found) if the specified sound cannot be played.

'WaveName is an optional argument that specifies either a .wav file or
a sound
'event (events are listed in Control Panel's Sounds applet). If this
'argument is specified, it overrides both the ResourceID and the
ExternalDLL
'arguments. Windows will look first for a sound event matching
WaveName. If it
'can't find a match, it assumes the name is a .wav file. A couple of
notes regarding
'this. Potentially, a file (if no path or extension is specified) and
an event could
'have the same name. Also throw into this mix the fact that Windows
will look for a
'.wav file in a number of different folders if no path is specified.
Let's say there
'is a sound event named Quit and there is also a file named QUIT.WAV in
the Windows
'folder and another file named QUIT.WAV in your application's folder
(as returned by
'App.Path). You pass "QUIT" for WaveName and want QUIT.WAV in the
Windows folder to play.
'Not gonna happen. Windows is going to play the QUIT sound event (upper
and lower case
'is not relevant). So, you pass "QUIT.WAV" for WaveName thinking then
that Windows
'will play the file in the Windows folder. Not gonna happen. Windows
looks first in
'the application folder. Moral? Pass a fully qualified path and file
name which
'includes the extension and DON'T hard code this path (the Windows
folder might not
'be C:\Windows on all computers). Alternatively, you can specify
either the SND_ALIAS
'or SND_FILENAME flags (but never both). SND_ALIAS means Windows will
only look for
'a matching sound event. SND_FILENAME means Windows will only look for
a .wav file;
'however, you still have the potential problem of the order of folders
in which
'Windows looks for this file.

'ExternalDLL indicates whether to play the sound from a resource-only
DLL file,
'which is the default. If this parameter is False, the sound is played
from a
'resource which is compiled into the same component as this class. To
play a
'sound in a DLL, the path and file name of the DLL must be specified
for the
'appropriate ResourceFileName property. Resource IDs less than 201 use
the
'SharedResourceFileName property; IDs greater than 201 use the
AppResourceFileName
'property.

'Override is another optional argument to ignore the Enabled property.
If not
'specifed, the default is False (do not override Enabled property). By
specifying
'True for Override, the sound will ALWAYS play, even if Enabled is
False. This
'could be useful when you need a sound to play even if the user has
sounds disabled
'(assuming that such an option was provided to the user).

'There are no checks to ensure that files exists and that IDs are
valid. This is
'because this won't cause an error. By default, there simply won't be
any sound
'played at all. If you want the system default sound to play in this
case, specify
'a Flag argument that does NOT include the ssSndNoDefault flag.

'The method returns either True or False. True means playback was
begun successfully.
'False means a sound couldn't be played due to a problem (file couldn't
be found,
'no sound resource having the specified ID, DLL module couldn't be
loaded, etc.) AND
'ssSndNoDefault was NOT specified (because then the system default
sound is played).

Dim lRet As Long
Dim hInstance As Long

Const SND_RESOURCE = &H40004 ' Name is a resource name or atom
Const SND_MEMORY = &H4 ' lpszSoundName points to a memory
file

'If both these are False, we will not play a sound. We need to return
True
'however, because the sound was not played due to some problem.
If Not Override And Not Enabled Then
BeginPlaySound = True
Exit Function
End If

On Error GoTo NoPlay

'If we're looping the sound, we have to make SURE that the sound is
being
'played asynchronously; otherwise, neither the PlaySound or
sndPlaySound API
'functions will ever return. This means the program would lock up.
If (Flags And SND_LOOP) = SND_LOOP Then
Flags = Flags Or SND_ASYNC
End If

If Len(WaveName) Then
'No problem using PlaySound here within the IDE because we're not
playing a
'resource. See NOTE below.
lRet = PlaySound(WaveName, 0&, Flags)
Else
'Make sure SND_ALIAS or SND_FILENAME was not specified or else the
sound
'may not play.
If (Flags And SND_ALIAS) = SND_ALIAS Then
Flags = Flags And Not SND_ALIAS
End If

If (Flags And SND_FILENAME) = SND_FILENAME Then
Flags = Flags And Not SND_FILENAME
End If

If ExternalDLL Then
'Get an instance handle to the appropriate module
If ResourceId <= 200 Then
hInstance = LoadLibrary(SharedResourceFileName)
Else
hInstance = LoadLibrary(AppResourceFileName)
End If

'If the module couldn't be loaded, the system's default sound
will play
'unless the ssSndNoDefault flag was specified. This flag is
specified by
'by default, so if you want to hear the system default sound in
case the
'specified sound can't be played, you'll need to pass a value
for the
'Flag argument.

'Also, the sound cannot be play asynchronously as this causes a
GPF since
'the library is immediately freed.
Flags = Flags And Not SND_ASYNC

lRet = PlaySound("#" & CStr(ResourceId), hInstance, Flags Or
SND_RESOURCE)
FreeLibrary hInstance
Else
'NOTE:
'The hInstance property of the App object will return the
instance handle
'of VB when the project is run within the IDE. This means the
PlaySound
'function won't work within the IDE. Instead, we'll load the
resource
'into a buffer and use sndPlaySound (at least until MS drops
all support
'for sndPlaySound, which isn't likely because it is still
widely used).
'PlaySound works fine if the compiled component is run because
then hInstance
'returns the proper instance handle. If you don't care about
hearing the sound
'when the program is run within the IDE, you can comment out
the first 2 lines
'and uncomment the line which follows.

'************************************************************
'NOTE: This may cause a fatal exception under NT/2000 so it's
'best to use the PlaySound function.
'm_sSoundBuffer = StrConv(LoadResData(ResourceId, "WAVE"),
vbUnicode)
'lRet = sndPlaySound(m_sSoundBuffer, Flags Or SND_MEMORY)
'************************************************************

lRet = PlaySound(ResourceId, App.hInstance, Flags Or
SND_RESOURCE)
End If
End If

BeginPlaySound = CBool(lRet)

Exit Function

NoPlay:

End Function

Public Sub EndPlaySound()

'Invoke this method if you wish to stop playback
'of a sound playing asynchronously (for example,
'a sound that is being looped).

Dim lRet As Long
lRet = PlaySound(0&, App.hInstance, 0&)

End Sub

Public Property Get AppResourceFileName() As String

AppResourceFileName = m_sAppResourceFileName

End Property

Public Property Let SharedResourceFileName(FileName As String)

m_sSharedResourceFileName = FileName

End Property

Public Property Get SharedResourceFileName() As String

SharedResourceFileName = m_sSharedResourceFileName

End Property

-----END



--
Mike
Microsoft MVP Visual Basic






.



Relevant Pages

  • Re: Playing sound from resource file via sndPlaySound API
    ... PlaySound supports playing *directly* from the resource. ... sound resources MUST have a type of "WAVE". ... 'BeginPlaySound method has an optional parameter to specify whether the ... 'Flags is an optional argument to pass the SND_ constants. ...
    (microsoft.public.vb.general.discussion)
  • Re: Play A Sound
    ... The file is loaded as a resource. ... Public Declare Function PlaySoundMem Lib "winmm.dll" Alias _ ... ' Play the sound ... Public Function BeginPlaySound(ByVal ResourceId As Long, Optional ByVal Flags As SoundFlagConstants = SND_ASYNC Or SND_NODEFAULT Or SND_RESOURCE) As Boolean ...
    (microsoft.public.vb.general.discussion)
  • Re: Fork bombing a Linux machine as a non-root user
    ... >>installation values known to the ops, and expect ops to address the ... >>resource allocation issue at time of installation. ... to use default settings suitable for all ops on all systems. ... sound card default settings and user ...
    (Fedora)
  • Re: How to play a sound from an embedded resource file? (.resx file)?
    ... Place the sound in the application resources as an embedded resource. ... a resource file (.resx file?) that plays a small sound, 25 kb, that I ... public void playSoundFromResource(object sender, EventArgs e) ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: All System Sounds Stopped
    ... In Device Manager check the Properties> Resource tab> Resource Settings for Controller and Sound devices. ... Click each resource and see what the info in the lower window pane show for any conflict info. ...
    (microsoft.public.windowsxp.help_and_support)