There are just some times when it is super handy to see when function X on the base class has been called with a certain value. The best example I have of this is debugging layout. 99.99% of all sizing/location logic routes through one single method in System.Windows.Forms.Control – SetBoundsCore.
There is the super obvious quick fix for this: override the method and see when it gets called. But if you’ve got a lot of controls, this route can be tedious.
The following steps work for v1.1 (Everett) (tested from my home machine, nothing up my sleeves). I have not tested the same steps in Whidbey, although I expect them to be roughly the same (except you might have to turn off Tools->Options->Debugging->Just My Code). You may also want to right click on the call stack window and select “Show External Code”.
Read up (optional)
There’s lots of great books out there on debugging .net, this one in particular rocks because it covers everything from soup to nuts: Debugging Applications for Microsoft .NET and Microsoft Windows, John Robbins
Get yourself hooked up with the Microsoft symbol server
What’s a symbol? Without getting too technical, a symbol file provides debugging information for a particular dll or executable. It allows you to take what is a gobbledygook of memory and figure out information about function names, local variables etc. These files are generally called PDB files (although older formats exist).
Microsoft actually gives out this debugging information using a technology called a symbol server. It’s just like it sounds, when your debugger is hooked up to the symbol server, as it needs debugging information for a particular DLL, it pulls the matching PDB down from Microsoft’s server.
The symbol server stuff is put out by the same folks who put together the Debugging Tools for Windows, although you do not need to use their debugger (WinDbg) in order to get the symbols.
In order to hook up to the Microsoft Symbol Server in Visual Studio.NET, follow these instructions.
The don’t-leave-your-seat-to-find-the-Visual Studio-CD-instructions:
- install windbg (Debugging Tools For Windows)
- find the directory where it installed
- copy symsrv.dll from this directory to the same directory where devenv.exe is installed, typically:
- (C:\Program Files\Debugging Tools for Windows\symsrv.dll)
- (C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\symsrv.dll)
- close/reopen visual studio
For each solution:
- open your solution you want to debug
- right click on solution in solution explorer, select properties
- Select common properties->debug symbol files
- Add the following to my symbol search path
Verify you’re hooked up correctly
The way to see if you’re hooked up correctly with symbols is to run your application, then break in the debugger by pushing the pause button. Open up Debug->Windows->Modules and look at the symbol column, if you see “Symbols Loaded”, that means you’re good as gold. In Whidbey, if you don’t see that your symbol is loaded correctly, you can right click on the dll and check the symbol load path.
Besides all the folders that you add to your project for places to look for PDB files, there is another known convention (_NT_SYMBOL_PATH) which windows/vs honors for symbol lookup.
Putting a breakpoint on code where you don’t have the source
Walk with me through this one.
- Create a new windows forms application.
- Open up the solution properties and add the symbol server path as described above.
- Drop on a button and set the Location property to 64, 16
- Drop on a panel and set the Location property to 48, 80
- Add a new breakpoint by pushing the “new” button
- In the “Function:” put System.Windows.Forms.Control.SetBoundsCore
- Make sure the Language is set to C#.
- F5 the application
You should now see it stop whenever the SetBoundsCore has been called, in this case once for the panel and once for the button.
Using Debug->Windows->Call Stack, you can walk back up the stack to see why each call was made. In this case, you’ll see a few frames up a call from Form1.InitializeComponent() to Control.set_Location.
Narrowing down to just see when the panel’s size is set
But if you’ve got a lot of controls, the above breakpoint will be hit so often it’s practically useless. We’ve got to narrow down when it gets called. This is where the rockin feature of conditional breakpoints comes in.
Select the breakpoint we made above and right click, select properties
- Press the condition button
- Add the condition: x == 48
F5 the application
Now the breakpoint only gets hit once.
You can add other conditions like this.Name == … and this.Text ==, however you may have to re-arrange the code so that these properties are set before the location is set, otherwise you wont be able to trap them.
In Whidbey (VS 2005), it’s even more awesome what you can do in a conditional breakpoint, you can say things like “this is Panel”.
WARNING: this feature is so powerful, you can also shoot yourself in the foot. If you accidentally type one equals sign “=” instead of both “==”, you can accidentally start assigning values and changing the behavior of your program. Just try the x=48, you’ll notice the button scoots over to line up with the panel.
Inspecting properties and evaluating methods
Everyone knows about the watch window to inspect properties, but you may not realize you can change the value at any time by just typing into the watch window’s value column.
There’s also a little known window called the Immediate Window which can be a godsend.
Using this window you can not only evaluate properties, but you can set them typing in real lines of code AND you can evaluate some methods. As with conditional breakpoints, this feature has also become more powerful in VS 2005.
I’m no expert on debugging tools, but I thought I’d share with you some tips and tricks I’ve picked up over the years. The good news is it’s so powerful in Everett (VS 2003). And if you're excited about that, it’s a lot MORE powerful in Whidbey (VS 2005). Debugging just keeps getting better.