3

我想使用 aSystem.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我在过去写的一篇文章中实现了这个确切的机制(如 Kenny Kerr 所述),它按预期工作,但_timer在这种情况下永远不会滴答作响。

我还阅读了其他指向相同行为的 SO 文章,但无法弄清楚我的插件设置有什么不同?

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

4

3 回答 3

6

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

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

也就是说,规则是:

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

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

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

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

于 2013-08-21T04:29:58.947 回答
5

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

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

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

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

于 2013-08-20T05:48:14.590 回答
-1

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

于 2013-08-20T06:49:28.403 回答