Debugging Applications for MicrosoftВ® .NET and Microsoft WindowsВ® (Pro-Developer)
|
As I mentioned in the Chapter 5, the Watch window is one of the most exciting features in Visual Studio .NET debugging. Since I already discussed calling methods and setting properties, there's only one thing left to talk about concerning managed code in the Watch window and that's how to set up the Watch window to help you debug even faster.
Expanding Your Own Types Automatically
If you've done any amount of managed code debugging, you've probably seen that certain types seem to display more information in the Watch window than other types. For example, an exception type such as System.ArgumentException always displays the message associated with the exception in the Watch window Value column, whereas a type such as System.Threading.Thread displays only the type. In the former case, you can quickly see the important information about the class at a glance whereas in the latter you need to expand the class and hunt through a huge list of member fields to find more specific information such as the name. To me, it's not very useful when you have an entry in the Watch window, and the Value column and the Type column both show the same value. Figure 6-2 shows an example of a type that desperately needs some autoexpand help.
By adding your own types to the Watch window as well as key .NET Framework class library classes not already added, you can trim a great deal of time off debugging because the key information you need is always there at a glance. The best part of this expansion is that it also applies to the data tips that pop up when you hover your mouse over a variable in the Source windows as well as when showing parameters in the Call Stack window.
Although autoexpands is extremely cool, it's not perfect. The most serious shortcoming is that autoexpands work only for C#, J#, and Managed Extensions for C++. Like the unfortunate absence of XML documentation comments in Visual Basic .NET, the lack of autoexpands is quite sad because it makes debugging Visual Basic .NET code harder than it should be. Additionally, the rules file is read-only when you start Visual Studio .NET, so when adding autoexpand rules you'll be doing a lot of startup and shut down as you test your rules. Interestingly, with native code autoexpands, which I'll discuss in Chapter 7, the rules file is read each time you start debugging. A minor issue is that the file containing the expansion rules isn't stored with your project but rather must reside in the directory where Visual Studio .NET is installed. This means that when you put the team copy of the autoexpands file in your version control, you'll have to make sure you set the working directory to the hard-coded location, which is described in the next paragraph, so that the debugger can find it.
The first step to autoexpand bliss is finding the appropriate rules files. The files,
The comments, delimited by semicolon characters, at the tops of both files describe the gyrations necessary for expanding C# and Managed Extensions for C++ types. Although the documentation implies through omission that only the actual field values can be used in the autoexpand rules, you can call property get accessors as well as methods in the rules. Obviously, you'll want to ensure that the method or property you access returns something that makes sense to return, such as a string or numeric value. Returning classes or complex value types show only the type, not any useful information. As usual, here's where I need to warn you about making sure you use only properties and methods that won't cause side effects.
As an example, I'll add the C# autoexpand for System.Threading.Thread class types so that you'll see the thread name in the Value column if the name is set. Looking at the examples Microsoft provides in
The
Field | Description |
---|---|
type | The type name, which should be the complete type. |
text | Any literal text. This field is generally the member name or a shorthand version of it. |
member/method | The actual data member to display or method to call. This field can be an expression when you want to show a calculated value. The casting operators also work. |
format | Additional format specifiers for the member variables to force numeric base display. Permissible values here are d (decimal), o (octal), and h (hexadecimal). |
In the System.Threading.Thread autoexpand, I'm just interested in the Name property, so I place this value in the file:
<System.Threading.Thread>=Name=<Name>
Figure 6-3 shows the result of adding the expansion in the Watch window. The icing on the cake is shown in Figure 6-4, in which you can see that the tool tip shows the autoexpand as well. Included with this book's sample files is my
Sometimes, the best way to learn how something fits together is to see in a hierarchical manner who is calling what. Although the Visual Studio .NET debugger doesn't offer any such options (without someone possibly writing an add-in), CORDBG.EXE, the debugger that comes with the .NET Framework, does. (For a solution to viewing the flow without a debugger, see Chapter 11.) If you want to experience all the joys of PC debugging circa 1985, CORDBG.EXE's console-based debugging is all for you.
CORDBG.EXE has the wt command, which mimics WinDBG's Watch and Trace commands. In addition to showing the complete hierarchy of calls, wt also shows how many native instructions executed as a result of each method called. To best utilize the wt command, set a breakpoint on the first line of the method you want to start tracing from. The wt command will execute until it hits the return address of the starting scope.
The following shows the output of the wt command for an application (WT.CS, available with this book's sample files) that calls three empty functions. As you can imagine, calling items in the .NET Framework class library will generate volumes of output.
(cordbg) wt 1 App::Main 3 App::Foo 3 App::Bar 5 App::Baz 3 App::Bar 3 App::Foo 3 App::Main 21 instructions total
If you're just starting out with CORDBG.EXE, the most important command is ? because that will get you the command help. A ? followed by a command will show you more information about that command as well as provide example usages.
|