0

I have built a VSTO addin for Outlook. I have implemented a timer to "refresh" the pane at specified intervals. When I run it directly from Visual Studio (i.e., from the Debugger), it works fine (the timer callback is called every 30 seconds). But when I publish the solution and install it, the timer is called once (or sometimes twice) and never again. Here is a snippet of my code

using System.Threading;
using ThreadedTimer = System.Threading.Timer;

namespace Outlook2013TodoAddIn  
{
    public partial class AppointmentsControl : UserControl
    {
        public AppointmentsControl()
        {
            InitializeComponent(); 
            InitializeRefhreshTimer();  
            this.textBox1.Text = "0";
        }

        private void InitializeRefhreshTimer()
        {
            TimerCallback timerDelegate =
               new TimerCallback(refreshTimer_Tick);

            TimeSpan refreshIntervalTime = 
                new TimeSpan(0, 0, 0, 30, 1000); //every 30 seconds 
            ThreadedTimer refreshTimer = 
                new ThreadedTimer(timerDelegate, null, TimeSpan.Zero, refreshIntervalTime);
        }

        public void refreshTimer_Tick(Object stateInfo)
        {
            if (InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(delegate
                {
                    this.textBox1.Text = 
                        (Int32.Parse(this.textBox1.Text) + 1).ToString();
                }));
            }
        }       
    }
}   

What am I missing?

kmote
  • 16,095
  • 11
  • 68
  • 91

1 Answers1

2

Ahh, I finally figured it out. I found the following comment buried in ch. 27 of Jeffrey Richter's CLR via C#:

When a Timer object is garbage collected, its finalization code tells the thread pool to cancel the timer so that it no longer goes off. So when using a Timer object, make sure that a variable is keeping the Timer object alive or else your callback method will stop getting called.

My timer was getting gobbled up by the GC! I created a private variable in my AppointmentsControl class and that solved the problem.


What puzzles me is that the official documentation specifically prescribes the approach that I originally used. It's too bad that page doesn't allow user feedback.

kmote
  • 16,095
  • 11
  • 68
  • 91
  • 2
    I would not recommend using a System.Threading.Timer object - it uses secondary threads to run the timer handler. If you access anything from the Outlook Object Model in a secondary thread it is nothing but trouble, so in that case use a System.Windows.Forms.Timer instead. – Eric Legault Jul 12 '16 at 01:50
  • @EricLegault: I started with the Windows.Forms timer, but I needed more flexibility. Specifically, I needed to trigger the timer at the top of each hour, and according to [this post](http://stackoverflow.com/a/4532859/93394), System.Threading.Timer was my only choice. Can you give me an idea of what sort of trouble this choice might cause? – kmote Jul 12 '16 at 13:35
  • I explained the troubles already: secondary threads + Outlook objects = unsupported (and Outlook crashing). If you're not using Outlook objects in these threads than don't worry about it – Eric Legault Jul 12 '16 at 14:49