If you ever try to debug a multi-threaded Windows Forms application, today's blog is for you. A customer asked me about this issue, and since I have also run into this problem, I decided to talk about it.
If you already know what func eval is, or don't care, skip to the bottom.
For the rest of us, I wanted to give some background. Func Eval is short for 'function evaluation'. The idea is this: instead of just reading data out of the debuggee process to fill out the watch window, the debugger actually causes code to run inside the debuggee process. It does this by hijacking the stopped thread, and running something entirely different on that thread. Func eval has been a feature of the Visual Studio debugger for a while now.
Usually, func eval is a great feature. It makes it so easy to dig into variables that hold framework classes. You don't need to write a bunch of code to figure out what all the properties are returning, you can just look at all of them under the debugger.
The problem with func eval is that it is a double-edged sword: if your ToString implementation or your property-getter implementation does something 'bad', it can make it pretty much impossible to debug, because your code will act differently under the debugger.
To avoid problems with func eval, don't write complex property getters/ToString functions. If you need to do something complex, put it in a 'GetFoo' instead making Foo a property. If you decide to make your property complicated, mark it as never browsable:
The biggest no-no with func eval is to try and interact with other threads. When a debugger runs the function, it only allows the thread running to function to execute. All the other threads are going to be suspended. It needs to do this to preserve where that thread is stopped. This is a problem if the evaluated function tries to acquire a lock, or run code on another thread.
If you have ever tried to look at a form object on a thread other then the thread that created it, you have probably run into this problem. The basic problem is that your form inherits from System.Windows.Forms.Form, and to evaluate ToString and most of its properties, System.Windows.Forms.Form tries to run code on the main thread. This causes the debugger to hang for several seconds when evaluating your form. The debugger attempts to evaluate at many times, so the most likely symptom is that the debugger appear hung after stopping at a breakpoint. I can offer a couple of work arounds:
Work around #1 -- Stop the debugger from calling ToString.
This doesn't solve the problem, but it means that you have to expand the form class, which greatly reduces the problem.
To do this you can override ToString -or- set a DebuggerDisplay attribute (see below) -or- uncheck 'Call ToString() on objects in variables windows (C# only)'.
Example of DebuggerDisplay attribute:
public partial class Form1 : Form
Work around #2 -- Disable func eval entirely
Uncheck 'Enable property evaluation and other implicit function calls'. This solves the problem, but it may make debugging other things more difficult.
Work around #3 -- Avoid the problem
Depending on your architecture, it might make sense to not pass a reference to your form object to other threads. If this makes sense for you, then you can avoid the problem entirely.
Lastly, I would like to make a plug for feedback on func-eval issues. If you run into this problem, or other func eval-induced problems, drop me a line. We know that these problems exist, but we would love to know how frequent they are.