Прохождение потока пользовательского интерфейса способ получить другой поток для вызова в C#


У меня есть проект Windows Forms на C#. В этом проекте есть устройство WaveOut, которое воспроизводится в отдельном потоке. Этот поток воспроизведения периодически должен вызывать метод потока пользовательского интерфейса и передавать ему некоторые данные (массив, содержащий аудиоинформацию, передаваемую на звуковую карту). Метод passAudio периодически вызывает подключенный обработчик событий.

Прямо сейчас, устройство waveout (WaveOutPlayer.cs) имеет EventHandler:

public class WaveOutPlayer : IDisposable
{
    public event EventHandler<AudioEventArgs> BufferSwapped;
    ...
    private void passAudio(byte[] pAudiodata)
    {
        AudioEventArgs args = new AudioEventArgs();
        args.Data = pAudiodata;
        args.WaveFormat = ws.Format;
        if (BufferSwapped != null)
        {
            BufferSwapped.Invoke(this, args);
        }
    }
}

И экземпляр формы Windows подключается к этому EventHandler:

private void Start()
{
    WaveStream Audio = new WaveStream("sine440hz_16bit_stereo.wav");
    WaveOutPlayer wp = new WaveOutPlayer(audio, 0);
    wp.BufferSize = 8192; // testing
    wp.Repeat = false; // 'true' not implemented yet
    wp.BufferSwapped += Wp_BufferSwapped;
}

private void Wp_BufferSwapped(object sender, AudioEventArgs e)
{
    // The audio buffer data can be found in the event args.
    // So analyze this Audio and manipulate some of the forms' controls 
    // accordingly.

    this.labelForAmplitude.Text = "some value";
}

Однако это приводит к исключению, так как Wp_BufferSwapped-метод фактически принадлежит потоку воспроизведения и, следовательно, не может манипулировать текстом метки.

Теперь: Как решить эту проблему, не усложняя код формы Windows? Причина этого в том, что я хочу, чтобы мои ученики (старшеклассники) делали некоторые интересные вещи с массивами и простыми пользовательскими интерфейсами. Но на данный момент у них есть только очень базовое понимание пользовательские интерфейсы работают. Они еще ничего не знают о таких вещах, как BeginInvoke или MethodInvoker. Поэтому я хочу дать им WaveOutPlayer в виде DLL-и им нужно только иметь дело с формой Windows. Есть ли решение для этой конкретной проблемы?

1 3

1 ответ:

Вы можете захватить текущий SynchronizationContext в конструкторе, а затем Post Ваш вызов обработчика событий, например:

public class WaveOutPlayer {
    private readonly SynchronizationContext _context;
    public WaveOutPlayer() {
        // capture
        _context = SynchronizationContext.Current;
    }

    public event EventHandler<AudioEventArgs> BufferSwapped;

    private void passAudio(byte[] pAudioData) {
        var args = new AudioEventArgs();
        args.Data = pAudioData;
        var handler = BufferSwapped;
        if (handler != null) {
            if (_context != null)
                // post
                _context.Post(_ => handler(this, args), null);
            else
                handler(this, args);
        }
    }
}

Делая это, вы не вводите зависимость от winforms в свой WaveOutPlayer, в то же время от WinForms не требуется никаких компилированных действий, обработчики событий просто вызываются в потоке пользовательского интерфейса. Заметим, что Post здесь будет аналогом Control.BeginInvoke. Если вам нужен аналог Control.Invoke - Используйте вместо него Send.