You are victim of re-entry via the message loop. You are recursing into your timer1_Tick
function indirectly via the message loop. What is happening is that inside SendKeys.SendWait
another message loop is being spun up (not on a different thread) to monitor the whether the messages have been processed. Then on another thread, while messages are being processed in this inner loop, the timer is firing and posting a message to call to your tick function again. Hilarity ensues.
Perhaps a simplified example will help. Run this and observe the output.
public class Program
{
private static Queue<Action> queue = new Queue<Action>();
public static void Main(string[] args)
{
// put three things in the queue.
// In a simple world, they would finish in order.
queue.Enqueue(() => DoWork(1));
queue.Enqueue(() => DoComplicatedWork(2));
queue.Enqueue(() => DoWork(3));
PumpMessages();
}
private static void PumpMessages()
{
while (queue.Count > 0)
{
Action action = queue.Dequeue();
action();
}
}
private static void DoWork(int i)
{
Console.WriteLine("Doing work: {0}", i);
}
private static void DoComplicatedWork(int i)
{
Console.WriteLine("Before doing complicated work: {0}", i);
PumpMessages();
Console.WriteLine("After doing complicated work: {0}", i);
}
}`
You are sort of assuming because there is only one thread pumping messages in the UI that each item that is queued is processed in order. However, this is not true when methods put into the queue can themselves pump messages. In the example, the 3rd operation actually completes before the 2nd one. The DoComplicatedWork
method is analogous to what is happening in SendWait
.
I should answer your second question on how to prevent this. A lock
won't help because they are re-entrant (i.e. the same thread can acquire a lock more than once). The best way is to disable the timer or detach the tick handler while inside the method and re-enable/attach the handler again before returning. You could also just try a simple boolean
flag to indicate whether you are in the method already and return if so.