一尘不染

如何将消息发布到运行消息泵的STA线程?

c#

因此,在此之后,我决定在专用STA线程上显式实例化COM对象。实验表明,COM对象需要一个消息泵,该泵是通过调用Application.Run()以下命令创建的:

private MyComObj _myComObj;

// Called from Main():
Thread myStaThread = new Thread(() =>
{
    _myComObj = new MyComObj();
    _myComObj.SomethingHappenedEvent += OnSomthingHappened;
    Application.Run();
});
myStaThread.SetApartmentState(ApartmentState.STA);
myStaThread.Start();

如何从其他线程发布STA线程的消息泵中的消息?

注意: 为简洁起见,我对问题进行了大量编辑。@Servy的答案的某些部分现在似乎无关紧要,但它们是针对原始问题的。


阅读 231

收藏
2020-05-19

共1个答案

一尘不染

请记住,Windows为STA线程创建的消息队列已经是线程安全队列的实现。因此,仅将其用于您自己的目的。这是您可以使用的基类,派生自己的基类以包含COM对象。重写Initialize()方法,线程准备开始执行代码时将立即调用它。不要忘记在覆盖中调用base.Initialize()。

您希望在该线程上运行代码,然后使用BeginInvoke或Invoke方法,就像对Control.Begin /
Invoke或Dispatcher.Begin /
Invoke方法一样。调用其Dispose()方法关闭线程,这是可选的。请注意,只有当您100%确定所有COM对象都已完成时,这样做才是安全的。由于您通常没有保证,因此最好不要。

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;
}
2020-05-19