1

I have a C# WinForms app that uses NAudio for audio playback.

Here's the code for the play button:

    private void btnPlayQuestionAudio_Click(object sender, EventArgs e)
    {
        if (btnPlayQuestionAudio.Text == "?")
        {
            try
            {
                LoadQuestionAudio(lstQuestions[glbintIndex].strQuestionAudio);
                QuestionAudioPlayer.Play();
                btnPlayQuestionAudio.Text = "?";
                QuestionAudioPlayer.PlaybackStopped += QuestionAudioPlayer_PlaybackStopped;
            }
            catch (FormatException fe)
            {
                QuestionAudioPlayer.Stop();
                btnPlayQuestionAudio.Text = "?";
                MessageBox.Show(fe.ToString(), "oops.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        else if (btnPlayQuestionAudio.Text == "?")
        {
            QuestionAudioPlayer.Stop();
            btnPlayQuestionAudio.Text = "?";
        }
    }

And here's the code for stopped playback:

    private void QuestionAudioPlayer_PlaybackStopped(Object Sender, EventArgs e)
    {
        btnPlayQuestionAudio.Text = "?";
    }

Problem: When the audio plays through completely and the "QuestionAudioPlayer_PlaybackStopped" event handler executes, the app crashes in the "QuestionAudioPlayer_PlaybackStopped" event handler with the following exception:

"An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll. Additional information: Cross-thread operation not valid: Control 'btnPlayQuestionAudio' accessed from a thread other than the thread it was created on."

This didn't always happen. I can't see where this new thread is being created. I tried putting all the playback code in a background worker, but that turned out to be more trouble than it's worth. I only use background workers when I have something to do that will require more than the UI thread can handle safely and without taking a long time. How can I prevent code from being executed on a new thread? Or is there another way to handle this other than a background worker?

1 Answers1

0

Your "QuestionAudioPlayer_PlaybackStopped" method is being called in a different thread context. You'll have to do the "InvokeRequired" pattern check inside there, like so:

private void QuestionAudioPlayer_PlaybackStopped(object sender, EventArgs e)
{
    if( this.InvokeRequired )
    {
        // We're in an asynchronous context...
        MethodInvoker del = delegate
            {
                this.QuestionAudioPlayer_PlaybackStopped(sender, e);
            };
        this.Invoke(del);
        return;
    }

    // This will be executed in the synchronous context:
    btnPlayQuestionAudio.Text = "?";
}
Bob C
  • 391
  • 2
  • 10