1

Hans gives this great answer about pumping an STA thread: https://stackoverflow.com/a/21684059/197229

using System;
using System.Threading;
using System.Windows.Forms;

class STAThread : IDisposable {
    public STAThread() {
        using (mre = new ManualResetEvent(false)) {
            thread = new Thread(() => {
                Application.Idle += Initialize;
                Application.Run();
            });
            thread.IsBackground = true;
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            mre.WaitOne();
        }
    }
    public void BeginInvoke(Delegate dlg, params Object[] args) {
        if (ctx == null) throw new ObjectDisposedException("STAThread");
        ctx.Post((_) => dlg.DynamicInvoke(args), null);
    }
    public object Invoke(Delegate dlg, params Object[] args) {
        if (ctx == null) throw new ObjectDisposedException("STAThread");
        object result = null;
        ctx.Send((_) => result = dlg.DynamicInvoke(args), null);
        return result;
    }
    protected virtual void Initialize(object sender, EventArgs e) {
        ctx = SynchronizationContext.Current;
        mre.Set();
        Application.Idle -= Initialize;
    }
    public void Dispose() {
        if (ctx != null) {
            ctx.Send((_) => Application.ExitThread(), null);
            ctx = null;
        }
    }
    private Thread thread;
    private SynchronizationContext ctx;
    private ManualResetEvent mre;
}

But it relies on calls like Application.Run which is a Windows Forms class, I don't think I want in a non-UI static library.

So is there a way to tweak this, or perhaps it could even be used as is?

Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
  • You can interop GetMessage/TranslateMessage/DispatchMessage : https://stackoverflow.com/questions/2222365/what-is-a-message-pump it's not strictly equivalent to Application.Run but may be sufficient depending on your context. – Simon Mourier Aug 07 '20 at 17:41

1 Answers1

1

You may use AsyncContextThread from Nito.AsyncEx.Context nuget by Stephen Cleary. From its github description

AsyncContextThread provides properties that can be used to schedule tasks on that thread.

The nuget contains a custom implementation of SynchronizationContext, so the code could be easily rewritten e.g. as:

using System;
using System.Threading;
using Nito.AsyncEx;

class STAThread : IDisposable
{
    public STAThread()
    {
        ctx = new AsyncContextThread();
    }

    public void BeginInvoke(Delegate dlg, params Object[] args)
    {
        ctx.Context.SynchronizationContext
            .Post((_) => dlg.DynamicInvoke(args), null);
    }

    public object Invoke(Delegate dlg, params Object[] args)
    {
        object result = null;
        ctx.Context.SynchronizationContext
            .Send((_) => result = dlg.DynamicInvoke(args), null);
        return result;
    }
    
    public void Dispose()
    {
        ctx.JoinAsync().GetAwaiter().GetResult();
        ctx.Dispose();
    }

    private readonly AsyncContextThread ctx;
}

Btw, from this MSDN article, not all implementations of SynchronizationContext guarantee that delegates will be executed on specific thread, while WinForms and WPF SynchronizationContext guarantee that, default and ASP.NET do not.

Renat
  • 7,718
  • 2
  • 20
  • 34