How to post messages to an STA thread running a message pump?

后端 未结 2 437
鱼传尺愫
鱼传尺愫 2020-11-22 02:05

So, following this, I decided to explicitly instantiate a COM object on a dedicated STA thread. Experiments showed that the COM object needed a message pump, which I created

2条回答
  •  难免孤独
    2020-11-22 02:47

    Keep in mind that the message queue that Windows creates for an STA thread is already an implementation of a thread-safe queue. So just use it for your own purposes. Here's a base class that you can use, derive your own to include your COM object. Override the Initialize() method, it will be called as soon as the thread is ready to start executing code. Don't forget to call base.Initialize() in your override.

    It you want to run code on that thread then use the BeginInvoke or Invoke methods, just like you would for the Control.Begin/Invoke or Dispatcher.Begin/Invoke methods. Call its Dispose() method to shut down the thread, it is optional. Beware that this is only safe to do when you are 100% sure that all COM objects are finalized. Since you don't usually have that guarantee, it is better that you don't.

    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;
    }
    

提交回复
热议问题