从 SoundPlayer 的一个 “Bug” 看.net GC 的工作

 
我写过这么样的一个类,简化之后类似如下:
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报告:
 
还有CodeProject 上的一篇文章:
 
这实际上是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(); 释放资源。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值