问题处理异常优雅

时间:2010-08-12 22:32:24

标签: c# exception

我正在开发一个应用程序,它使用NAudio记录从麦克风到.wav文件的输入。我的Recorder类使用BackgroundWorker进行记录。我开始录制并继续这样做,直到10秒钟或者用户请求取消。除了一个,大多数情况下都能正常工作。在调试中,当我在中途拔掉录音设备时,事情无处可去。我预计,在工作完成事件args为e.Error(如果我抛出任何标准异常时工作正常)的情况下,问题将在BackgroundWorkedCompletedTask方法中捕获,但事实并非如此。当我选择“全部中断”时,它会将我带到行this.waveIn.StopRecording();上的BackgroundWorkedCompletedTask方法,并且对象上的所有属性都显示为无法计算表达式,因为本机框位于调用堆栈的顶部。

我认为这与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 个答案:

答案 0 :(得分:1)

如果对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方法(如果存在),要么继续礼貌地关闭应用程序(因为你有一个线程处于不稳定状态,你的应用程序状态可能会受到损害。)在实际的生产场景中,最好进一步改进这个方法,这样如果任务在超时后完成,你就可以得到肯定的确认句点(如果您有某种形式的取消,则可以调用。)

答案 1 :(得分:1)

Hmya,我以前见过这样的NAudio问题。它以一种使死锁很常见的方式使用mmsystem。不确定它应该受到指责,它以文档化的方式使用API​​。只是一种很少进行测试的方式。音频驱动程序也是一个常见的麻烦来源,恶劣的竞争对于一个优秀的软件工程师来说似乎没有多少钱。

在设备使用过程中拔出插头始终是一个问题,同时也可以在写入时将闪存驱动器从USB插槽中拉出来。或者在端口打开时拔出USB串行端口仿真器。在使用设备时拔掉设备的冲动似乎是不可抗拒的。虽然尝试了几次后他们会对它感到厌倦。

也许Larry Osterman可能会有一些意见,他在音频层上做了很多工作。他在这里发帖,你可以在他的博客上留言告诉他这个问题。除此之外,您还需要找到一种方法让NAudio作者与Microsoft支持人员一起工作。或者自己解决这个问题,提供源代码是有原因的。祝你好运。