Как создать форму с отдельным потоком для обработки виндоус сообщений?

199
17 сентября 2018, 17:30

Для программы создаётся расширение на .NET с использованием WinForms. Из этого расширение вызывается форма.

Проблема в том, что поток с виндоус очередью у основного приложения повисает. Как сделать, так чтобы у формы расширения был отдельный поток на обработку очереди сообщений?
И как его правильно потом остановить после отключения расширения?

UPD

Утрированный пример:

private void button1_Click(object sender, EventArgs e)
{
    Form2 form2 = new Form2();
    form2.Show();
    Thread.Sleep(1000 * 10); // что-то долго делает.
    form2.Close();
}

Здесь требуется чтобы форма form2 нормально прорисовывалась и реагировала на действия пользователя, хотя при этом поток родительской формы виснет на обработке чего-нибудь.

Answer 1

Чтобы form2 работала в отдельном потоке независимо от родительской окна можно создать поток и установить у него апартаменты STA. Из этом потока показываем form2.

Основываясь на этом примере создаем класс второго потока:
(UPD по совету @PavelMayorov упрощаем реализацию)

class SecondWindowsThread : IDisposable
{
    private SynchronizationContext ctx;
    public SecondWindowsThread()
    {
        ManualResetEvent mre = new ManualResetEvent(false);
        try
        {
            Thread thread = new Thread(
                () =>
                {
                    this.ctx = new WindowsFormsSynchronizationContext();
                    SynchronizationContext.SetSynchronizationContext(this.ctx);
                    mre.Set();
                    Application.Run();
                }
            );
            thread.IsBackground = true;
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            mre.WaitOne();
        }
        finally
        {
            mre.Dispose();
        }
    }
    #region IDisposable Support
    private bool disposedValue = false;
    public void Dispose()
    {
        if (disposedValue) return;
        if (this.ctx != null)
        {
            this.ctx.Send((_) => Application.ExitThread(), null);
            this.ctx = null;
        }
        disposedValue = true;
    }
    #endregion
    public SynchronizationContext SynchronizationContext
    {
        get
        {
            if (this.disposedValue) throw new ObjectDisposedException(nameof(SynchronizationContext));
            return this.ctx;
        }
    }
}

Тогда обработчик кнопки будет такой:

private void button1_Click(object sender, EventArgs e)
{
    Form2 form2 = null;
    using (var t2 = new SecondWindowsThread())
    {
        t2.SynchronizationContext.Send(
            (_) =>
            {
                form2 = new Form2();
                form2.Show();
            }, null);
        Thread.Sleep(1000 * 10); // что-то долго делает.
        t2.SynchronizationContext.Send((_) => form2.Close(), null);
    }
}

При этом надо учитывать, что работать с компонентами одной формы из другой надо через синхронизирующий контекст или методы Control.Invoke, Control.BeginInvoke.

Лабораторная работа

READ ALSO
Как правильно упаковывать ImageTarget(Vuforia) в AssetBundle?

Как правильно упаковывать ImageTarget(Vuforia) в AssetBundle?

Во время работы, программа должна принимать AssetBundleAssetBundle должен содержать ImageTarget и контент, относящийся к нему

218
LINQ из выражения в точечную нотацию

LINQ из выражения в точечную нотацию

Есть классы моделей

157
Как десериализовать такой JSON (в Visual Studio)? [дубликат]

Как десериализовать такой JSON (в Visual Studio)? [дубликат]

На данный вопрос уже ответили:

179
Конструктор Android в Visual Studio

Конструктор Android в Visual Studio

Решил сделать программку на шарпе для андроидДобавил в VS installer все инструменты для андроид розработки

200