如何将消息发布到运行消息泵的 STA 线程? [英] How to post messages to an STA thread running a message pump?

查看:18
本文介绍了如何将消息发布到运行消息泵的 STA 线程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,根据this,我决定在专用 STA 线程上显式实例化 COM 对象.实验表明 COM 对象需要一个消息泵,我通过调用 Application.Run() 来创建它:

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 by calling 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线程的消息泵中发布来自其他线程的消息?

How do I post messages the the STA thread's message pump from other threads?

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

Note: I heavily edited the question for the sake of brevity. Some parts of @Servy's answer now seems unrelated, but they were for the original question.

推荐答案

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

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.

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

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

这篇关于如何将消息发布到运行消息泵的 STA 线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