internal const string Version = "version.dll";
internal const string WebSocket = "websocket.dll";
internal const string WinHttp = "winhttp.dll";
+ internal const string WinMM = "winmm.dll";
internal const string Ws2_32 = "ws2_32.dll";
internal const string Wtsapi32 = "wtsapi32.dll";
internal const string CompressionNative = "clrcompression.dll";
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+
+internal partial class Interop
+{
+ internal partial class User32
+ {
+ internal const int MB_OK = 0;
+ internal const int MB_ICONHAND = 0x10;
+ internal const int MB_ICONQUESTION = 0x20;
+ internal const int MB_ICONEXCLAMATION = 0x30;
+ internal const int MB_ICONASTERISK = 0x40;
+
+ [DllImport(Libraries.User32, ExactSpelling = true)]
+ internal static extern bool MessageBeep(int type);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class WinMM
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal class MMCKINFO
+ {
+ internal int ckID;
+ internal int cksize;
+ internal int fccType;
+ internal int dwDataOffset;
+ internal int dwFlags;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class WinMM
+ {
+ internal const int SND_SYNC = 0x0;
+ internal const int SND_ASYNC = 0x1;
+ internal const int SND_NODEFAULT = 0x2;
+ internal const int SND_MEMORY = 0x4;
+ internal const int SND_LOOP = 0x8;
+ internal const int SND_PURGE = 0x40;
+ internal const int SND_FILENAME = 0x20000;
+ internal const int SND_NOSTOP = 0x10;
+
+ [DllImport(Libraries.WinMM, ExactSpelling = true, CharSet = CharSet.Unicode, EntryPoint = "PlaySoundW")]
+ internal static extern bool PlaySound(string soundName, IntPtr hmod, int soundFlags);
+
+ [DllImport(Libraries.WinMM, ExactSpelling = true, EntryPoint = "PlaySoundW")]
+ internal static extern bool PlaySound(byte[] soundName, IntPtr hmod, int soundFlags);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class WinMM
+ {
+ [DllImport(Libraries.WinMM)]
+ internal static extern int mmioAscend(IntPtr hMIO, MMCKINFO lpck, int flags);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class WinMM
+ {
+ [DllImport(Interop.Libraries.WinMM)]
+ internal static extern int mmioClose(IntPtr hMIO, int flags);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class WinMM
+ {
+ internal const int MMIO_FINDRIFF = 0x00000020;
+
+ [DllImport(Libraries.WinMM)]
+ internal static extern int mmioDescend(IntPtr hMIO,
+ [MarshalAs(UnmanagedType.LPStruct)] MMCKINFO lpck,
+ [MarshalAs(UnmanagedType.LPStruct)] MMCKINFO lcpkParent,
+ int flags);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class WinMM
+ {
+ internal const int MMIO_READ = 0x00000000;
+ internal const int MMIO_ALLOCBUF = 0x00010000;
+
+ [DllImport(Libraries.WinMM, CharSet = CharSet.Auto)]
+ internal static extern IntPtr mmioOpen(string fileName, IntPtr not_used, int flags);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class WinMM
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal class WAVEFORMATEX
+ {
+ internal short wFormatTag;
+ internal short nChannels;
+ internal int nSamplesPerSec;
+ internal int nAvgBytesPerSec;
+ internal short nBlockAlign;
+ internal short wBitsPerSample;
+ internal short cbSize;
+ }
+
+ internal const int WAVE_FORMAT_PCM = 0x0001;
+ internal const int WAVE_FORMAT_ADPCM = 0x0002;
+ internal const int WAVE_FORMAT_IEEE_FLOAT = 0x0003;
+
+ [DllImport(Libraries.WinMM)]
+ internal static extern int mmioRead(IntPtr hMIO, [MarshalAs(UnmanagedType.LPArray)] byte[] wf, int cch);
+ }
+}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+// ------------------------------------------------------------------------------
+// Changes to this file must follow the http://aka.ms/api-review process.
+// ------------------------------------------------------------------------------
namespace System.Drawing
{
public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; }
}
}
-namespace System.Security.Cryptography.X509Certificates
+namespace System.Media
{
- public enum X509SelectionFlag
+ public partial class SoundPlayer : System.ComponentModel.Component, System.Runtime.Serialization.ISerializable
{
- SingleSelection = 0x00,
- MultiSelection = 0x01
+ public SoundPlayer() { }
+ public SoundPlayer(System.IO.Stream stream) { }
+ protected SoundPlayer(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext context) { }
+ public SoundPlayer(string soundLocation) { }
+ public bool IsLoadCompleted { get { throw null; } }
+ public int LoadTimeout { get { throw null; } set { } }
+ public string SoundLocation { get { throw null; } set { } }
+ public System.IO.Stream Stream { get { throw null; } set { } }
+ public object Tag { get { throw null; } set { } }
+ public event System.ComponentModel.AsyncCompletedEventHandler LoadCompleted { add { } remove { } }
+ public event System.EventHandler SoundLocationChanged { add { } remove { } }
+ public event System.EventHandler StreamChanged { add { } remove { } }
+ public void Load() { }
+ public void LoadAsync() { }
+ protected virtual void OnLoadCompleted(System.ComponentModel.AsyncCompletedEventArgs e) { }
+ protected virtual void OnSoundLocationChanged(System.EventArgs e) { }
+ protected virtual void OnStreamChanged(System.EventArgs e) { }
+ public void Play() { }
+ public void PlayLooping() { }
+ public void PlaySync() { }
+ public void Stop() { }
+ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
}
-
+ public partial class SystemSound
+ {
+ internal SystemSound() { }
+ public void Play() { }
+ }
+ public static partial class SystemSounds
+ {
+ public static System.Media.SystemSound Asterisk { get { throw null; } }
+ public static System.Media.SystemSound Beep { get { throw null; } }
+ public static System.Media.SystemSound Exclamation { get { throw null; } }
+ public static System.Media.SystemSound Hand { get { throw null; } }
+ public static System.Media.SystemSound Question { get { throw null; } }
+ }
+}
+namespace System.Security.Cryptography.X509Certificates
+{
public sealed partial class X509Certificate2UI
- {
- public static void DisplayCertificate(X509Certificates.X509Certificate2 certificate) { throw null; }
- public static void DisplayCertificate(X509Certificates.X509Certificate2 certificate, IntPtr hwndParent) { throw null; }
- public static X509Certificates.X509Certificate2Collection SelectFromCollection(X509Certificates.X509Certificate2Collection certificates, string title, string message, X509SelectionFlag selectionFlag) { throw null; }
- public static X509Certificates.X509Certificate2Collection SelectFromCollection(X509Certificates.X509Certificate2Collection certificates, string title, string message, X509SelectionFlag selectionFlag, IntPtr hwndParent) { throw null; }
+ {
+ public X509Certificate2UI() { }
+ public static void DisplayCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { }
+ public static void DisplayCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, System.IntPtr hwndParent) { }
+ public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection SelectFromCollection(System.Security.Cryptography.X509Certificates.X509Certificate2Collection certificates, string title, string message, System.Security.Cryptography.X509Certificates.X509SelectionFlag selectionFlag) { throw null; }
+ public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection SelectFromCollection(System.Security.Cryptography.X509Certificates.X509Certificate2Collection certificates, string title, string message, System.Security.Cryptography.X509Certificates.X509SelectionFlag selectionFlag, System.IntPtr hwndParent) { throw null; }
+ }
+ public enum X509SelectionFlag
+ {
+ MultiSelection = 1,
+ SingleSelection = 0,
}
}
<ItemGroup>
<ProjectReference Include="..\..\System.ComponentModel.TypeConverter\ref\System.ComponentModel.TypeConverter.csproj" />
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
+ <ProjectReference Include="..\..\System.ComponentModel.EventBasedAsync\ref\System.ComponentModel.EventBasedAsync.csproj" />
+ <ProjectReference Include="..\..\System.ComponentModel.Primitives\ref\System.ComponentModel.Primitives.csproj" />
<ProjectReference Include="..\..\System.Security.Cryptography.X509Certificates\ref\System.Security.Cryptography.X509Certificates.csproj" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
cryptui.dll!CryptUIDlgViewCertificateW
-cryptui.dll!CryptUIDlgSelectCertificateW
\ No newline at end of file
+cryptui.dll!CryptUIDlgSelectCertificateW
+user32.dll!MessageBeep
+winmm.dll!mmioAscend
+winmm.dll!mmioDescend
+winmm.dll!mmioClose
+winmm.dll!mmioOpen
+winmm.dll!mmioRead
+winmm.dll!PlaySoundW
<data name="PlatformNotSupported_X509Certificate2UI" xml:space="preserve">
<value>X509Certificate2UI is not supported on this platform.</value>
</data>
+ <data name="SoundAPIBadSoundLocation" xml:space="preserve">
+ <value>Could not determine a universal resource identifier for the sound location.</value>
+ </data>
+ <data name="SoundAPIFileDoesNotExist" xml:space="preserve">
+ <value>Please be sure a sound file exists at the specified location.</value>
+ </data>
+ <data name="SoundAPIFormatNotSupported" xml:space="preserve">
+ <value>Sound API only supports playing PCM wave files.</value>
+ </data>
+ <data name="SoundAPIInvalidWaveFile" xml:space="preserve">
+ <value>The file located at {0} is not a valid wave file.</value>
+ </data>
+ <data name="SoundAPIInvalidWaveHeader" xml:space="preserve">
+ <value>The wave header is corrupt.</value>
+ </data>
+ <data name="SoundAPILoadTimedOut" xml:space="preserve">
+ <value>The request to load the wave file in memory timed out.</value>
+ </data>
+ <data name="SoundAPILoadTimeout" xml:space="preserve">
+ <value>The LoadTimeout property of a SoundPlayer cannot be negative.</value>
+ </data>
+ <data name="SoundAPIReadError" xml:space="preserve">
+ <value>=There was an error reading the file located at {0}. Please make sure that a valid wave file exists at the specified location.</value>
+ </data>
<data name="TextParseFailedFormat" xml:space="preserve">
<value>Text "{0}" cannot be parsed. The expected text format is "{1}".</value>
</data>
<data name="PropertyValueInvalidEntry" xml:space="preserve">
<value>IDictionary parameter contains at least one entry that is not valid. Ensure all values are consistent with the object's properties.</value>
</data>
-</root>
\ No newline at end of file
+</root>
<Compile Include="$(CommonPath)\Interop\Windows\CryptUI\Interop.CryptUIDlgCertificate.cs">
<Link>Common\Interop\Windows\Crypt32\Interop.CryptUIDlgCertificate.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\User32\Interop.MessageBeep.cs">
+ <Link>Common\Interop\Windows\User32\Interop.MessageBeep.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\winmm\Interop.MMCKINFO.cs">
+ <Link>Common\Interop\Windows\winmm\Interop.MMCKINFO.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\winmm\Interop.mmioAscend.cs">
+ <Link>Common\Interop\Windows\winmm\Interop.mmioAscend.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\winmm\Interop.mmioClose.cs">
+ <Link>Common\Interop\Windows\winmm\Interop.mmioClose.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\winmm\Interop.mmioDescend.cs">
+ <Link>Common\Interop\Windows\winmm\Interop.mmioDescend.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\winmm\Interop.mmioRead.cs">
+ <Link>Common\Interop\Windows\winmm\Interop.mmioRead.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\winmm\Interop.mmioOpen.cs">
+ <Link>Common\Interop\Windows\winmm\Interop.mmioOpen.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\winmm\Interop.PlaySound.cs">
+ <Link>Common\Interop\Windows\winmm\Interop.PlaySound.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs">
<Link>Common\Interop\Windows\Interop.Libraries.cs</Link>
</Compile>
<Compile Include="System\Drawing\ImageConverter.cs" />
<Compile Include="System\Drawing\ImageFormatConverter.cs" />
<Compile Include="System\Drawing\Printing\MarginsConverter.cs" />
+ <Compile Include="System\Media\SoundPlayer.cs" />
+ <Compile Include="System\Media\SystemSound.cs" />
+ <Compile Include="System\Media\SystemSounds.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp'">
<Reference Include="System.Buffers" />
<Reference Include="System.Collections.NonGeneric" />
+ <Reference Include="System.ComponentModel.EventBasedAsync" />
+ <Reference Include="System.ComponentModel.Primitives" />
<Reference Include="System.ComponentModel.TypeConverter" />
<Reference Include="System.Diagnostics.Debug" />
<Reference Include="System.Diagnostics.Tools" />
<Reference Include="System.Drawing.Common" />
+ <Reference Include="System.IO.FileSystem" />
<Reference Include="System.Memory" />
+ <Reference Include="System.Net.Requests" />
<Reference Include="System.Resources.ResourceManager" />
- <Reference Include="System.Runtime" />
<Reference Include="System.Runtime.Extensions" />
<Reference Include="System.Runtime.InteropServices" />
+ <Reference Include="System.Runtime" />
<Reference Include="System.Security.Cryptography.X509Certificates" />
<Reference Include="System.Text.RegularExpressions" />
+ <Reference Include="System.Threading.Thread" />
<Reference Include="System.Threading" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Threading;
+
+namespace System.Media
+{
+ public class SoundPlayer : Component, ISerializable
+ {
+ private const int BlockSize = 1024;
+ private const int DefaultLoadTimeout = 10000; // 10 secs
+
+ private Uri _uri = null;
+ private string _soundLocation = string.Empty;
+ private int _loadTimeout = DefaultLoadTimeout;
+
+ // used to lock all synchronous calls to the SoundPlayer object
+ private readonly 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
+ private int _currentPos = 0;
+ private Stream _stream = null;
+ 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 s_eventLoadCompleted = new object();
+ private static readonly object s_eventSoundLocationChanged = new object();
+ private static readonly object s_eventStreamChanged = new object();
+
+ public SoundPlayer()
+ {
+ _loadAsyncOperationCompleted = new SendOrPostCallback(LoadAsyncOperationCompleted);
+ }
+
+ public SoundPlayer(string soundLocation) : this()
+ {
+ SetupSoundLocation(soundLocation ?? string.Empty);
+ }
+
+ public SoundPlayer(Stream stream) : this()
+ {
+ _stream = stream;
+ }
+
+ protected SoundPlayer(SerializationInfo serializationInfo, StreamingContext context)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public int LoadTimeout
+ {
+ get => _loadTimeout;
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("LoadTimeout", value, SR.SoundAPILoadTimeout);
+ }
+
+ _loadTimeout = value;
+ }
+ }
+
+ public string SoundLocation
+ {
+ get => _soundLocation;
+ set
+ {
+ if (value == null)
+ {
+ value = string.Empty;
+ }
+
+ if (_soundLocation.Equals(value))
+ {
+ return;
+ }
+
+ SetupSoundLocation(value);
+ OnSoundLocationChanged(EventArgs.Empty);
+ }
+ }
+
+ 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 _stream;
+ }
+ set
+ {
+ if (_stream == value)
+ {
+ return;
+ }
+
+ SetupStream(value);
+ OnStreamChanged(EventArgs.Empty);
+ }
+ }
+
+ public bool IsLoadCompleted { get; private set; } = false;
+
+ public object Tag { get; set; } = null;
+
+ 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.SoundAPIFileDoesNotExist, _soundLocation);
+ }
+
+ OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
+ return;
+ }
+
+ // if we are actively loading, keep it running
+ if (_copyThread != null && _copyThread.ThreadState == 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()
+ {
+ _currentPos = 0;
+ _streamData = null;
+ IsLoadCompleted = false;
+ _lastLoadException = null;
+ _doesLoadAppearSynchronous = false;
+ _copyThread = null;
+ _semaphore.Set();
+ }
+
+ public void Load()
+ {
+ // 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 sync
+ 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.SoundAPIFileDoesNotExist, _soundLocation);
+ }
+ IsLoadCompleted = true;
+ OnLoadCompleted(new AsyncCompletedEventArgs(null, false, null));
+ return;
+ }
+
+ LoadSync();
+ }
+
+ private void LoadAndPlay(int flags)
+ {
+ // 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)
+ {
+ // Someone can call 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;
+
+ // Play the path - don't use uri.AbsolutePath because that gives problems
+ // when there are whitespaces in file names
+ IsLoadCompleted = true;
+
+ ValidateSoundFile(localPath);
+ Interop.WinMM.PlaySound(localPath, IntPtr.Zero, Interop.WinMM.SND_NODEFAULT | flags);
+ }
+ else
+ {
+ LoadSync();
+ ValidateSoundData(_streamData);
+ Interop.WinMM.PlaySound(_streamData, IntPtr.Zero, Interop.WinMM.SND_MEMORY | Interop.WinMM.SND_NODEFAULT | flags);
+ }
+ }
+
+ 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))
+ {
+ _copyThread?.Abort();
+ CleanupStreamData();
+ throw new TimeoutException(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)
+ {
+ 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))
+ {
+ _copyThread?.Abort();
+ CleanupStreamData();
+ throw new TimeoutException(SR.SoundAPILoadTimedOut);
+ }
+
+ _doesLoadAppearSynchronous = false;
+
+ if (_lastLoadException != null)
+ {
+ throw _lastLoadException;
+ }
+ }
+
+ // we don't need the worker copyThread anymore
+ _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(WorkerThread));
+ _copyThread.Start();
+ }
+ }
+
+ public void Play()
+ {
+ LoadAndPlay(Interop.WinMM.SND_ASYNC);
+ }
+
+ public void PlaySync()
+ {
+ LoadAndPlay(Interop.WinMM.SND_SYNC);
+ }
+
+ public void PlayLooping()
+ {
+ LoadAndPlay(Interop.WinMM.SND_LOOP | Interop.WinMM.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);
+ _soundLocation = soundLocation;
+ _stream = null;
+ if (_uri == null)
+ {
+ if (!string.IsNullOrEmpty(soundLocation))
+ {
+ throw new UriFormatException(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 (_copyThread != null)
+ {
+ _copyThread.Abort();
+ CleanupStreamData();
+ }
+
+ _stream = stream;
+ _soundLocation = string.Empty;
+ _streamData = null;
+ _currentPos = 0;
+ IsLoadCompleted = false;
+ if (stream != null)
+ {
+ _uri = null;
+ }
+ }
+
+ public void Stop()
+ {
+ Interop.WinMM.PlaySound((byte[])null, IntPtr.Zero, Interop.WinMM.SND_PURGE);
+ }
+
+ public event AsyncCompletedEventHandler LoadCompleted
+ {
+ add
+ {
+ Events.AddHandler(s_eventLoadCompleted, value);
+ }
+ remove
+ {
+ Events.RemoveHandler(s_eventLoadCompleted, value);
+ }
+ }
+
+ public event EventHandler SoundLocationChanged
+ {
+ add
+ {
+ Events.AddHandler(s_eventSoundLocationChanged, value);
+ }
+ remove
+ {
+ Events.RemoveHandler(s_eventSoundLocationChanged, value);
+ }
+ }
+
+ public event EventHandler StreamChanged
+ {
+ add
+ {
+ Events.AddHandler(s_eventStreamChanged, value);
+ }
+ remove
+ {
+ Events.RemoveHandler(s_eventStreamChanged, value);
+ }
+ }
+
+ protected virtual void OnLoadCompleted(AsyncCompletedEventArgs e)
+ {
+ ((AsyncCompletedEventHandler)Events[s_eventLoadCompleted])?.Invoke(this, e);
+ }
+
+ protected virtual void OnSoundLocationChanged(EventArgs e)
+ {
+ ((EventHandler)Events[s_eventSoundLocationChanged])?.Invoke(this, e);
+ }
+
+ protected virtual void OnStreamChanged(EventArgs e)
+ {
+ ((EventHandler)Events[s_eventStreamChanged])?.Invoke(this, e);
+ }
+
+ 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();
+ }
+
+ _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)
+ {
+ IntPtr hMIO = Interop.WinMM.mmioOpen(fileName, IntPtr.Zero, Interop.WinMM.MMIO_READ | Interop.WinMM.MMIO_ALLOCBUF);
+ if (hMIO == IntPtr.Zero)
+ {
+ throw new FileNotFoundException(SR.SoundAPIFileDoesNotExist, _soundLocation);
+ }
+
+ try
+ {
+ Interop.WinMM.WAVEFORMATEX waveFormat = null;
+ var ckRIFF = new Interop.WinMM.MMCKINFO()
+ {
+ fccType = mmioFOURCC('W', 'A', 'V', 'E')
+ };
+ var ck = new Interop.WinMM.MMCKINFO();
+ if (Interop.WinMM.mmioDescend(hMIO, ckRIFF, null, Interop.WinMM.MMIO_FINDRIFF) != 0)
+ {
+ throw new InvalidOperationException(SR.Format(SR.SoundAPIInvalidWaveFile, _soundLocation));
+ }
+
+ while (Interop.WinMM.mmioDescend(hMIO, ck, ckRIFF, 0) == 0)
+ {
+ if (ck.dwDataOffset + ck.cksize > ckRIFF.dwDataOffset + ckRIFF.cksize)
+ {
+ throw new InvalidOperationException(SR.SoundAPIInvalidWaveHeader);
+ }
+
+ if (ck.ckID == mmioFOURCC('f', 'm', 't', ' '))
+ {
+ if (waveFormat == null)
+ {
+ int dw = ck.cksize;
+ if (dw < Marshal.SizeOf(typeof(Interop.WinMM.WAVEFORMATEX)))
+ {
+ dw = Marshal.SizeOf(typeof(Interop.WinMM.WAVEFORMATEX));
+ }
+
+ waveFormat = new Interop.WinMM.WAVEFORMATEX();
+ var data = new byte[dw];
+ if (Interop.WinMM.mmioRead(hMIO, data, dw) != dw)
+ {
+ throw new InvalidOperationException(SR.Format(SR.SoundAPIReadError, _soundLocation));
+ }
+
+ fixed (byte* pdata = data)
+ {
+ Marshal.PtrToStructure((IntPtr)pdata, waveFormat);
+ }
+ }
+ else
+ {
+ // multiple formats?
+ }
+ }
+ Interop.WinMM.mmioAscend(hMIO, ck, 0);
+ }
+
+ if (waveFormat == null)
+ {
+ throw new InvalidOperationException(SR.SoundAPIInvalidWaveHeader);
+ }
+ if (waveFormat.wFormatTag != Interop.WinMM.WAVE_FORMAT_PCM &&
+ waveFormat.wFormatTag != Interop.WinMM.WAVE_FORMAT_ADPCM &&
+ waveFormat.wFormatTag != Interop.WinMM.WAVE_FORMAT_IEEE_FLOAT)
+ {
+ throw new InvalidOperationException(SR.SoundAPIFormatNotSupported);
+ }
+
+ }
+ finally
+ {
+ if (hMIO != IntPtr.Zero)
+ {
+ Interop.WinMM.mmioClose(hMIO, 0);
+ }
+ }
+ }
+
+ private static void ValidateSoundData(byte[] data)
+ {
+ int position = 0;
+ short wFormatTag = -1;
+ bool fmtChunkFound = false;
+
+ // the RIFF header should be at least 12 bytes long.
+ if (data.Length < 12)
+ {
+ throw new InvalidOperationException(SR.SoundAPIInvalidWaveHeader);
+ }
+
+ // validate the RIFF header
+ if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F')
+ {
+ throw new InvalidOperationException(SR.SoundAPIInvalidWaveHeader);
+ }
+ if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E')
+ {
+ throw new InvalidOperationException(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 InvalidOperationException(SR.SoundAPIInvalidWaveHeader);
+ }
+
+ short cbSize = BytesToInt16(data[position + 8 + sizeOfWAVEFORMATEX - 1],
+ data[position + 8 + sizeOfWAVEFORMATEX - 2]);
+ if (cbSize + sizeOfWAVEFORMATEX != chunkSize)
+ {
+ throw new InvalidOperationException(SR.SoundAPIInvalidWaveHeader);
+ }
+ }
+
+ // make sure the buffer passed in is big enough to store a short
+ if (len < position + 9)
+ {
+ throw new InvalidOperationException(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 InvalidOperationException(SR.SoundAPIInvalidWaveHeader);
+ }
+
+ if (wFormatTag != Interop.WinMM.WAVE_FORMAT_PCM &&
+ wFormatTag != Interop.WinMM.WAVE_FORMAT_ADPCM &&
+ wFormatTag != Interop.WinMM.WAVE_FORMAT_IEEE_FLOAT)
+ {
+ throw new InvalidOperationException(SR.SoundAPIFormatNotSupported);
+ }
+ }
+
+ private static short BytesToInt16(byte ch0, byte ch1)
+ {
+ int res;
+ res = ch1;
+ res |= ch0 << 8;
+ return (short)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 |= ch0;
+ result |= ch1 << 8;
+ result |= ch2 << 16;
+ result |= ch3 << 24;
+ return result;
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Media
+{
+ public class SystemSound
+ {
+ private readonly int _soundType;
+
+ internal SystemSound(int soundType)
+ {
+ _soundType = soundType;
+ }
+
+ public void Play()
+ {
+ Interop.User32.MessageBeep(_soundType);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Media
+{
+ public static class SystemSounds
+ {
+ private static volatile SystemSound s_asterisk;
+ private static volatile SystemSound s_beep;
+ private static volatile SystemSound s_exclamation;
+ private static volatile SystemSound s_hand;
+ private static volatile SystemSound s_question;
+
+ public static SystemSound Asterisk
+ {
+ get => s_asterisk ?? (s_asterisk = new SystemSound(Interop.User32.MB_ICONASTERISK));
+ }
+
+ public static SystemSound Beep
+ {
+ get => s_beep ?? (s_beep = new SystemSound(Interop.User32.MB_OK));
+ }
+
+ public static SystemSound Exclamation
+ {
+ get => s_exclamation ?? (s_exclamation = new SystemSound(Interop.User32.MB_ICONEXCLAMATION));
+ }
+
+ public static SystemSound Hand
+ {
+ get => s_hand ?? (s_hand = new SystemSound(Interop.User32.MB_ICONHAND));
+ }
+
+ public static SystemSound Question
+ {
+ get => s_question ?? (s_question = new SystemSound(Interop.User32.MB_ICONQUESTION));
+ }
+ }
+}
<Compile Include="System\Drawing\ImageConverterTests.cs" />
<Compile Include="System\Drawing\ImageFormatConverterTests.cs" />
<Compile Include="System\Drawing\Printing\MarginsConverterTests.cs" />
+ <Compile Include="System\Media\SoundPlayerTests.cs" />
+ <Compile Include="System\Media\SystemSoundTests.cs" />
+ <Compile Include="System\Media\SystemSoundsTests.cs" />
</ItemGroup>
<ItemGroup>
<SupplementalTestData Include="$(PackagesDir)system.componentmodel.typeconverter.testdata\$(TestDataPackageVersion)\content\**\*.*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</SupplementalTestData>
+ <SupplementalTestData Include="$(PackagesDir)system.windows.extensions.testdata\$(TestDataPackageVersion)\content\**\*.*">
+ <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
+ </SupplementalTestData>
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.Serialization.Formatters.Binary;
+using Xunit;
+
+namespace System.Media.Test
+{
+ public class SoundPlayerTests
+ {
+ [Fact]
+ public void Ctor_Default()
+ {
+ var player = new SoundPlayer();
+ Assert.Null(player.Container);
+ Assert.False(player.IsLoadCompleted);
+ Assert.Equal(10000, player.LoadTimeout);
+ Assert.Null(player.Site);
+ Assert.Empty(player.SoundLocation);
+ Assert.Null(player.Stream);
+ Assert.Null(player.Tag);
+ }
+
+ public static IEnumerable<object[]> Stream_TestData()
+ {
+ yield return new object[] { null };
+ yield return new object[] { new MemoryStream() };
+ }
+
+ [Theory]
+ [MemberData(nameof(Stream_TestData))]
+ public void Ctor_Stream(Stream stream)
+ {
+ var player = new SoundPlayer(stream);
+ Assert.Null(player.Container);
+ Assert.False(player.IsLoadCompleted);
+ Assert.Equal(10000, player.LoadTimeout);
+ Assert.Null(player.Site);
+ Assert.Empty(player.SoundLocation);
+ Assert.Same(stream, player.Stream);
+ Assert.Null(player.Tag);
+ }
+
+ [Theory]
+ [InlineData("http://google.com")]
+ [InlineData("invalid")]
+ [InlineData("/file")]
+ public void Ctor_String(string soundLocation)
+ {
+ var player = new SoundPlayer(soundLocation);
+ Assert.Null(player.Container);
+ Assert.False(player.IsLoadCompleted);
+ Assert.Equal(10000, player.LoadTimeout);
+ Assert.Null(player.Site);
+ Assert.Equal(soundLocation ?? "", player.SoundLocation);
+ Assert.Null(player.Stream);
+ Assert.Null(player.Tag);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ public void Ctor_NullOrEmptyString_ThrowsArgumentException(string soundLocation)
+ {
+ AssertExtensions.Throws<ArgumentException>("path", null, () => new SoundPlayer(soundLocation));
+ }
+
+ public static IEnumerable<object[]> Play_String_TestData()
+ {
+ yield return new object[] { "adpcm.wav" };
+ yield return new object[] { "pcm.wav" };
+ }
+
+ public static IEnumerable<object[]> Play_InvalidString_TestData()
+ {
+ yield return new object[] { "ccitt.wav" };
+ yield return new object[] { "ima.wav" };
+ }
+
+ [Theory]
+ [MemberData(nameof(Play_String_TestData))]
+ [OuterLoop]
+ public void Load_SourceLocation_Success(string sourceLocation)
+ {
+ var soundPlayer = new SoundPlayer(sourceLocation);
+ soundPlayer.Load();
+
+ // Load again.
+ soundPlayer.Load();
+
+ // Play.
+ soundPlayer.Play();
+ }
+
+ [Theory]
+ [MemberData(nameof(Play_String_TestData))]
+ [OuterLoop]
+ public void Load_Stream_Success(string sourceLocation)
+ {
+ using (FileStream stream = File.OpenRead(sourceLocation.Replace("file://", "")))
+ {
+ var soundPlayer = new SoundPlayer(stream);
+ soundPlayer.Load();
+
+ // Load again.
+ soundPlayer.Load();
+
+ // Play.
+ soundPlayer.Play();
+ }
+ }
+
+ [Fact]
+ public void Load_NoSuchFile_ThrowsFileNotFoundException()
+ {
+ var soundPlayer = new SoundPlayer("noSuchFile");
+ Assert.Throws<FileNotFoundException>(() => soundPlayer.Load());
+ }
+
+ [Fact]
+ public void Load_NullStream_ThrowsNullReferenceException()
+ {
+ var player = new SoundPlayer();
+ Assert.Throws<NullReferenceException>(() => player.Load());
+
+ player = new SoundPlayer((Stream)null);
+ Assert.Throws<NullReferenceException>(() => player.Load());
+ }
+
+ [Theory]
+ [MemberData(nameof(Play_InvalidString_TestData))]
+ public void Load_InvalidSourceLocation_Success(string sourceLocation)
+ {
+ var soundPlayer = new SoundPlayer(sourceLocation);
+ soundPlayer.Load();
+ }
+
+ [Theory]
+ [MemberData(nameof(Play_InvalidString_TestData))]
+ public void Load_InvalidStream_Success(string sourceLocation)
+ {
+ using (FileStream stream = File.OpenRead(sourceLocation))
+ {
+ var soundPlayer = new SoundPlayer(stream);
+ soundPlayer.Load();
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(Play_String_TestData))]
+ [OuterLoop]
+ public void Play_SourceLocation_Success(string sourceLocation)
+ {
+ var soundPlayer = new SoundPlayer(sourceLocation);
+ soundPlayer.Play();
+
+ // Play again.
+ soundPlayer.Play();
+
+ // Load.
+ soundPlayer.Load();
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))]
+ public void Play_NoSuchFile_ThrowsFileNotFoundException()
+ {
+ var soundPlayer = new SoundPlayer("noSuchFile");
+ Assert.Throws<FileNotFoundException>(() => soundPlayer.Play());
+ }
+
+ [Theory]
+ [MemberData(nameof(Play_String_TestData))]
+ [OuterLoop]
+ public void Play_Stream_Success(string sourceLocation)
+ {
+ using (FileStream stream = File.OpenRead(sourceLocation.Replace("file://", "")))
+ {
+ var soundPlayer = new SoundPlayer(stream);
+ soundPlayer.Play();
+
+ // Play again.
+ soundPlayer.Play();
+
+ // Load.
+ soundPlayer.Load();
+ }
+ }
+
+ [Fact]
+ [OuterLoop]
+ public void Play_NullStream_Success()
+ {
+ var player = new SoundPlayer();
+ player.Play();
+
+ player = new SoundPlayer((Stream)null);
+ player.Play();
+ }
+
+ [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))]
+ [MemberData(nameof(Play_InvalidString_TestData))]
+ [InlineData("http://google.com")]
+ public void Play_InvalidFile_ThrowsInvalidOperationException(string sourceLocation)
+ {
+ var soundPlayer = new SoundPlayer(sourceLocation);
+ Assert.Throws<InvalidOperationException>(() => soundPlayer.Play());
+ }
+
+ [Theory]
+ [MemberData(nameof(Play_InvalidString_TestData))]
+ public void Play_InvalidStream_ThrowsInvalidOperationException(string sourceLocation)
+ {
+ using (FileStream stream = File.OpenRead(sourceLocation.Replace("file://", "")))
+ {
+ var soundPlayer = new SoundPlayer(stream);
+ Assert.Throws<InvalidOperationException>(() => soundPlayer.Play());
+ }
+ }
+
+ [Fact]
+ [OuterLoop]
+ public void PlayLooping_NullStream_Success()
+ {
+ var player = new SoundPlayer();
+ player.PlayLooping();
+
+ player = new SoundPlayer((Stream)null);
+ player.PlayLooping();
+ }
+
+ [Fact]
+ [OuterLoop]
+ public void PlaySync_NullStream_Success()
+ {
+ var player = new SoundPlayer();
+ player.PlaySync();
+
+ player = new SoundPlayer((Stream)null);
+ player.PlaySync();
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ public void LoadTimeout_SetValid_GetReturnsExpected(int value)
+ {
+ var player = new SoundPlayer { LoadTimeout = value };
+ Assert.Equal(value, player.LoadTimeout);
+ }
+
+ [Fact]
+ public void LoadTimeout_SetNegative_ThrowsArgumentOutOfRangeException()
+ {
+ var player = new SoundPlayer();
+ AssertExtensions.Throws<ArgumentOutOfRangeException>("LoadTimeout", () => player.LoadTimeout = -1);
+ }
+
+ [Theory]
+ [InlineData("http://google.com")]
+ [InlineData("invalid")]
+ [InlineData("/file")]
+ [InlineData("file:///name")]
+ public void SoundLocation_SetValid_Success(string soundLocation)
+ {
+ var player = new SoundPlayer() { SoundLocation = soundLocation };
+ Assert.Equal(soundLocation, player.SoundLocation);
+
+ bool calledHandler = false;
+ player.SoundLocationChanged += (args, sender) => calledHandler = true;
+
+ // Set the same.
+ player.SoundLocation = soundLocation;
+ Assert.Equal(soundLocation, player.SoundLocation);
+ Assert.False(calledHandler);
+
+ // Set different.
+ player.SoundLocation = soundLocation + "a";
+ Assert.Equal(soundLocation + "a", player.SoundLocation);
+ Assert.True(calledHandler);
+
+ player = new SoundPlayer("location") { SoundLocation = soundLocation };
+ Assert.Equal(soundLocation, player.SoundLocation);
+
+ using (var stream = new MemoryStream())
+ {
+ player = new SoundPlayer(stream) { SoundLocation = soundLocation };
+ Assert.Equal(soundLocation, player.SoundLocation);
+ }
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ public void SoundLocation_SetNullOrEmpty_ThrowsArgumentException(string soundLocation)
+ {
+ var player = new SoundPlayer() { SoundLocation = soundLocation };
+ Assert.Equal("", player.SoundLocation);
+
+ player = new SoundPlayer("location");
+ AssertExtensions.Throws<ArgumentException>("path", null, () => player.SoundLocation = soundLocation);
+
+ using (var stream = new MemoryStream())
+ {
+ player = new SoundPlayer(stream) { SoundLocation = soundLocation };
+ Assert.Equal("", player.SoundLocation);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(Stream_TestData))]
+ public void Stream_SetValid_Success(Stream stream)
+ {
+ var player = new SoundPlayer() { Stream = stream };
+ Assert.Equal(stream, player.Stream);
+
+ bool calledHandler = false;
+ player.StreamChanged += (args, sender) => calledHandler = true;
+
+ // Set the same.
+ player.Stream = stream;
+ Assert.Equal(stream, player.Stream);
+ Assert.False(calledHandler);
+
+ // Set different.
+ using (var other = new MemoryStream())
+ {
+ player.Stream = other;
+ Assert.Equal(other, player.Stream);
+ Assert.True(calledHandler);
+ }
+
+ player = new SoundPlayer("location") { Stream = stream };
+ Assert.Equal(stream, player.Stream);
+
+ using (var other = new MemoryStream())
+ {
+ player = new SoundPlayer(other) { Stream = stream };
+ Assert.Equal(stream, player.Stream);
+ }
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("tag")]
+ public void Tag_Set_GetReturnsExpected(object value)
+ {
+ var player = new SoundPlayer { Tag = value };
+ Assert.Equal(value, player.Tag);
+ }
+
+ [Fact]
+ public void LoadCompleted_AddRemove_Success()
+ {
+ bool calledHandler = false;
+ void handler(object args, EventArgs sender) => calledHandler = true;
+
+ var player = new SoundPlayer();
+ player.LoadCompleted += handler;
+ player.LoadCompleted -= handler;
+
+ Assert.False(calledHandler);
+ }
+
+ [Fact]
+ public void SoundLocationChanged_AddRemove_Success()
+ {
+ bool calledHandler = false;
+ void handler(object args, EventArgs sender) => calledHandler = true;
+
+ var player = new SoundPlayer();
+ player.SoundLocationChanged += handler;
+ player.SoundLocationChanged -= handler;
+
+ player.SoundLocation = "location";
+ Assert.False(calledHandler);
+ }
+
+ [Fact]
+ public void StreamChanged_AddRemove_Success()
+ {
+ bool calledHandler = false;
+ void handler(object args, EventArgs sender) => calledHandler = true;
+
+ var player = new SoundPlayer();
+ player.StreamChanged += handler;
+ player.StreamChanged -= handler;
+
+ using (var stream = new MemoryStream())
+ {
+ player.Stream = stream;
+ Assert.False(calledHandler);
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Media.Test
+{
+ [OuterLoop]
+ public class SystemSoundTests
+ {
+ public static IEnumerable<object[]> Play_TestData()
+ {
+ yield return new object[] { SystemSounds.Asterisk };
+ yield return new object[] { SystemSounds.Beep };
+ yield return new object[] { SystemSounds.Exclamation };
+ yield return new object[] { SystemSounds.Hand };
+ yield return new object[] { SystemSounds.Question };
+ }
+
+ [Theory]
+ [MemberData(nameof(Play_TestData))]
+ public void Question_Play_Success(SystemSound sound)
+ {
+ sound.Play();
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Media.Test
+{
+ public class SystemSoundsTests
+ {
+ [Fact]
+ public void Asterisk_Get_ReturnsExpected()
+ {
+ SystemSound sound = SystemSounds.Asterisk;
+ Assert.NotNull(sound);
+ Assert.Same(sound, SystemSounds.Asterisk);
+ }
+
+ [Fact]
+ public void Beep_Get_ReturnsExpected()
+ {
+ SystemSound sound = SystemSounds.Beep;
+ Assert.NotNull(sound);
+ Assert.Same(sound, SystemSounds.Beep);
+ }
+
+ [Fact]
+ public void Exclamation_Get_ReturnsExpected()
+ {
+ SystemSound sound = SystemSounds.Exclamation;
+ Assert.NotNull(sound);
+ Assert.Same(sound, SystemSounds.Exclamation);
+ }
+
+ [Fact]
+ public void Hand_Get_ReturnsExpected()
+ {
+ SystemSound sound = SystemSounds.Hand;
+ Assert.NotNull(sound);
+ Assert.Same(sound, SystemSounds.Hand);
+ }
+
+ [Fact]
+ public void Question_Get_ReturnsExpected()
+ {
+ SystemSound sound = SystemSounds.Question;
+ Assert.NotNull(sound);
+ Assert.Same(sound, SystemSounds.Question);
+ }
+ }
+}
<PackageReference Include="System.ComponentModel.TypeConverter.TestData" Version="1.0.0" />
<PackageReference Include="System.Drawing.Common.TestData" Version="1.0.7" />
<PackageReference Include="System.Text.RegularExpressions.TestData" Version="1.0.2" />
+ <PackageReference Include="System.Windows.Extensions.TestData" Version="1.0.0" />
<PackageToInclude Include="xunit.abstractions" />
<PackageToInclude Include="xunit.assert" />