|
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.
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.
Figure 6-2: Desperately seeking 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, MCEE_CS.DAT for C#, VJSEE.DAT for J#, and MCEE_MC.DAT for Managed Extensions for C++, are in your <Visual Studio .NET installation directory>\COMMON7\PACKAGES\ DEBUGGER directory. As you peruse the three files, you'll notice that the C# version has quite a few more expansions in it than the Managed Extensions for C++ version. (The J# version has more Java-specific expansions.) If you're doing Managed Extensions for C++ development, you might want to copy the rules in MCEE_CS.DAT to MCEE_MC.DAT so that you'll get the additional rules.
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 MCEE_CS.DAT, you'll notice that most of the types are specified with complete namespace delimiters. Since the tiny bit of documentation at the top of the MCEE_CS.DAT file doesn't indicate what the minimum level is, I always use the complete intrinsic type name to avoid any problems.
The MCEE_CS.DAT file documentation is shown in Bakus-Naur (BNR) form, which isn't always the easiest thing to read. To simplify the descriptions, I thought I'd show a cleaner and more common-sense formation: "<type>=[text]<member/method,format>..." Table 6-1 explains the meaning of each field. The angle brackets are mandatory on both the type element and around the member/method,format sections. Also note that the autoexpand rule can show multiple member values for a type.
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 MCEE_CS.DAT file, in which I added common autoexpands I thought Microsoft forgot, such as the StringBuilder class. You can use it as a base for starting your own expansion files.
Figure 6-3: The joy of autoexpands
Figure 6-4: The ecstasy of autoexpands
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.
|