【发布时间】:2015-07-06 13:48:18
【问题描述】:
我在保护一段代码时遇到了一个奇怪的问题。我的应用程序是一个托盘应用程序。我在我的类 (ApplicationContext) 中创建了一个 NotifyIcon。我已经为 NotifyIcon 对象分配了一个气球点击处理程序和一个双击处理程序。还有一个上下文菜单,但我没有显示所有代码。只有重要的部分。
public class SysTrayApplicationContext: ApplicationContext
{
private NotifyIcon notifyIcon;
private MainForm afDashBoardForm;
public SysTrayApplicationContext()
{
this.notifyIcon = new NotifyIcon();
this.notifyIcon.BalloonTipClicked += notifyIcon_BalloonTipClicked;
this.notifyIcon.MouseDoubleClick += notifyIcon_MouseDoubleClick;
// ... more code
}
两个处理程序都启动或创建/显示我的表单:
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
openDashboard();
}
}
private void notifyIcon_BalloonTipClicked(object sender, EventArgs e)
{
openDashboard();
}
private void openDashboard()
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
上面的代码有问题。可能不止 1 个。问题:可以显示 2 个仪表板表单,这不是我想要的。如果用户在显示气球消息时双击托盘图标会导致 openDashboard 中出现竞争条件。我可以很容易地重现这一点。因此,我在 openDashboard 代码中的代码周围添加了一个锁,令我惊讶的是,这并没有阻止显示 2 个仪表板表单。我应该无法创建 2 个 MainForms。我哪里错了?
这里是带有锁语句的更新代码:
private void openDashboard()
{
lock (dashBoardFormlocker)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
}
注意:锁对象已添加到类中并在构造函数中初始化。
private object dashBoardFormlocker;
更新:显示更多代码。这就是代码开始的方式:
static void Main()
{
if (SingleInstance.Start())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
XmlConfigurator.Configure();
// For a system tray application we don't want to create
// a form, we instead create a new ApplicationContext. The Run method takes
Application.Run(new SysTrayApplicationContext());
SingleInstance.Stop();
SingleInstance.Dispose();
}
}
}
更新 2:为清晰起见提供更多代码
public partial class MainForm : Form
{
public MainForm()
{
log.Trace("MainForm constructor...");
InitializeComponent();
// ... code not shown
this.label_OSVersion.Text = getOSFriendlyName();
// .. more code
}
private string getOSFriendlyName()
{
try
{
string result = string.Empty;
var mgmtObj = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
result = mgmtObj != null ? mgmtObj.ToString() : string.Empty;
OperatingSystem os = Environment.OSVersion;
String sp = os.ServicePack ?? string.Empty;
return !string.IsNullOrWhiteSpace(result) ? result + sp : "Unknown";
}
catch (System.Exception ex)
{
log.Error("Error trying to get the OS version", ex);
return "Unknown";
}
}
}
【问题讨论】:
-
这里面的多线程在哪里?表单(以及几乎所有标准 Windows UI 组件)共享一个线程,因此
lock不会像您预期的那样工作。 -
我认为气球点击会在不同的线程上运行。
-
为什么会这样?它将
WM_CLICK(或其他)发布到消息传递/窗口系统内的消息队列中。消息泵将其拾取并触发相应的事件。 -
另外,NotifyIcon 是否被视为标准 UI 组件?此应用程序没有主窗体。表单稍后会更新。
-
是的。它仍然适用于相同的基本消息传递/窗口系统。
标签: c# multithreading winforms