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

NAudio播放进度拖动时COM对象转换异常问题排查

NAudio播放时拖动进度条触发InvalidCastException异常的原因及解决

场景说明

我用NAudio开发GUI音频播放器,实现逻辑为:创建AudioFileReader,将其作为源传入VolumeSampleProvider构造函数,再用该VolumeSampleProvider初始化WaveOut——目的是实现播放时调整音量,甚至支持音量值大于1(比如给未归一化的音频实时做归一化处理)。

播放器包含进度条,播放过程中用户点击或拖动进度条时,执行流程是:先调用WaveOut.Stop(),修改AudioFileReader.Position,再调用WaveOut.Play()恢复播放。但这个操作频繁触发InvalidCastException,奇怪的是暂停或停止状态下拖动进度条从未出现异常,哪怕用的是相同的WaveOut.Pause()和WaveOut.Stop()方法。

我已尝试修改Position前先调用Stop(),也限制Position修改频率在100ms以上排查同步问题,但均未彻底解决。

异常信息

System.InvalidCastException
  HResult=0x80004002
  Message=无法将类型为“System.__ComObject”的 COM 对象强制转换为接口类型“NAudio.MediaFoundation.IMFSourceReader”。此操作失败的原因是对 COM 组件执行 QueryInterface 调用,以获取具有 IID“{70AE66F2-C809-4E4F-8915-BDCB406B7993}”的接口时,出现以下错误: 不支持的接口 (0x80004002 (E_NOINTERFACE))。
  Source=System.Private.CoreLib
  StackTrace:
   at System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget, Boolean& pfNeedsRelease)
   at NAudio.MediaFoundation.IMFSourceReader.SetCurrentPosition(Guid guidTimeFormat, IntPtr varPosition)
   at NAudio.Wave.MediaFoundationReader.Reposition(Int64 desiredPosition) in NAudio.Wave\MediaFoundationReader.cs:line 264
   at NAudio.Wave.MediaFoundationReader.Read(Byte[] buffer, Int32 offset, Int32 count) in NAudio.Wave\MediaFoundationReader.cs:line 208
   at NAudio.Wave.SampleProviders.Pcm16BitToSampleProvider.Read(Single[] buffer, Int32 offset, Int32 count) in NAudio.Wave.SampleProviders\Pcm16BitToSampleProvider.cs:line 16
   at NAudio.Wave.SampleProviders.MeteringSampleProvider.Read(Single[] buffer, Int32 offset, Int32 count) in NAudio.Wave.SampleProviders\MeteringSampleProvider.cs:line 42
   at NAudio.Wave.SampleProviders.VolumeSampleProvider.Read(Single[] buffer, Int32 offset, Int32 sampleCount) in NAudio.Wave.SampleProviders\VolumeSampleProvider.cs:line 19
   at NAudio.Wave.AudioFileReader.Read(Single[] buffer, Int32 offset, Int32 count) in NAudio.Wave\AudioFileReader.cs:line 107
   at NAudio.Wave.AudioFileReader.Read(Byte[] buffer, Int32 offset, Int32 count) in NAudio.Wave\AudioFileReader.cs:line 99
   at NAudio.Wave.SampleProviders.WaveToSampleProvider.Read(Single[] buffer, Int32 offset, Int32 count) in NAudio.Wave.SampleProviders\WaveToSampleProvider.cs:line 21
   at NAudio.Wave.SampleProviders.VolumeSampleProvider.Read(Single[] buffer, Int32 offset, Int32 sampleCount) in NAudio.Wave.SampleProviders\VolumeSampleProvider.cs:line 19
   at NAudio.Wave.SampleProviders.SampleToWaveProvider.Read(Byte[] buffer, Int32 offset, Int32 count) in NAudio.Wave.SampleProviders\SampleToWaveProvider.cs:line 24
   at NAudio.Wave.WaveOutBuffer.OnDone() in NAudio.Wave\WaveOutBuffer.cs:line 91
   at NAudio.Wave.WaveOut.EnqueueBuffers() in NAudio.Wave\WaveOut.cs:line 122
   at NAudio.Wave.WaveOut.Play() in NAudio.Wave\WaveOut.cs:line 111
   at DropNormalizer.AudioPlayer.set_PositionInPercentage(Single value) in C:\CSharp\DropNormalizer\DropNormalizer\AudioPlayer\AudioPlayer.cs:line 184
   at DropNormalizerGUI_Avalonia.Views.MainWindow.PlayerProgressBar_PointerPressed(Object sender, PointerPressedEventArgs e) in C:\CSharp\DropNormalizer\DropNormalizerGUI_Avalonia\Views\MainWindow.axaml.cs:line 253
   at Avalonia.Interactivity.EventRoute.RaiseEventImpl(RoutedEventArgs e) in /_/src/Avalonia.Interactivity/EventRoute.cs:line 152
   at Avalonia.Interactivity.EventRoute.RaiseEvent(IInteractive source, RoutedEventArgs e) in /_/src/Avalonia.Interactivity/EventRoute.cs:line 102
   at Avalonia.Interactivity.Interactive.RaiseEvent(RoutedEventArgs e) in /_/src/Avalonia.Interactivity/Interactive.cs:line 122
   at Avalonia.Input.MouseDevice.MouseDown(IMouseDevice device, UInt64 timestamp, IInputElement root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers) in /_/src/Avalonia.Input/MouseDevice.cs:line 263
   at Avalonia.Input.MouseDevice.ProcessRawEvent(RawPointerEventArgs e) in /_/src/Avalonia.Input/MouseDevice.cs:line 157
   at Avalonia.Input.InputManager.ProcessInput(RawInputEventArgs e) in /_/src/Avalonia.Input/InputManager.cs:line 36
   at Avalonia.Win32.WindowImpl.AppWndProc(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam) in /_/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs:line 526
   at Avalonia.Win32.WindowImpl.WndProc(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam) in /_/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs:line 33
   at Avalonia.Win32.Interop.UnmanagedMethods.DispatchMessage(MSG& lpmsg)
   at Avalonia.Win32.Win32Platform.RunLoop(CancellationToken cancellationToken) in /_/src/Windows/Avalonia.Win32/Win32Platform.cs:line 206
   at Avalonia.Threading.Dispatcher.MainLoop(CancellationToken cancellationToken) in /_/src/Avalonia.Base/Threading/Dispatcher.cs:line 66
   at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.Start(String[] args) in /_/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs:line 121
   at DropNormalizerGUI_Avalonia.Program.Main(String[] args) in C:\CSharp\DropNormalizer\DropNormalizerGUI_Avalonia\Program.cs:line 15

