我在玩托盘应用程序。该应用程序仅在系统托盘中运行,并且没有与之关联的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)
}