我寫過這麼樣的一個類,簡化之後類似如下:
public class SoundHelper
...{
private SoundPlayer _snd = new SoundPlayer();
public SoundHelper()
...{
_snd.Stream = PropertIEs.Resources.logon_wav;//@"../sounds/logon.wav";
}
public void Play()
...{
try
...{
_snd.Play();
}
catch (Exception)
...{
}
}
}
目的就是用來根據構造函數傳遞的不同枚舉來播放資源中不同的聲音。
運行的結果開始很正常,但是在音頻播放到末尾的時候會聽到刺耳的撕裂聲。
開一我還以為是我的音頻資源文件有問題,但是換了幾個文件問題依舊(不信你也可以試試,加個音頻文件到你的資源裡,然後把上面的代碼加到工程中,改改構造中的資源文件名。
SoundHelper snh = new SoundHelper(); snh.Play();)。
於是我認為這可能是SoundPlayer的Bug, 一搜索,在微軟網站上找到了一個這樣的Bug報告:
http://connect.microsoft.com/VisualStudio/feedback/VIEwFeedback.ASPx?FeedbackID=93642
還有CodeProject 上的一篇文章:
http://www.codeproject.com/cs/media/soundplayerbug.ASP
這實際上是Soundplayer設計上的缺陷,我們知道 SoundPlayer的 Play 函數實際上是通過調用API
PlaySound
實現功能。而PlaySound 在被Play調用時是用的異步模式(它被PlaySync 調用時使用同步模式),而 GC 對於非托管的API的資源收集策略是:當這個API返回時,就立刻回收它使用的資源。這就造成一個問題:當異步模式的PlaySound函數返回時,它所播放的音頻內存立刻被釋放,但是這個時候——因為異步播放,播放還沒有完成。於是PlaySound 播放課一段損壞了的內存,於是就出現了上面的Bug。
要解決這個Bug 其實也很簡單。可以用SoundPlayer 的PlaySync 加上線程在框架的層次上實現異步播放,這樣就不用擔心GC會提前回收你的資源了(因為PlaySync調用 PlaySound實際上是用這個API的同步模式 SND_SYNC),或者就像CodeProject 那篇文章裡的方法,用GCHandle來鎖住資源,然後在適當的時機調用 gcHandle.Value.Free(); 釋放資源。