2016-08-03 10:59:49 +00:00
namespace System.Media {
using System ;
using System.IO ;
using System.ComponentModel ;
using System.Runtime.InteropServices ;
using System.Runtime.Serialization ;
using System.Runtime.Versioning ;
using System.Diagnostics ;
using System.Threading ;
using System.Net ;
using System.Globalization ;
using System.Security.Permissions ;
using System.Security ;
using System.Diagnostics.CodeAnalysis ;
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer"]/*' />
[
Serializable ,
ToolboxItem ( false ) ,
SuppressMessage ( "Microsoft.Design" , "CA1020:AvoidNamespacesWithFewTypes" ) , // This is the first class added to System.Media namespace.
SuppressMessage ( "Microsoft.Usage" , "CA2240:ImplementISerializableCorrectly" ) , // vsw 427356
HostProtection ( UI = true )
]
public class SoundPlayer : Component , ISerializable {
const int blockSize = 1024 ;
const int defaultLoadTimeout = 10000 ; // 10 secs
private Uri uri = null ;
private string soundLocation = String . Empty ;
private int loadTimeout = defaultLoadTimeout ;
private object tag = null ;
// used to lock all synchronous calls to the SoundPlayer object
private ManualResetEvent semaphore = new ManualResetEvent ( true ) ;
// the worker copyThread
// we start the worker copyThread ONLY from entry points in the SoundPlayer API
// we also set the tread to null only from the entry points in the SoundPlayer API
private Thread copyThread = null ;
// local buffer information
int currentPos = 0 ;
private Stream stream = null ;
private bool isLoadCompleted = false ;
private Exception lastLoadException = null ;
private bool doesLoadAppearSynchronous = false ;
private byte [ ] streamData = null ;
private AsyncOperation asyncOperation = null ;
private readonly SendOrPostCallback loadAsyncOperationCompleted ;
// event
private static readonly object EventLoadCompleted = new object ( ) ;
private static readonly object EventSoundLocationChanged = new object ( ) ;
private static readonly object EventStreamChanged = new object ( ) ;
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundPlayer"]/*' />
public SoundPlayer ( ) {
loadAsyncOperationCompleted =
new SendOrPostCallback ( LoadAsyncOperationCompleted ) ;
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundPlayer1"]/*' />
public SoundPlayer ( string soundLocation ) : this ( ) {
if ( soundLocation = = null ) {
soundLocation = String . Empty ;
}
SetupSoundLocation ( soundLocation ) ;
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundPlayer2"]/*' />
public SoundPlayer ( Stream stream ) : this ( ) {
this . stream = stream ;
}
/ * *
* Constructor used in deserialization
* /
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundPlayer4"]/*' />
[
SuppressMessage ( "Microsoft.Performance" , "CA1808:AvoidCallsThatBoxValueTypes" ) , // SerializationInfo stores LoadTimeout as an object.
SuppressMessage ( "Microsoft.Performance" , "CA1801:AvoidUnusedParameters" ) // Serialization constructor needs a Context parameter.
]
protected SoundPlayer ( SerializationInfo serializationInfo , StreamingContext context ) {
foreach ( SerializationEntry entry in serializationInfo ) {
switch ( entry . Name ) {
case "SoundLocation" :
SetupSoundLocation ( ( string ) entry . Value ) ;
break ;
case "Stream" :
stream = ( Stream ) entry . Value ;
// when we deserialize a stream we have to reset its seek position
// vsWhidbey 180361
if ( stream . CanSeek ) {
stream . Seek ( 0 , SeekOrigin . Begin ) ;
}
break ;
case "LoadTimeout" :
this . LoadTimeout = ( int ) entry . Value ;
break ;
}
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.LoadTimeout"]/*' />
public int LoadTimeout {
get {
return loadTimeout ;
}
set {
if ( value < 0 ) {
throw new ArgumentOutOfRangeException ( "LoadTimeout" , value , SR . GetString ( SR . SoundAPILoadTimeout ) ) ;
}
loadTimeout = value ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Path"]/*' />
public string SoundLocation {
get {
if ( uri ! = null & & uri . IsFile ) {
FileIOPermission fiop = new FileIOPermission ( PermissionState . None ) ;
fiop . AllFiles = FileIOPermissionAccess . PathDiscovery ;
fiop . Demand ( ) ;
}
return soundLocation ;
}
set {
if ( value = = null )
value = String . Empty ;
if ( soundLocation . Equals ( value ) )
return ;
SetupSoundLocation ( value ) ;
OnSoundLocationChanged ( EventArgs . Empty ) ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Stream"]/*' />
public Stream Stream {
get {
// if the path is set, we should return null
// Path and Stream are mutually exclusive
if ( uri ! = null )
return null ;
return this . stream ;
}
set {
if ( stream = = value )
return ;
SetupStream ( value ) ;
OnStreamChanged ( EventArgs . Empty ) ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.IsLoadCompleted"]/*' />
public bool IsLoadCompleted {
get {
return isLoadCompleted ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Tag"]/*' />
public object Tag {
get {
return tag ;
}
set {
tag = value ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.LoadAsync"]/*' />
public void LoadAsync ( ) {
// if we have a file there is nothing to load - we just pass the file to the PlaySound function
// if we have a stream, then we start loading the stream async
//
if ( uri ! = null & & uri . IsFile ) {
Debug . Assert ( stream = = null , "we can't have a stream and a path at the same time" ) ;
isLoadCompleted = true ;
FileInfo fi = new FileInfo ( uri . LocalPath ) ;
if ( ! fi . Exists ) {
throw new FileNotFoundException ( SR . GetString ( SR . SoundAPIFileDoesNotExist ) , this . soundLocation ) ;
}
OnLoadCompleted ( new AsyncCompletedEventArgs ( null , false , null ) ) ;
return ;
}
// if we are actively loading, keep it running
if ( copyThread ! = null & & copyThread . ThreadState = = System . Threading . ThreadState . Running ) {
return ;
}
isLoadCompleted = false ;
streamData = null ;
currentPos = 0 ;
asyncOperation = AsyncOperationManager . CreateOperation ( null ) ;
LoadStream ( false ) ;
}
private void LoadAsyncOperationCompleted ( object arg )
{
OnLoadCompleted ( ( AsyncCompletedEventArgs ) arg ) ;
}
// called for loading a stream synchronously
// called either when the user is setting the path/stream and we are loading
// or when loading took more time than the time out
private void CleanupStreamData ( ) {
this . currentPos = 0 ;
this . streamData = null ;
this . isLoadCompleted = false ;
this . lastLoadException = null ;
this . doesLoadAppearSynchronous = false ;
this . copyThread = null ;
this . semaphore . Set ( ) ;
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Load"]/*' />
public void Load ( ) {
// if we have a file there is nothing to load - we just pass the file to the PlaySound function
2017-08-21 15:34:15 +00:00
// if we have a stream, then we start loading the stream sync
2016-08-03 10:59:49 +00:00
//
if ( uri ! = null & & uri . IsFile ) {
Debug . Assert ( stream = = null , "we can't have a stream and a path at the same time" ) ;
FileInfo fi = new FileInfo ( uri . LocalPath ) ;
if ( ! fi . Exists ) {
throw new FileNotFoundException ( SR . GetString ( SR . SoundAPIFileDoesNotExist ) , this . soundLocation ) ;
}
isLoadCompleted = true ;
OnLoadCompleted ( new AsyncCompletedEventArgs ( null , false , null ) ) ;
return ;
}
LoadSync ( ) ;
}
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] // FileIOPermission based on URI path, but path isn't gonna change during scope of Demand
private void LoadAndPlay ( int flags ) {
// bug 16794: when the user does not specify a sound location nor a stream, play Beep
if ( String . IsNullOrEmpty ( soundLocation ) & & stream = = null ) {
SystemSounds . Beep . Play ( ) ;
return ;
}
if ( uri ! = null & & uri . IsFile ) {
// VSW 580992: With more than one thread, someone could call SoundPlayer::set_Location
// between the time LoadAndPlay demands FileIO and the time it calls PlaySound under elevation.
//
// Another scenario is someone calling SoundPlayer::set_Location between the time
// LoadAndPlay validates the sound file and the time it calls PlaySound.
// The SoundPlayer will end up playing an un-validated sound file.
// The solution is to store the uri.LocalPath on a local variable
string localPath = uri . LocalPath ;
// request permission to read the file:
// pass the full path to the FileIOPermission
FileIOPermission perm = new FileIOPermission ( FileIOPermissionAccess . Read , localPath ) ;
perm . Demand ( ) ;
// play the path
isLoadCompleted = true ;
System . Media . SoundPlayer . IntSecurity . SafeSubWindows . Demand ( ) ;
System . ComponentModel . IntSecurity . UnmanagedCode . Assert ( ) ;
// ValidateSoundFile calls into the MMIO API so we need UnmanagedCode permissions to do that.
// And of course we need UnmanagedCode permissions to all Win32::PlaySound method.
try {
// don't use uri.AbsolutePath because that gives problems when there are whitespaces in file names
ValidateSoundFile ( localPath ) ;
UnsafeNativeMethods . PlaySound ( localPath , IntPtr . Zero , NativeMethods . SND_NODEFAULT | flags ) ;
} finally {
System . Security . CodeAccessPermission . RevertAssert ( ) ;
}
} else {
LoadSync ( ) ;
ValidateSoundData ( streamData ) ;
System . Media . SoundPlayer . IntSecurity . SafeSubWindows . Demand ( ) ;
System . ComponentModel . IntSecurity . UnmanagedCode . Assert ( ) ;
try {
UnsafeNativeMethods . PlaySound ( streamData , IntPtr . Zero , NativeMethods . SND_MEMORY | NativeMethods . SND_NODEFAULT | flags ) ;
} finally {
System . Security . CodeAccessPermission . RevertAssert ( ) ;
}
}
}
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] // WebPermission based on URI path, but path isn't gonna change during scope of Demand
private void LoadSync ( ) {
Debug . Assert ( ( uri = = null | | ! uri . IsFile ) , "we only load streams" ) ;
// first make sure that any possible download ended
if ( ! semaphore . WaitOne ( LoadTimeout , false ) ) {
if ( copyThread ! = null )
copyThread . Abort ( ) ;
CleanupStreamData ( ) ;
throw new TimeoutException ( SR . GetString ( SR . SoundAPILoadTimedOut ) ) ;
}
// if we have data, then we are done
if ( streamData ! = null )
return ;
// setup the http stream
if ( uri ! = null & & ! uri . IsFile & & stream = = null ) {
WebPermission webPerm = new WebPermission ( NetworkAccess . Connect , uri . AbsolutePath ) ;
webPerm . Demand ( ) ;
WebRequest webRequest = WebRequest . Create ( uri ) ;
webRequest . Timeout = LoadTimeout ;
WebResponse webResponse ;
webResponse = webRequest . GetResponse ( ) ;
// now get the stream
stream = webResponse . GetResponseStream ( ) ;
}
if ( stream . CanSeek ) {
// if we can get data synchronously, then get it
LoadStream ( true ) ;
} else {
// the data can't be loaded synchronously
// load it async, then wait for it to finish
doesLoadAppearSynchronous = true ; // to avoid OnFailed call.
LoadStream ( false ) ;
if ( ! semaphore . WaitOne ( LoadTimeout , false ) ) {
if ( copyThread ! = null )
copyThread . Abort ( ) ;
CleanupStreamData ( ) ;
throw new TimeoutException ( SR . GetString ( SR . SoundAPILoadTimedOut ) ) ;
}
doesLoadAppearSynchronous = false ;
if ( lastLoadException ! = null )
{
throw lastLoadException ;
}
}
// we don't need the worker copyThread anymore
this . copyThread = null ;
}
private void LoadStream ( bool loadSync ) {
if ( loadSync & & stream . CanSeek ) {
int streamLen = ( int ) stream . Length ;
currentPos = 0 ;
streamData = new byte [ streamLen ] ;
stream . Read ( streamData , 0 , streamLen ) ;
isLoadCompleted = true ;
OnLoadCompleted ( new AsyncCompletedEventArgs ( null , false , null ) ) ;
} else {
// lock any synchronous calls on the Sound object
semaphore . Reset ( ) ;
// start loading
copyThread = new Thread ( new ThreadStart ( this . WorkerThread ) ) ;
copyThread . Start ( ) ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Play"]/*' />
public void Play ( ) {
LoadAndPlay ( NativeMethods . SND_ASYNC ) ;
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.PlaySync"]/*' />
public void PlaySync ( ) {
LoadAndPlay ( NativeMethods . SND_SYNC ) ;
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.PlayLooping"]/*' />
public void PlayLooping ( ) {
LoadAndPlay ( NativeMethods . SND_LOOP | NativeMethods . SND_ASYNC ) ;
}
private static Uri ResolveUri ( string partialUri ) {
Uri result = null ;
try {
result = new Uri ( partialUri ) ;
} catch ( UriFormatException ) {
// eat URI parse exceptions
}
if ( result = = null ) {
// try relative to appbase
try {
result = new Uri ( Path . GetFullPath ( partialUri ) ) ;
} catch ( UriFormatException ) {
// eat URI parse exceptions
}
}
return result ;
}
private void SetupSoundLocation ( string soundLocation ) {
// if we are loading a file, stop it right now
//
if ( copyThread ! = null ) {
copyThread . Abort ( ) ;
CleanupStreamData ( ) ;
}
uri = ResolveUri ( soundLocation ) ;
this . soundLocation = soundLocation ;
stream = null ;
if ( uri = = null ) {
if ( ! String . IsNullOrEmpty ( soundLocation ) )
throw new UriFormatException ( SR . GetString ( SR . SoundAPIBadSoundLocation ) ) ;
} else {
if ( ! uri . IsFile ) {
// we are referencing a web resource ...
//
// we treat it as a stream...
//
streamData = null ;
currentPos = 0 ;
isLoadCompleted = false ;
}
}
}
private void SetupStream ( Stream stream ) {
if ( this . copyThread ! = null ) {
copyThread . Abort ( ) ;
CleanupStreamData ( ) ;
}
this . stream = stream ;
this . soundLocation = String . Empty ;
this . streamData = null ;
this . currentPos = 0 ;
isLoadCompleted = false ;
if ( stream ! = null ) {
uri = null ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Stop"]/*' />
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public void Stop ( ) {
IntSecurity . SafeSubWindows . Demand ( ) ;
UnsafeNativeMethods . PlaySound ( ( byte [ ] ) null , IntPtr . Zero , NativeMethods . SND_PURGE ) ;
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.LoadCompleted"]/*' />
public event AsyncCompletedEventHandler LoadCompleted {
add {
Events . AddHandler ( EventLoadCompleted , value ) ;
}
remove {
Events . RemoveHandler ( EventLoadCompleted , value ) ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundLocationChanged"]/*' />
public event EventHandler SoundLocationChanged {
add {
Events . AddHandler ( EventSoundLocationChanged , value ) ;
}
remove {
Events . RemoveHandler ( EventSoundLocationChanged , value ) ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.StreamChanged"]/*' />
public event EventHandler StreamChanged {
add {
Events . AddHandler ( EventStreamChanged , value ) ;
}
remove {
Events . RemoveHandler ( EventStreamChanged , value ) ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.OnLoadCompleted"]/*' />
protected virtual void OnLoadCompleted ( AsyncCompletedEventArgs e ) {
AsyncCompletedEventHandler eh = ( AsyncCompletedEventHandler ) Events [ EventLoadCompleted ] ;
if ( eh ! = null )
{
eh ( this , e ) ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.OnSoundLocationChanged"]/*' />
protected virtual void OnSoundLocationChanged ( EventArgs e ) {
EventHandler eh = ( EventHandler ) Events [ EventSoundLocationChanged ] ;
if ( eh ! = null )
{
eh ( this , e ) ;
}
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.OnStreamChanged"]/*' />
protected virtual void OnStreamChanged ( EventArgs e ) {
EventHandler eh = ( EventHandler ) Events [ EventStreamChanged ] ;
if ( eh ! = null )
{
eh ( this , e ) ;
}
}
[
SuppressMessage ( "Microsoft.Design" , "CA1031:DoNotCatchGeneralExceptionTypes" ) // The set of reasons why WorkerThread should fail is not finite
]
private void WorkerThread ( ) {
try
{
// setup the http stream
if ( uri ! = null & & ! uri . IsFile & & stream = = null ) {
WebRequest webRequest = WebRequest . Create ( uri ) ;
WebResponse webResponse = webRequest . GetResponse ( ) ;
stream = webResponse . GetResponseStream ( ) ;
}
this . streamData = new byte [ blockSize ] ;
int readBytes = stream . Read ( streamData , currentPos , blockSize ) ;
int totalBytes = readBytes ;
while ( readBytes > 0 ) {
currentPos + = readBytes ;
if ( streamData . Length < currentPos + blockSize ) {
byte [ ] newData = new byte [ streamData . Length * 2 ] ;
Array . Copy ( streamData , newData , streamData . Length ) ;
streamData = newData ;
}
readBytes = stream . Read ( streamData , currentPos , blockSize ) ;
totalBytes + = readBytes ;
}
lastLoadException = null ;
}
catch ( Exception exception )
{
lastLoadException = exception ;
}
if ( ! doesLoadAppearSynchronous )
{
// Post notification back to the UI thread.
asyncOperation . PostOperationCompleted (
loadAsyncOperationCompleted ,
new AsyncCompletedEventArgs ( lastLoadException , false , null ) ) ;
}
isLoadCompleted = true ;
semaphore . Set ( ) ;
}
private unsafe void ValidateSoundFile ( string fileName ) {
NativeMethods . MMCKINFO ckRIFF = new NativeMethods . MMCKINFO ( ) ;
NativeMethods . MMCKINFO ck = new NativeMethods . MMCKINFO ( ) ;
NativeMethods . WAVEFORMATEX waveFormat = null ;
int dw ;
IntPtr hMIO = UnsafeNativeMethods . mmioOpen ( fileName , IntPtr . Zero , NativeMethods . MMIO_READ | NativeMethods . MMIO_ALLOCBUF ) ;
if ( hMIO = = IntPtr . Zero )
throw new FileNotFoundException ( SR . GetString ( SR . SoundAPIFileDoesNotExist ) , this . soundLocation ) ;
try {
ckRIFF . fccType = mmioFOURCC ( 'W' , 'A' , 'V' , 'E' ) ;
if ( UnsafeNativeMethods . mmioDescend ( hMIO , ckRIFF , null , NativeMethods . MMIO_FINDRIFF ) ! = 0 )
throw new InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveFile , this . soundLocation ) ) ;
while ( UnsafeNativeMethods . mmioDescend ( hMIO , ck , ckRIFF , 0 ) = = 0 ) {
if ( ck . dwDataOffset + ck . cksize > ckRIFF . dwDataOffset + ckRIFF . cksize )
throw new InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
if ( ck . ckID = = mmioFOURCC ( 'f' , 'm' , 't' , ' ' ) ) {
if ( waveFormat = = null ) {
dw = ck . cksize ;
if ( dw < Marshal . SizeOf ( typeof ( NativeMethods . WAVEFORMATEX ) ) )
dw = Marshal . SizeOf ( typeof ( NativeMethods . WAVEFORMATEX ) ) ;
waveFormat = new NativeMethods . WAVEFORMATEX ( ) ;
byte [ ] data = new byte [ dw ] ;
if ( UnsafeNativeMethods . mmioRead ( hMIO , data , dw ) ! = dw )
throw new InvalidOperationException ( SR . GetString ( SR . SoundAPIReadError , this . soundLocation ) ) ;
fixed ( byte * pdata = data ) {
Marshal . PtrToStructure ( ( IntPtr ) pdata , waveFormat ) ;
}
} else {
//
// multiple formats?
//
}
}
UnsafeNativeMethods . mmioAscend ( hMIO , ck , 0 ) ;
}
if ( waveFormat = = null )
throw new InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
if ( waveFormat . wFormatTag ! = NativeMethods . WAVE_FORMAT_PCM & &
waveFormat . wFormatTag ! = NativeMethods . WAVE_FORMAT_ADPCM & &
waveFormat . wFormatTag ! = NativeMethods . WAVE_FORMAT_IEEE_FLOAT )
throw new InvalidOperationException ( SR . GetString ( SR . SoundAPIFormatNotSupported ) ) ;
} finally {
if ( hMIO ! = IntPtr . Zero )
UnsafeNativeMethods . mmioClose ( hMIO , 0 ) ;
}
}
private static void ValidateSoundData ( byte [ ] data ) {
int position = 0 ;
Int16 wFormatTag = - 1 ;
bool fmtChunkFound = false ;
// the RIFF header should be at least 12 bytes long.
if ( data . Length < 12 )
throw new System . InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
// validate the RIFF header
if ( data [ 0 ] ! = 'R' | | data [ 1 ] ! = 'I' | | data [ 2 ] ! = 'F' | | data [ 3 ] ! = 'F' )
throw new System . InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
if ( data [ 8 ] ! = 'W' | | data [ 9 ] ! = 'A' | | data [ 10 ] ! = 'V' | | data [ 11 ] ! = 'E' )
throw new System . InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
// we only care about "fmt " chunk
position = 12 ;
int len = data . Length ;
while ( ! fmtChunkFound & & position < len - 8 ) {
if ( data [ position ] = = ( byte ) 'f' & & data [ position + 1 ] = = ( byte ) 'm' & & data [ position + 2 ] = = ( byte ) 't' & & data [ position + 3 ] = = ( byte ) ' ' ) {
//
// fmt chunk
//
fmtChunkFound = true ;
int chunkSize = BytesToInt ( data [ position + 7 ] , data [ position + 6 ] , data [ position + 5 ] , data [ position + 4 ] ) ;
//
// get the cbSize from the WAVEFORMATEX
//
int sizeOfWAVEFORMAT = 16 ;
if ( chunkSize ! = sizeOfWAVEFORMAT ) {
// we are dealing w/ WAVEFORMATEX
// do extra validation
int sizeOfWAVEFORMATEX = 18 ;
// make sure the buffer is big enough to store a short
if ( len < position + 8 + sizeOfWAVEFORMATEX - 1 )
throw new System . InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
Int16 cbSize = BytesToInt16 ( data [ position + 8 + sizeOfWAVEFORMATEX - 1 ] ,
data [ position + 8 + sizeOfWAVEFORMATEX - 2 ] ) ;
if ( cbSize + sizeOfWAVEFORMATEX ! = chunkSize )
throw new System . InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
}
// make sure the buffer passed in is big enough to store a short
if ( len < position + 9 )
throw new System . InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
wFormatTag = BytesToInt16 ( data [ position + 9 ] , data [ position + 8 ] ) ;
position + = chunkSize + 8 ;
} else {
position + = 8 + BytesToInt ( data [ position + 7 ] , data [ position + 6 ] , data [ position + 5 ] , data [ position + 4 ] ) ;
}
}
if ( ! fmtChunkFound )
throw new System . InvalidOperationException ( SR . GetString ( SR . SoundAPIInvalidWaveHeader ) ) ;
if ( wFormatTag ! = NativeMethods . WAVE_FORMAT_PCM & &
wFormatTag ! = NativeMethods . WAVE_FORMAT_ADPCM & &
wFormatTag ! = NativeMethods . WAVE_FORMAT_IEEE_FLOAT )
throw new System . InvalidOperationException ( SR . GetString ( SR . SoundAPIFormatNotSupported ) ) ;
}
private static Int16 BytesToInt16 ( byte ch0 , byte ch1 ) {
int res ;
res = ( int ) ch1 ;
res | = ( int ) ( ( ( int ) ch0 ) < < 8 ) ;
return ( Int16 ) res ;
}
private static int BytesToInt ( byte ch0 , byte ch1 , byte ch2 , byte ch3 ) {
return mmioFOURCC ( ( char ) ch3 , ( char ) ch2 , ( char ) ch1 , ( char ) ch0 ) ;
}
private static int mmioFOURCC ( char ch0 , char ch1 , char ch2 , char ch3 ) {
int result = 0 ;
result | = ( ( int ) ch0 ) ;
result | = ( ( int ) ch1 ) < < 8 ;
result | = ( ( int ) ch2 ) < < 16 ;
result | = ( ( int ) ch3 ) < < 24 ;
return result ;
}
/// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.GetObjectData"]/*' />
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] // vsw 427356
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
void ISerializable . GetObjectData ( SerializationInfo info , StreamingContext context ) {
if ( ! String . IsNullOrEmpty ( this . soundLocation ) ) {
info . AddValue ( "SoundLocation" , this . soundLocation ) ;
}
if ( this . stream ! = null ) {
info . AddValue ( "Stream" , this . stream ) ;
}
info . AddValue ( "LoadTimeout" , this . loadTimeout ) ;
}
private class IntSecurity {
// Constructor added because of FxCop rules
private IntSecurity ( ) { }
private static volatile CodeAccessPermission safeSubWindows ;
internal static CodeAccessPermission SafeSubWindows {
get {
if ( safeSubWindows = = null ) {
safeSubWindows = new UIPermission ( UIPermissionWindow . SafeSubWindows ) ;
}
return safeSubWindows ;
}
}
}
private class NativeMethods {
// Constructor added because of FxCop rules
private NativeMethods ( ) { }
internal const int WAVE_FORMAT_PCM = 0x0001 ,
WAVE_FORMAT_ADPCM = 0x0002 ,
WAVE_FORMAT_IEEE_FLOAT = 0x0003 ;
internal const int MMIO_READ = 0x00000000 ,
MMIO_ALLOCBUF = 0x00010000 ,
MMIO_FINDRIFF = 0x00000020 ;
internal const int SND_SYNC = 0000 ,
SND_ASYNC = 0x0001 ,
SND_NODEFAULT = 0x0002 ,
SND_MEMORY = 0x0004 ,
SND_LOOP = 0x0008 ,
SND_PURGE = 0x0040 ,
SND_FILENAME = 0x00020000 ,
SND_NOSTOP = 0x0010 ;
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal class MMCKINFO {
internal int ckID ;
internal int cksize ;
internal int fccType ;
internal int dwDataOffset ;
internal int dwFlags ;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal class WAVEFORMATEX {
internal System . Int16 wFormatTag ;
internal System . Int16 nChannels ;
internal int nSamplesPerSec ;
internal int nAvgBytesPerSec ;
internal System . Int16 nBlockAlign ;
internal System . Int16 wBitsPerSample ;
internal System . Int16 cbSize ;
}
}
private class UnsafeNativeMethods {
// Constructor added because of FxCop rules
private UnsafeNativeMethods ( ) { }
[DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
[ResourceExposure(ResourceScope.Machine)]
internal static extern bool PlaySound ( [ MarshalAs ( UnmanagedType . LPWStr ) ] string soundName , IntPtr hmod , int soundFlags ) ;
[DllImport(ExternDll.WinMM, ExactSpelling=true, CharSet=CharSet.Auto)]
[ResourceExposure(ResourceScope.Machine)]
internal static extern bool PlaySound ( byte [ ] soundName , IntPtr hmod , int soundFlags ) ;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments")]
[DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
[ResourceExposure(ResourceScope.Machine)]
internal static extern IntPtr mmioOpen ( string fileName , IntPtr not_used , int flags ) ;
[DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
internal static extern int mmioAscend ( IntPtr hMIO , NativeMethods . MMCKINFO lpck , int flags ) ;
[DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
internal static extern int mmioDescend ( IntPtr hMIO ,
[MarshalAs(UnmanagedType.LPStruct)] NativeMethods . MMCKINFO lpck ,
[MarshalAs(UnmanagedType.LPStruct)] NativeMethods . MMCKINFO lcpkParent ,
int flags ) ;
[DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
internal static extern int mmioRead ( IntPtr hMIO , [ MarshalAs ( UnmanagedType . LPArray ) ] byte [ ] wf , int cch ) ;
[DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
internal static extern int mmioClose ( IntPtr hMIO , int flags ) ;
}
}
}