Проблема обработки исключений изящно - PullRequest
0 голосов
/ 13 августа 2010

Я работаю над приложением, которое записывает ввод с микрофонов в файлы .wav, используя NAudio. Мой класс Recorder использует BackgroundWorker для записи. Я начинаю запись и продолжаю делать это до тех пор, пока не истечет 10 секунд или пользователь не запросит отмену. Это работает нормально в большинстве случаев, кроме одного. При отладке, когда я отсоединяю записывающее устройство на полпути, все идет в никуда. Я ожидал, что проблема будет обнаружена в методе BackgroundWorkedCompletedTask в случае, когда аргументом события завершения работника является e.Error (что прекрасно работает, если я выбрасываю любое стандартное исключение), но это не так. Когда я выбираю «Разбить все», он приводит меня к методу 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 ]

1 голос
/ 13 августа 2010

Хм, я видел проблемы с NAudio, как это раньше.Он использует mmsystem таким образом, что делает взаимоблокировку довольно распространенной.Не уверен, что он заслуживает вины, он использует API документированным способом.Просто способ, который редко подвергается испытанию.Аудиодрайверы также являются распространенным источником проблем, потому что конкуренция не оставляет много денег для хорошего инженера-программиста.

Отключение устройств во время их использования всегда является проблемой.а также вытащить флешку из слота USB во время записи.Или отключите эмулятор последовательного порта USB, когда порт открыт.Желание отключить устройства во время их использования кажется непреодолимым.Хотя после нескольких попыток им это надоест.

Возможно, у Ларри Остермана есть какой-то вклад, он много работал над звуковым слоем.Он публикует здесь, вы можете оставить сообщение в своем блоге, чтобы направить его на этот вопрос.Кроме того, вам нужно найти способ заставить автора NAudio работать вместе со службой поддержки Microsoft.Или решить это самостоятельно, исходный код предоставлен по причине.Удачи.

1 голос
/ 13 августа 2010

Если вызов 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, который отменяет ожидающие операции (если таковые существуют), либо вам следует продолжить отключениеваше приложение вежливо (поскольку у вас поток в подвешенном состоянии и состояние вашего приложения может быть скомпрометировано.) В реальном производственном сценарии было бы неплохо улучшить этот метод немного дальше, чтобы вы могли получить положительное подтверждение, если задача когда-либо выполняласьзавершено после периода ожидания (полезно, если у вас есть какая-либо форма отмены, которую вы можете вызвать.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...