原因分析

异常核心是MediaFoundationReader的COM对象被提前释放,或在多线程环境下被非法访问:

  • 播放状态下调用WaveOut.Stop()时,WaveOut的后台线程可能仍在读取MediaFoundationReader(AudioFileReader内部依赖该组件)的数据,此时直接修改Position会触发Reposition操作,而后台线程可能已释放COM接口引用,导致QueryInterface调用失败。
  • 暂停/停止状态下无后台线程读取数据,修改Position不会触发冲突;但播放中执行Stop()后,后台线程的回调可能仍在执行,此时操作COM对象就会出现类型转换失败。

解决办法

  1. 等待PlaybackStopped事件触发后再操作:调用WaveOut.Stop()后,不要立即修改Position,而是等待PlaybackStopped事件确认播放完全停止后,再执行位置修改和恢复播放:
    private bool _isUserSeeking = false;
    
    private void PlayerProgressBar_PointerPressed(object sender, PointerPressedEventArgs e)
    {
        _isUserSeeking = true;
        _waveOut.Stop();
    }
    
    private void WaveOut_PlaybackStopped(object sender, StoppedEventArgs e)
    {
        if (_isUserSeeking)
        {
            _isUserSeeking = false;
            // 计算并设置目标位置
            _audioFileReader.Position = CalculateTargetPosition();
            _waveOut.Play();
        }
    }
    
  2. 加锁同步操作:在修改AudioFileReader.Position和调用WaveOut方法时,用锁锁住相关对象,避免多线程冲突:
    private readonly object _playerLock = new object();
    
    private void SeekToTarget(long targetPosition)
    {
        lock (_playerLock)
        {
            _waveOut.Stop();
            // 短暂等待后台线程完成读取
            Thread.Sleep(50);
            _audioFileReader.Position = targetPosition;
            _waveOut.Play();
        }
    }
    
  3. 替换MediaFoundationReader:如果问题仍存在,可针对WAV文件改用WaveFileReader,或选择其他非MediaFoundation依赖的读取器,避开COM对象的线程问题。

内容的提问来源于stack exchange,提问作者Michael Švejcar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 05:28:12