You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免System.Media.SoundPlayer在底层音频设备移除时挂起?

解决System.Media.SoundPlayer设备移除导致应用挂起的问题

System.Media.SoundPlayer依赖底层winmm.dll实现,同步播放时遇到音频设备移除会导致线程无限阻塞,且无异常抛出。要解决这个问题,可从以下几个方向入手:

1. 改用异步播放,避免主线程阻塞

同步Play()方法会阻塞当前线程,设备异常时直接卡死线程。改用异步播放可以让主线程保持响应,即使播放线程出问题也不会导致整个应用挂起。

  • .NET Core 3.0+ 直接提供PlayAsync()方法:
var soundPlayer = new SoundPlayer("audio.wav");
await soundPlayer.PlayAsync();
  • .NET Framework 可自行封装异步播放方法:
public static Task PlaySoundAsync(this SoundPlayer player)
{
    var tcs = new TaskCompletionSource<bool>();
    void PlayCompletedHandler(object sender, EventArgs e)
    {
        player.PlayCompleted -= PlayCompletedHandler;
        tcs.SetResult(true);
    }
    player.PlayCompleted += PlayCompletedHandler;
    player.Play();
    return tcs.Task;
}

// 使用方式
await soundPlayer.PlaySoundAsync();

2. 提前监听音频设备状态变化

不要等设备移除后再处理,提前监听设备事件,在设备移除的第一时间停止播放并释放资源。可以通过Windows的MMDeviceAPI实现,若不想引入第三方库,可使用P/Invoke调用相关API;也可以用NAudio简化开发:

using NAudio.CoreAudioApi;

var enumerator = new MMDeviceEnumerator();
enumerator.RegisterEndpointNotificationCallback(new DeviceMonitor(yourSoundPlayerInstance));

// 自定义设备监听类
class DeviceMonitor : IMMNotificationClient
{
    private SoundPlayer _soundPlayer;

    public DeviceMonitor(SoundPlayer player)
    {
        _soundPlayer = player;
    }

    public void OnDeviceRemoved(string deviceId)
    {
        // 设备移除时立即停止播放并释放
        _soundPlayer?.Stop();
        _soundPlayer?.Dispose();
    }

    // 实现接口其他方法(空实现即可)
    public void OnDefaultDeviceChanged(DataFlow flow, Role role, string defaultDeviceId) { }
    public void OnDeviceAdded(string deviceId) { }
    public void OnDeviceStateChanged(string deviceId, DeviceState newState) { }
    public void OnPropertyValueChanged(string deviceId, PropertyKey key) { }
}

3. 给播放操作添加超时兜底

即使做了异步和提前监听,仍可能出现极端情况导致播放线程卡住。用超时机制强制终止无响应的播放任务:

var playTask = soundPlayer.PlaySoundAsync();
var timeoutTask = Task.Delay(5000); // 设置5秒超时

var completedTask = await Task.WhenAny(playTask, timeoutTask);
if (completedTask == timeoutTask)
{
    // 超时后强制停止并释放资源
    soundPlayer.Stop();
    soundPlayer.Dispose();
}

4. 替换为更健壮的音频库

System.Media.SoundPlayer本身功能简陋,对异常场景处理不足。可以替换为NAudio、MediaFoundation等专业音频库,这些库对设备异常、资源管理的处理更完善,从根源避免挂起问题。比如用NAudio播放WAV:

using NAudio.Wave;

using var waveOut = new WaveOutEvent();
using var waveReader = new WaveFileReader("audio.wav");
waveOut.Init(waveReader);
waveOut.Play();

// 设备移除时调用Stop即可,不会出现挂起
waveOut.Stop();

内容的提问来源于stack exchange,提问作者Horst Walter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 09:32:46