托盘应用程序:从后台线程事件处理程序在主线程上创建UI



我在玩托盘应用程序。该应用程序仅在系统托盘中运行,并且没有与之关联的Windows窗体。该应用程序使用ManagementEventWatcher,并在某些情况下显示警报窗口。

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new AppContext());
}

public class AppContext : ApplicationContext
{
    private System.ComponentModel.IContainer _components;
    private NotifyIcon _notifyIcon;
    private ContextMenuStrip _contextMenu;
    private ManagementEventWatcher _regWatcher;

    public AppContext()
    {
        //Initialize context menu & tray icon
        _regWatcher = new ManagementEventWatcher(query);
        _regWatcher.EventArrived += new EventArrivedEventHandler(_regWatcher_EventArrived);
        _regWatcher.Start();
    }
    void _regWatcher_EventArrived(object sender, EventArrivedEventArgs e)
    {
        Alert.Show("Alert!", "My Message", someParam);
    }
}

public class Alert
{
    public static void Show(string title, string message, string extraInfo)
    {
        new Alert(title, message, extraInfo).ShowDialog();
    }
    private Alert(string title, string message, string extraInfo)
    {
        InitializeComponent();
        this.Icon = Properties.Resources._default;
        this.Text = title;
        this.label1.Text = message;
        this.linkLabel1.Text = extraInfo;
    }
}

有趣的是,它并没有抱怨没有以线程安全的方式访问UI。我想是因为它只存在于这个后台线程中。但稍后,当表单尝试访问剪贴板时,它无法工作,因为它正在MTA线程上运行。到目前为止,我发现的所有类似问题都已经有了一个可以调用Invoke的表单,或者可以选择使用BackgroundWorker。在这种情况下,在主线程上创建和显示警报表单的最佳方式是什么?

多亏了Idle_Mind链接到Andy Whitfield的博客文章,我找到了解决方案。我在AppContext类中添加了一个私有的全局SynchronizationContext。在构造函数中,我将其初始化为WindowsFormsSynchronizationContext的实例。然后,当注册表观察程序的事件发生时,我可以将任务发布回主线程。

public class AppContext : ApplicationContext
{
    private SynchronizationContext _uiThreadContext;
    ...
    public AppContext()
    {
        //Initialize context menu & tray icon
        _uiThreadContext = new WindowsFormsSynchronizationContext();
        _regWatcher = new ManagementEventWatcher(query);
        _regWatcher.EventArrived += new EventArrivedEventHandler(_regWatcher_EventArrived);
        _regWatcher.Start();
        ...
    }
    private void _regWatcher_EventArrived(object sender, EventArrivedEventArgs e)
    {
        ...
        _uiThreadContext.Post(new SendOrPostCallback(MyEventHandler), parameters) 
    }

最新更新