【问题标题】:System.Windows.Forms.Timer not firingSystem.Windows.Forms.Timer 未触发
【发布时间】:2013-08-22 01:30:00
【问题描述】:

我想使用System.Windows.Forms.Timer 来确保在我正在创建的 Excel 插件的 UI 线程上触发事件。我构造定时器如下:

private System.Windows.Forms.Timer _timer;

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    Debug.WriteLine("ThisAddIn_Startup:" + Thread.CurrentThread.ManagedThreadId);

    _timer = new System.Windows.Forms.Timer();
    _timer.Tick += new EventHandler(TimerEventHandler);
    _timer.Interval = 500;            
}

计时器由我正在使用的库中的 COM 事件触发:

private void OnEvent()
{
    _timer.Start();
}

然后我希望_timer 在它滴答作响时调用以下方法:

public void TimerEventHandler(object sender, EventArgs args)
{
    _timer.Stop();
    Debug.WriteLine("Tick: " + Thread.CurrentThread.ManagedThreadId);                  
}

据我了解,当我在插件线程中创建计时器时,即使它是从另一个线程(在本例中为 COM 事件)启动的,它也应该在创建它的线程上触发,即插件线程。但是,这不会发生。

我已经在我过去写的RTDServer (as outlined by Kenny Kerr) 中实现了这个确切的机制,它按预期工作,但在这种情况下_timer 永远不会滴答作响。

我还阅读了 other SO articles 指出相同的行为,但无法弄清楚我的插件设置有什么不同?

编辑: OnEvent() 方法被触发。

【问题讨论】:

  • 你的事件 onEvent 被触发了吗?
  • 是的,事件触发了。
  • 你有什么异常吗?

标签: c# multithreading com timer excel-addins


【解决方案1】:

我最初打算将此作为评论发布,但结果太长了。

首先,您的线程结构对我来说有点令人困惑,就像您描述的那样。将Debug.WriteLine("OnEvent:" + Thread.CurrentThread.ManagedThreadId) 放入OnEvent 中,让我们知道您从调试输出中看到的所有线程ID。

也就是说,规则是:

  • 您应该在 STA thread 上创建 WinForms 的 Timer 对象,并且应该在启动之前将线程配置为 STA。

  • 这个线程可能是也可能不是主 UI 线程(主窗体创建的地方),但它仍然应该执行一个消息循环(使用Application.Run)以触发计时器事件。还有其他方法可以发送消息,但通常您不能通过 .NET 代码来控制它们。

  • 您应该在创建它的同一线程上处理由 WinForms 的Timer 提供的事件。然后,您可以根据需要将这些事件“转发”到另一个线程上下文(使用 SynchronizationContext SendPost),但我想不出这种复杂性的任何原因。

在我看来,@Maarten 的回答实际上暗示了正确的做法。

【讨论】:

  • 我忘记了 STA 线程的要求。感谢您的意见!
【解决方案2】:

winforms 计时器是一个控件,必须通过将其放置在表单上来使用。您永远不会将它添加到控件集合中,因此我不希望它能够正常工作。 The documentation 说如下

实现一个以用户定义的时间间隔引发事件的计时器。 此计时器已针对在 Windows 窗体应用程序中的使用进行了优化,并且必须在窗口中使用。

因此,我建议您使用System.Timers.Timer class 的实例。这个类可以在任何地方使用。

请注意,您在上面使用的Tick-event 在 System.Timer.Timer 类中由另一个名称调用,即Elapsed-event

【讨论】:

  • 在引用 System.Windows.Forms.Timer 时来自 Kerr 文章:“它在内部创建一个隐藏窗口来处理 WM_TIMER 消息,然后引发 Tick 事件。”见这里:weblogs.asp.net/kennykerr/archive/2008/11/13/Rtd3.aspx
  • 任何对@Maarten 的回答投反对票的人都做错了。这是一个概念上正确的答案,赞成。唯一的小问题,Winform Timer 对象不必添加到表单中,但仍必须在 STA UI 线程上创建。
【解决方案3】:

我还不明白为什么 Forms.Timer 没有按预期运行,但以下优秀文章详细解释了如何将工作编组到 UI 线程:http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多