1 namespace System.Media {
4 using System.ComponentModel;
5 using System.Runtime.InteropServices;
6 using System.Runtime.Serialization;
7 using System.Runtime.Versioning;
8 using System.Diagnostics;
9 using System.Threading;
11 using System.Globalization;
12 using System.Security.Permissions;
13 using System.Security;
14 using System.Diagnostics.CodeAnalysis;
16 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer"]/*' />
20 SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes"), // This is the first class added to System.Media namespace.
21 SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly"), // vsw 427356
22 HostProtection(UI = true)
24 public class SoundPlayer : Component, ISerializable {
26 const int blockSize = 1024;
27 const int defaultLoadTimeout = 10000;// 10 secs
28 private Uri uri = null;
29 private string soundLocation = String.Empty;
30 private int loadTimeout = defaultLoadTimeout;
32 private object tag = null;
34 // used to lock all synchronous calls to the SoundPlayer object
35 private ManualResetEvent semaphore = new ManualResetEvent(true);
37 // the worker copyThread
38 // we start the worker copyThread ONLY from entry points in the SoundPlayer API
39 // we also set the tread to null only from the entry points in the SoundPlayer API
40 private Thread copyThread = null;
42 // local buffer information
44 private Stream stream = null;
45 private bool isLoadCompleted = false;
46 private Exception lastLoadException = null;
47 private bool doesLoadAppearSynchronous = false;
48 private byte[] streamData = null;
49 private AsyncOperation asyncOperation = null;
50 private readonly SendOrPostCallback loadAsyncOperationCompleted;
53 private static readonly object EventLoadCompleted = new object();
54 private static readonly object EventSoundLocationChanged = new object();
55 private static readonly object EventStreamChanged = new object();
57 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundPlayer"]/*' />
58 public SoundPlayer() {
59 loadAsyncOperationCompleted =
60 new SendOrPostCallback(LoadAsyncOperationCompleted);
63 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundPlayer1"]/*' />
64 public SoundPlayer(string soundLocation) : this() {
65 if(soundLocation == null) {
66 soundLocation = String.Empty;
68 SetupSoundLocation(soundLocation);
71 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundPlayer2"]/*' />
72 public SoundPlayer(Stream stream) : this() {
77 * Constructor used in deserialization
79 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundPlayer4"]/*' />
81 SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes"), // SerializationInfo stores LoadTimeout as an object.
82 SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") // Serialization constructor needs a Context parameter.
84 protected SoundPlayer(SerializationInfo serializationInfo, StreamingContext context) {
85 foreach(SerializationEntry entry in serializationInfo) {
87 case "SoundLocation" :
88 SetupSoundLocation((string) entry.Value);
91 stream = (Stream) entry.Value;
92 // when we deserialize a stream we have to reset its seek position
95 stream.Seek(0, SeekOrigin.Begin);
99 this.LoadTimeout = (int) entry.Value;
105 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.LoadTimeout"]/*' />
106 public int LoadTimeout {
112 throw new ArgumentOutOfRangeException("LoadTimeout", value, SR.GetString(SR.SoundAPILoadTimeout));
119 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Path"]/*' />
120 public string SoundLocation {
122 if (uri != null && uri.IsFile) {
123 FileIOPermission fiop = new FileIOPermission(PermissionState.None);
124 fiop.AllFiles = FileIOPermissionAccess.PathDiscovery;
127 return soundLocation;
131 value = String.Empty;
133 if (soundLocation.Equals(value))
136 SetupSoundLocation(value);
138 OnSoundLocationChanged(EventArgs.Empty);
142 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Stream"]/*' />
143 public Stream Stream {
145 // if the path is set, we should return null
146 // Path and Stream are mutually exclusive
157 OnStreamChanged(EventArgs.Empty);
161 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.IsLoadCompleted"]/*' />
162 public bool IsLoadCompleted {
164 return isLoadCompleted;
168 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Tag"]/*' />
178 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.LoadAsync"]/*' />
179 public void LoadAsync() {
180 // if we have a file there is nothing to load - we just pass the file to the PlaySound function
181 // if we have a stream, then we start loading the stream async
183 if (uri!= null && uri.IsFile){
184 Debug.Assert(stream == null, "we can't have a stream and a path at the same time");
185 isLoadCompleted = true;
187 FileInfo fi = new FileInfo(uri.LocalPath);
189 throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation);
192 OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
196 // if we are actively loading, keep it running
197 if (copyThread != null && copyThread.ThreadState == System.Threading.ThreadState.Running) {
200 isLoadCompleted = false;
204 asyncOperation = AsyncOperationManager.CreateOperation(null);
209 private void LoadAsyncOperationCompleted(object arg)
211 OnLoadCompleted((AsyncCompletedEventArgs)arg);
214 // called for loading a stream synchronously
215 // called either when the user is setting the path/stream and we are loading
216 // or when loading took more time than the time out
217 private void CleanupStreamData() {
219 this.streamData = null;
220 this.isLoadCompleted = false;
221 this.lastLoadException = null;
222 this.doesLoadAppearSynchronous = false;
223 this.copyThread = null;
224 this.semaphore.Set();
227 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Load"]/*' />
229 // if we have a file there is nothing to load - we just pass the file to the PlaySound function
230 // if we have a stream, then we start loading the stream sync
232 if (uri != null && uri.IsFile){
233 Debug.Assert(stream == null, "we can't have a stream and a path at the same time");
234 FileInfo fi = new FileInfo(uri.LocalPath);
236 throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation);
238 isLoadCompleted = true;
239 OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
246 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] // FileIOPermission based on URI path, but path isn't gonna change during scope of Demand
247 private void LoadAndPlay(int flags) {
248 // bug 16794: when the user does not specify a sound location nor a stream, play Beep
249 if (String.IsNullOrEmpty(soundLocation) && stream == null) {
250 SystemSounds.Beep.Play();
254 if (uri != null && uri.IsFile) {
255 // VSW 580992: With more than one thread, someone could call SoundPlayer::set_Location
256 // between the time LoadAndPlay demands FileIO and the time it calls PlaySound under elevation.
258 // Another scenario is someone calling SoundPlayer::set_Location between the time
259 // LoadAndPlay validates the sound file and the time it calls PlaySound.
260 // The SoundPlayer will end up playing an un-validated sound file.
261 // The solution is to store the uri.LocalPath on a local variable
262 string localPath = uri.LocalPath;
264 // request permission to read the file:
265 // pass the full path to the FileIOPermission
266 FileIOPermission perm = new FileIOPermission(FileIOPermissionAccess.Read, localPath);
270 isLoadCompleted = true;
271 System.Media.SoundPlayer.IntSecurity.SafeSubWindows.Demand();
273 System.ComponentModel.IntSecurity.UnmanagedCode.Assert();
274 // ValidateSoundFile calls into the MMIO API so we need UnmanagedCode permissions to do that.
275 // And of course we need UnmanagedCode permissions to all Win32::PlaySound method.
277 // don't use uri.AbsolutePath because that gives problems when there are whitespaces in file names
278 ValidateSoundFile(localPath);
279 UnsafeNativeMethods.PlaySound(localPath, IntPtr.Zero, NativeMethods.SND_NODEFAULT | flags);
281 System.Security.CodeAccessPermission.RevertAssert();
285 ValidateSoundData(streamData);
286 System.Media.SoundPlayer.IntSecurity.SafeSubWindows.Demand();
288 System.ComponentModel.IntSecurity.UnmanagedCode.Assert();
290 UnsafeNativeMethods.PlaySound(streamData, IntPtr.Zero, NativeMethods.SND_MEMORY | NativeMethods.SND_NODEFAULT | flags);
292 System.Security.CodeAccessPermission.RevertAssert();
297 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] // WebPermission based on URI path, but path isn't gonna change during scope of Demand
298 private void LoadSync() {
300 Debug.Assert((uri == null || !uri.IsFile), "we only load streams");
302 // first make sure that any possible download ended
303 if (!semaphore.WaitOne(LoadTimeout, false)) {
304 if (copyThread != null)
307 throw new TimeoutException(SR.GetString(SR.SoundAPILoadTimedOut));
310 // if we have data, then we are done
311 if (streamData != null)
314 // setup the http stream
315 if (uri != null && !uri.IsFile && stream == null) {
316 WebPermission webPerm = new WebPermission(NetworkAccess.Connect, uri.AbsolutePath);
318 WebRequest webRequest = WebRequest.Create(uri);
319 webRequest.Timeout = LoadTimeout;
321 WebResponse webResponse;
322 webResponse = webRequest.GetResponse();
324 // now get the stream
325 stream = webResponse.GetResponseStream();
328 if (stream.CanSeek) {
329 // if we can get data synchronously, then get it
332 // the data can't be loaded synchronously
333 // load it async, then wait for it to finish
334 doesLoadAppearSynchronous = true; // to avoid OnFailed call.
337 if(!semaphore.WaitOne(LoadTimeout, false)) {
338 if (copyThread != null)
341 throw new TimeoutException(SR.GetString(SR.SoundAPILoadTimedOut));
344 doesLoadAppearSynchronous = false;
346 if (lastLoadException != null)
348 throw lastLoadException;
352 // we don't need the worker copyThread anymore
353 this.copyThread = null;
356 private void LoadStream(bool loadSync) {
357 if (loadSync && stream.CanSeek) {
358 int streamLen = (int) stream.Length;
360 streamData = new byte[streamLen];
361 stream.Read(streamData, 0, streamLen);
362 isLoadCompleted = true;
363 OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
365 // lock any synchronous calls on the Sound object
368 copyThread = new Thread(new ThreadStart(this.WorkerThread));
373 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Play"]/*' />
375 LoadAndPlay(NativeMethods.SND_ASYNC);
378 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.PlaySync"]/*' />
379 public void PlaySync() {
380 LoadAndPlay(NativeMethods.SND_SYNC);
383 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.PlayLooping"]/*' />
384 public void PlayLooping() {
385 LoadAndPlay(NativeMethods.SND_LOOP | NativeMethods.SND_ASYNC);
388 private static Uri ResolveUri(string partialUri) {
391 result = new Uri(partialUri);
392 } catch (UriFormatException) {
393 // eat URI parse exceptions
396 if (result == null) {
397 // try relative to appbase
399 result = new Uri(Path.GetFullPath(partialUri));
400 } catch (UriFormatException) {
401 // eat URI parse exceptions
407 private void SetupSoundLocation(string soundLocation) {
408 // if we are loading a file, stop it right now
410 if (copyThread != null) {
415 uri = ResolveUri(soundLocation);
417 this.soundLocation = soundLocation;
420 if (!String.IsNullOrEmpty(soundLocation))
421 throw new UriFormatException(SR.GetString(SR.SoundAPIBadSoundLocation));
424 // we are referencing a web resource ...
427 // we treat it as a stream...
431 isLoadCompleted = false;
436 private void SetupStream(Stream stream) {
437 if (this.copyThread != null) {
442 this.stream = stream;
443 this.soundLocation = String.Empty;
444 this.streamData = null;
446 isLoadCompleted = false;
447 if (stream != null) {
452 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.Stop"]/*' />
453 [ResourceExposure(ResourceScope.None)]
454 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
456 IntSecurity.SafeSubWindows.Demand();
457 UnsafeNativeMethods.PlaySound((byte[]) null, IntPtr.Zero, NativeMethods.SND_PURGE);
460 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.LoadCompleted"]/*' />
461 public event AsyncCompletedEventHandler LoadCompleted {
463 Events.AddHandler(EventLoadCompleted, value);
466 Events.RemoveHandler(EventLoadCompleted, value);
470 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.SoundLocationChanged"]/*' />
471 public event EventHandler SoundLocationChanged {
473 Events.AddHandler(EventSoundLocationChanged, value);
476 Events.RemoveHandler(EventSoundLocationChanged, value);
480 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.StreamChanged"]/*' />
481 public event EventHandler StreamChanged {
483 Events.AddHandler(EventStreamChanged, value);
486 Events.RemoveHandler(EventStreamChanged, value);
490 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.OnLoadCompleted"]/*' />
491 protected virtual void OnLoadCompleted(AsyncCompletedEventArgs e) {
492 AsyncCompletedEventHandler eh = (AsyncCompletedEventHandler) Events[EventLoadCompleted];
499 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.OnSoundLocationChanged"]/*' />
500 protected virtual void OnSoundLocationChanged(EventArgs e) {
501 EventHandler eh = (EventHandler) Events[EventSoundLocationChanged];
508 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.OnStreamChanged"]/*' />
509 protected virtual void OnStreamChanged(EventArgs e) {
510 EventHandler eh = (EventHandler) Events[EventStreamChanged];
518 SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") // The set of reasons why WorkerThread should fail is not finite
520 private void WorkerThread() {
523 // setup the http stream
524 if (uri != null && !uri.IsFile && stream == null) {
525 WebRequest webRequest = WebRequest.Create(uri);
527 WebResponse webResponse = webRequest.GetResponse();
529 stream = webResponse.GetResponseStream();
532 this.streamData = new byte[blockSize];
534 int readBytes = stream.Read(streamData, currentPos, blockSize);
535 int totalBytes = readBytes;
537 while (readBytes > 0) {
538 currentPos += readBytes;
539 if (streamData.Length < currentPos + blockSize) {
540 byte[] newData = new byte[streamData.Length * 2];
541 Array.Copy(streamData, newData, streamData.Length);
542 streamData = newData;
544 readBytes = stream.Read(streamData, currentPos, blockSize);
545 totalBytes += readBytes;
548 lastLoadException = null;
550 catch (Exception exception)
552 lastLoadException = exception;
555 if (!doesLoadAppearSynchronous)
557 // Post notification back to the UI thread.
558 asyncOperation.PostOperationCompleted(
559 loadAsyncOperationCompleted,
560 new AsyncCompletedEventArgs(lastLoadException, false, null));
562 isLoadCompleted = true;
566 private unsafe void ValidateSoundFile(string fileName) {
567 NativeMethods.MMCKINFO ckRIFF = new NativeMethods.MMCKINFO();
568 NativeMethods.MMCKINFO ck = new NativeMethods.MMCKINFO();
569 NativeMethods.WAVEFORMATEX waveFormat = null;
572 IntPtr hMIO = UnsafeNativeMethods.mmioOpen(fileName, IntPtr.Zero, NativeMethods.MMIO_READ | NativeMethods.MMIO_ALLOCBUF);
574 if (hMIO == IntPtr.Zero)
575 throw new FileNotFoundException(SR.GetString(SR.SoundAPIFileDoesNotExist), this.soundLocation);
578 ckRIFF.fccType = mmioFOURCC('W', 'A','V','E');
579 if (UnsafeNativeMethods.mmioDescend(hMIO, ckRIFF, null, NativeMethods.MMIO_FINDRIFF) != 0)
580 throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveFile, this.soundLocation));
582 while (UnsafeNativeMethods.mmioDescend(hMIO, ck, ckRIFF, 0) == 0) {
583 if (ck.dwDataOffset + ck.cksize > ckRIFF.dwDataOffset + ckRIFF.cksize)
584 throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
586 if (ck.ckID == mmioFOURCC('f','m','t',' ')) {
587 if (waveFormat == null) {
589 if (dw < Marshal.SizeOf(typeof(NativeMethods.WAVEFORMATEX)))
590 dw = Marshal.SizeOf(typeof(NativeMethods.WAVEFORMATEX));
592 waveFormat = new NativeMethods.WAVEFORMATEX();
593 byte[] data = new byte[dw];
594 if (UnsafeNativeMethods.mmioRead(hMIO, data, dw) != dw)
595 throw new InvalidOperationException(SR.GetString(SR.SoundAPIReadError, this.soundLocation));
596 fixed(byte* pdata = data) {
597 Marshal.PtrToStructure((IntPtr) pdata, waveFormat);
605 UnsafeNativeMethods.mmioAscend(hMIO, ck, 0);
608 if (waveFormat == null)
609 throw new InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
611 if (waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_PCM &&
612 waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_ADPCM &&
613 waveFormat.wFormatTag != NativeMethods.WAVE_FORMAT_IEEE_FLOAT)
614 throw new InvalidOperationException(SR.GetString(SR.SoundAPIFormatNotSupported));
617 if (hMIO != IntPtr.Zero)
618 UnsafeNativeMethods.mmioClose(hMIO, 0);
622 private static void ValidateSoundData(byte[] data) {
624 Int16 wFormatTag = -1;
625 bool fmtChunkFound = false;
627 // the RIFF header should be at least 12 bytes long.
628 if (data.Length < 12)
629 throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
631 // validate the RIFF header
632 if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F')
633 throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
634 if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E')
635 throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
637 // we only care about "fmt " chunk
639 int len = data.Length;
640 while (!fmtChunkFound && position < len - 8) {
641 if (data[position] == (byte)'f' && data[position + 1] == (byte)'m' && data[position + 2] == (byte)'t' && data[position+3] == (byte)' ') {
645 fmtChunkFound = true;
646 int chunkSize = BytesToInt(data[position+7], data[position+6], data[position+5], data[position+4]);
648 // get the cbSize from the WAVEFORMATEX
651 int sizeOfWAVEFORMAT = 16;
652 if (chunkSize != sizeOfWAVEFORMAT) {
653 // we are dealing w/ WAVEFORMATEX
654 // do extra validation
655 int sizeOfWAVEFORMATEX = 18;
657 // make sure the buffer is big enough to store a short
658 if (len < position + 8 + sizeOfWAVEFORMATEX - 1)
659 throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
661 Int16 cbSize = BytesToInt16(data[position+8 + sizeOfWAVEFORMATEX - 1],
662 data[position+8 + sizeOfWAVEFORMATEX-2]);
663 if (cbSize + sizeOfWAVEFORMATEX != chunkSize)
664 throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
667 // make sure the buffer passed in is big enough to store a short
668 if(len < position + 9)
669 throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
670 wFormatTag = BytesToInt16(data[position+9], data[position+8]);
672 position += chunkSize + 8;
674 position += 8 + BytesToInt(data[position+7], data[position+6], data[position+5], data[position+4]);
679 throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIInvalidWaveHeader));
681 if (wFormatTag != NativeMethods.WAVE_FORMAT_PCM &&
682 wFormatTag != NativeMethods.WAVE_FORMAT_ADPCM &&
683 wFormatTag != NativeMethods.WAVE_FORMAT_IEEE_FLOAT)
684 throw new System.InvalidOperationException(SR.GetString(SR.SoundAPIFormatNotSupported));
687 private static Int16 BytesToInt16(byte ch0, byte ch1) {
690 res |= (int) (((int)ch0) << 8);
693 private static int BytesToInt(byte ch0, byte ch1, byte ch2, byte ch3) {
694 return mmioFOURCC((char) ch3, (char)ch2, (char) ch1, (char)ch0);
697 private static int mmioFOURCC(char ch0, char ch1, char ch2, char ch3) {
699 result |= ((int) ch0);
700 result |= ((int) ch1) << 8;
701 result |= ((int) ch2) << 16;
702 result |= ((int) ch3) << 24;
706 /// <include file='doc\SoundPlayer.uex' path='docs/doc[@for="SoundPlayer.GetObjectData"]/*' />
707 [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
708 [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] // vsw 427356
709 [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
710 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
711 if (!String.IsNullOrEmpty(this.soundLocation)) {
712 info.AddValue("SoundLocation", this.soundLocation);
715 if (this.stream != null) {
716 info.AddValue("Stream", this.stream);
719 info.AddValue("LoadTimeout", this.loadTimeout);
722 private class IntSecurity {
723 // Constructor added because of FxCop rules
724 private IntSecurity() {}
726 private static volatile CodeAccessPermission safeSubWindows;
728 internal static CodeAccessPermission SafeSubWindows {
730 if (safeSubWindows == null) {
731 safeSubWindows = new UIPermission(UIPermissionWindow.SafeSubWindows);
734 return safeSubWindows;
739 private class NativeMethods {
740 // Constructor added because of FxCop rules
741 private NativeMethods() {}
743 internal const int WAVE_FORMAT_PCM = 0x0001,
744 WAVE_FORMAT_ADPCM = 0x0002,
745 WAVE_FORMAT_IEEE_FLOAT = 0x0003;
747 internal const int MMIO_READ = 0x00000000,
748 MMIO_ALLOCBUF = 0x00010000,
749 MMIO_FINDRIFF = 0x00000020;
751 internal const int SND_SYNC = 0000,
753 SND_NODEFAULT = 0x0002,
757 SND_FILENAME = 0x00020000,
760 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
761 internal class MMCKINFO {
764 internal int fccType;
765 internal int dwDataOffset;
766 internal int dwFlags;
769 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
770 internal class WAVEFORMATEX {
771 internal System.Int16 wFormatTag;
772 internal System.Int16 nChannels;
773 internal int nSamplesPerSec;
774 internal int nAvgBytesPerSec;
775 internal System.Int16 nBlockAlign;
776 internal System.Int16 wBitsPerSample;
777 internal System.Int16 cbSize;
781 private class UnsafeNativeMethods {
782 // Constructor added because of FxCop rules
783 private UnsafeNativeMethods() {}
785 [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
786 [ResourceExposure(ResourceScope.Machine)]
787 internal static extern bool PlaySound([MarshalAs(UnmanagedType.LPWStr)] string soundName, IntPtr hmod, int soundFlags);
789 [DllImport(ExternDll.WinMM, ExactSpelling=true, CharSet=CharSet.Auto)]
790 [ResourceExposure(ResourceScope.Machine)]
791 internal static extern bool PlaySound(byte[] soundName, IntPtr hmod, int soundFlags);
793 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments")]
794 [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
795 [ResourceExposure(ResourceScope.Machine)]
796 internal static extern IntPtr mmioOpen(string fileName, IntPtr not_used, int flags);
798 [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
799 [ResourceExposure(ResourceScope.None)]
800 internal static extern int mmioAscend(IntPtr hMIO, NativeMethods.MMCKINFO lpck, int flags);
802 [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
803 [ResourceExposure(ResourceScope.None)]
804 internal static extern int mmioDescend(IntPtr hMIO,
805 [MarshalAs(UnmanagedType.LPStruct)] NativeMethods.MMCKINFO lpck,
806 [MarshalAs(UnmanagedType.LPStruct)] NativeMethods.MMCKINFO lcpkParent,
808 [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
809 [ResourceExposure(ResourceScope.None)]
810 internal static extern int mmioRead(IntPtr hMIO, [MarshalAs(UnmanagedType.LPArray)] byte[] wf, int cch);
812 [DllImport(ExternDll.WinMM, CharSet=CharSet.Auto)]
813 [ResourceExposure(ResourceScope.None)]
814 internal static extern int mmioClose(IntPtr hMIO, int flags);