代码之家  ›  专栏  ›  技术社区  ›  Mathias

优雅地处理异常的问题

  •  0
  • Mathias  · 技术社区  · 15 年前

    我正在开发一个应用程序,它使用NAudio将麦克风的输入记录到.wav文件中。我的Recorder类使用BackgroundWorker来录制。我开始录音,一直这样做,直到10秒过去,或用户要求取消。这在大多数情况下都很好,只有一个例外。在debug中,当我中途拔掉录音设备的插头时,一切都将化为乌有。我希望在BackgroundWorkedCompletedTask方法中,如果worker完成事件args是e.Error(如果抛出任何标准异常,则可以正常工作),就会发现问题,但事实并非如此。当我选择“Break All”时,它会将我带到BackgroundWorkedCompletedTask方法的第行 this.waveIn.StopRecording(); ,对象上的所有属性显示为 无法计算表达式,因为本机帧位于调用堆栈的顶部。

    我假设这与NAudio使用非托管代码与设备交互有关;我希望您能就如何处理此类情况提供任何指导或建议。在这里,try/catch块似乎不起作用,那么我应该在哪里处理它呢?提前谢谢你的帮助。

    为完整起见,以下是代码的两个相关部分:

    private void RecordUsingBackgroundWorker(BackgroundWorker worker)
    {
       var devices = this.FindDevices();
       var deviceIndex = devices.IndexOf(this.requestedDevice);
       var waveIn = new WaveIn(WaveCallbackInfo.FunctionCallback());
       waveIn.DeviceNumber = deviceIndex;
       waveIn.WaveFormat = new WaveFormat(8000, 1);
       this.waveIn = waveIn;
    
       this.waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(DataIsAvailable);
    
       var filename = Globals.ThisAddIn.CommentFilePath;
       this.waveFileWriter = new WaveFileWriter(filename, waveIn.WaveFormat);
    
       var stopwatch = new Stopwatch();
       stopwatch.Start();
    
       this.waveIn.StartRecording();
       while (stopwatch.ElapsedMilliseconds < 10000 && !worker.CancellationPending)
       {
       }
    }
    

    private void BackgroundWorkedCompletedTask(object sender, RunWorkerCompletedEventArgs e)
    {
       if (e.Cancelled)
       {
       }
       else if (e.Error != null)
       {
       }
    
       if (this.waveIn != null)
       {
          this.waveIn.StopRecording();
       }
       this.CleanUpAfterStoppedRecording();
       this.IsRecording = false;
    
       if (this.RecorderBusyRecordingChanged != null)
       {
          this.RecorderBusyRecordingChanged(this, new RecorderBusyEventArgs(false));
       }
    }
    
    2 回复  |  直到 15 年前
        1
  •  1
  •   Dan Bryant    15 年前

    如果对StopRecording的调用被阻塞,一种方法是将其打包到异步调用中并等待其完成,并超时:

        public bool WaitFor(Action action, TimeSpan timeout)
        {
            var waitHandle = new AutoResetEvent(false);
            ThreadPool.QueueUserWorkItem(state =>
            {
                action();
                waitHandle.Set();
            });
    
            return waitHandle.WaitOne(timeout);
        }
    

            if (!WaitFor(this.waveIn.StopRecording, TimeSpan.FromSeconds(2)))
                throw new TimeoutException("Timed out waiting for recording to stop");
    

    请注意,这会使线程池线程在超时的操作上被阻塞,因此您可能需要调用取消挂起操作(如果存在)的API方法,或者应该在实际生产中礼貌地关闭应用程序(因为您的线程处于不确定状态,您的应用程序状态可能会受到影响)在这种情况下,最好进一步改进此方法,这样,如果任务确实在超时时间之后完成,您就可以得到肯定的确认(如果您可以调用某种形式的取消,则此方法非常有用)

        2
  •  1
  •   Hans Passant    15 年前

    嗯,我以前见过纳迪奥这样的问题。它使用mmsystem的方式使得死锁非常常见。不太确定它是否应该受到指责,它以文档化的方式使用API。只是一种很少被测试的方式。音频驱动程序也是一个常见的麻烦源,激烈的竞争似乎没有给一个好的软件工程师留下多少钱。

    也许拉里·奥斯特曼有一些意见,他在音频层做了很多工作。他在这里发帖,你可以在他的博客上留言,引导他回答这个问题。除此之外,您还需要找到一种方法让NAudio作者与微软的支持人员一起工作。或者自己解决这个问题,提供源代码是有原因的。祝你好运。

    推荐文章