Advanced Breakpoints and How to Use Them


Setting a breakpoint on a source line in the Visual Studio .NET debugger with Debug or project configuration is simple. Just load the source file, put the cursor on the line you want to stop on, and press the default breakpoint key, F9. Alternatively, you can click in the left margin next to the line. Although the folks coming from Microsoft Visual Basic 6 might not find the setting of margin breakpoints exciting (because Visual Basic has had them for years), C# and C++ developers will see them as a huge improvement. Setting a breakpoint on a source line this way is called setting a location breakpoint. When the code for such a line executes, the debugger will stop at that location. The ease of setting a location breakpoint belies its importance: the location breakpoint on a specific source code line is what separates the modern age of debugging from the debugging dark ages.

In the early days of computing, breakpoints simply didn't exist. Your only "strategy" for finding a bug was to run your program until it crashed and then look for the problem by wading through page after page of hexadecimal core-dump printouts of the state of memory. The only debuggers in the debugging dark ages were trace statements and faith. In the renaissance age of debugging, made possible by the introduction of higher-level languages, developers could set breakpoints but had to debug only at the assembly-language level. The higher-level languages still had no provisions for viewing local variables or seeing a program in source form. As the languages evolved into more sophisticated tools, the debugging modern age began, and developers were able to set a breakpoint on a line of source code and see their variables in a display that interpreted the variables' values into the exact types they specified. This simple location breakpoint is still extremely powerful, and with just it alone, my guess is that you can solve 99.46 percent of your debugging problems.

However wonderful, though, location breakpoints can get tedious very quickly. What would happen if you set the breakpoint on a line inside a for loop that executed from 1 through 10,000, and the bug turned up on the 10,000th iteration? Not only would you wear your index finger down to a nub from pressing the key assigned to the Go command, but you would also spend hours waiting to get to the iteration that produced the bug. Wouldn't it be nice if there were some way to tell the debugger that you want the breakpoint to execute 9,999 times before stopping?

Fortunately, there is a way: welcome to the realm of advanced breakpoints. In essence, advanced breakpoints allow you to program some smarts into breakpoints, letting the debugger handle the menial chores involved in tracking down bugs and minimizing the time and effort you have to spend in the debugger. A couple of conditions you can add with advanced breakpoints are having the breakpoint skip for a certain count and breaking when an expression is true. The advanced breakpoint capabilities have finally moved debuggers solidly into the modern age, allowing developers to do in minutes what used to take hours with simple location breakpoints.

Breakpoint Tips

Before we jump into advanced breakpoints, I just want to quickly mention four things you might not have been aware of when setting breakpoints. The first is that when setting advanced breakpoints, before you set them, it's always best if you start debugging with a single-step command. When you aren't debugging, the debugger uses IntelliSense to set the breakpoints. However, when you start debugging, you also get the actual Program Database (PDB) debugging symbols in addition to the IntelliSense to help set your breakpoints. For all the examples in this and the next two chapters, I started debugging before attempting to set any breakpoints.

One recommendation I have is that you should ensure your Breakpoints window—the view that shows which breakpoints are set—isn't a docked window. Sometimes when you're setting breakpoints, Visual Studio .NET will set them, and you'll wonder why they aren't triggered. By having the Breakpoints window as a full-fledged window, you'll be able to find it among all the other thousands of dockable windows in the Visual Studio .NET IDE. I don't know about you, but running Visual Studio .NET makes me long for a dual 35-inch monitor setup. To view the Breakpoints window, press Ctrl+Alt+B with the default keyboard mappings. Right-click on the Breakpoints window title bar, or tab and uncheck Dockable on the shortcut menu. You'll need to do this for both the normal editing mode as well as the debugging mode. Once you've got the Breakpoint window set as a full-fledged window, click and drag its tab over to the first position so that you can always find it.

The Breakpoints window shows various glyphs that indicate whether the breakpoint was set or not. Table 5-1 shows all the codes you'll see. The Warning glyph, the red dot with a white question mark in it, needs some extra explanation. In your normal debugging, you'll see the Warning glyph when you set a location breakpoint in a source file whose module has not been loaded yet, so the breakpoint is in essence an unresolved breakpoint. For those of you coming from the Microsoft Visual C++ 6 camp, the Additional DLLs dialog box is a thing of the past. Since I recommend that you start debugging before you set advanced breakpoints, if you see the Warning glyph in the Breakpoints window, you have a sign that the breakpoint wasn't set correctly.

Table 5-1: Breakpoint Window Codes

Glyph

State

Meaning

Enabled

Normal and active breakpoint

Disabled

Breakpoint is ignored by the debugger until re-enabled.

Error

Breakpoint could not be set.

Warning

Breakpoint could not be set because location is not loaded. If the debugger has to guess at the breakpoint, it shows the warning glyph.

Mapped

A breakpoint set in ASP code on an HTML page

Although you might never have realized it, you can set breakpoints in the Call Stack window, except when doing SQL debugging. This capability is extremely helpful when trying to get stopped on recursion or deeply nested stacks. All you need to do is highlight the call you want to stop on and either press F9 or right-click on the line and select Insert Breakpoint from the shortcut menu. Even nicer, just as with margin breakpoints, you can right-click on any breakpoint in the Call Stack window to enable, disable, or set the properties of that breakpoint.

Another much underused feature for setting breakpoints is the Run To Cursor option for setting one-shot breakpoints. You can set them in source edit windows by right-clicking on the line and selecting the Run To Cursor option from the menu, which is available both when debugging and editing, and which will start debugging. For the default keyboard layout, pressing Ctrl+F10 will do the same thing. As with breakpoints, right-clicking in the magical Call Stack window pops up the shortcut menu, which also has a Run To Cursor option. If you hit a breakpoint before execution takes place on the Run To Cursor line, the debugger stops on that breakpoint and discards your Run To Cursor one-shot breakpoint.

Finally, in managed code, you can now set subexpression breakpoints. For example, if you have the following expression when debugging and you click in the margin next to the line, the red highlight extends only on the i = 0 , m = 0 or on the initializer's portion of the expression. If you wanted to stop on the iterator's subexpression (in which the increment and decrement take place), place the cursor anywhere in the i++ , m-- portion of the statement and press F9. In the following statement, you can have up to three breakpoints on the line. You can differentiate them in the Breakpoints window because each will indicate the line and character position. There will be only a single red dot in the margin indicating the breakpoints. To clear all breakpoints at once, click the red dot in the left margin.

for ( i = 0 , m = 0 ; i < 10 ; i++ , m-- ) { }

Quickly Breaking on Any Function

The starting point for any advanced breakpoint is the Breakpoint dialog box, which is accessible by pressing Ctrl+B in the default keyboard mapping. This dialog box serves double duty as the New Breakpoint dialog box as well as the Breakpoint Properties dialog box. In many ways, the Breakpoint dialog box is simply a front end to the IntelliSense system. IntelliSense is extremely helpful for writing your code, but it's also used to help set breakpoints. There's an option for turning off IntelliSense to help verify breakpoints in the Options dialog box, Debugging folder, General property page, which can speed up mixed mode debugging, but I would highly recommend that you always leave the option Use IntelliSense To Verify Breakpoints checked on your systems. Also, you'll get IntelliSense breakpoints only when you have a project with source code open.

By using IntelliSense, you get a very powerful breakpoint-setting feature that can save you a tremendous amount of time. In the midst of my debugging battles, if I know the name of the class and method I want to break on, I can type it directly into the Breakpoint dialog box, Function tab's Function edit control. I've looked over the shoulder of countless developers who know the name of the method but spend 20 minutes wandering all over the project opening files just so they can move the cursor to the line and press F9. Setting breakpoints like this has some limitations, but they aren't too onerous. The first is that the name is case sensitive if the language is case sensitive, as you would expect. (This is where Visual Basic .NET is especially nice!) Second, in native C++, it's sometimes not possible to set the breakpoint because the actual method name is hidden by a define. Finally, the language selected in the Language drop-down list in the Breakpoint dialog box must be the correct language for the code.

Numerous things can happen when you try to set a breakpoint on a function using the Breakpoint dialog box. I want to go through the possible outcomes you'll see and explain how to work around any problems you might encounter. What you might want to do so that you can see the outcomes yourself is open up one of your projects or a project with this book's sample files and try to set a few breakpoints with the Breakpoint dialog box as I go through this discussion.

The first case of quickly setting a breakpoint that I'll discuss is when you want to set the breakpoint on a class and method that exists. For example, I have a Visual Basic .NET class named MyThreadClass with a method named ThreadFunc. When I pop up the Breakpoint dialog box with Ctrl+B, all I have to type in is mythreadclass.threadfunc (remember Visual Basic .NET is a case-insensitive language) and click OK. For Visual Basic .NET, J#, and C#, you separate the class and the method name with a period. For native C++, you separate the class and the method name with the language-specific double colons (::). Interestingly, for managed C++, you have to prefix the expression with MC:: to get the expression parse correctly. Assuming the same class and method name from the Visual Basic .NET example were in managed C++, you'd have to enter MC::MyThreadClass::ThreadFunc. Figure 5-1 shows the filled-out Breakpoint dialog box using the Visual Basic .NET example. If you aren't currently debugging when you set the breakpoint, the breakpoint dot appears in the margin, but the red highlight doesn't appear on the Public Sub ThreadFunc because the breakpoint still has to be resolved. Once you start debugging, Public Sub ThreadFunc is highlighted in red. If you're debugging native C++, the instruction isn't highlighted. If you're currently debugging, the breakpoint will be fully resolved by showing a filled-in red dot in the Breakpoints window and you'll see that the Public Sub ThreadFunc is highlighted in red. (In C# or native C++, the breakpoint appears inside the function, but it's still the first instruction after the function prolog. Also, just a breakpoint dot appears in the margin.)

click to expand
Figure 5-1: Breakpoint dialog box about to set a quick breakpoint on a function

If the class and method name you specify in the Breakpoint dialog is incorrect, you'll see a message box that says this: "IntelliSense could not find the specified location. Do you still want to set the breakpoint?" If you go ahead and set the breakpoint on the nonexistent method and you're doing managed code, you can be pretty certain that when you're debugging, the breakpoint will always have a question mark glyph next to it in the Breakpoints window because the breakpoint can't be resolved. As you'll see in Chapter 7, when we talk about native code debugging, you still have a chance to set the breakpoint correctly.

Because setting a breakpoint by specifying the class and method works well, I had to experiment with attempting to set a breakpoint in the Breakpoint dialog box by just specifying the method name. For C#, J#, Visual Basic .NET, and native C++, if the method name is unique in the application, the Breakpoint dialog box simply sets the breakpoint as if you typed in the complete class and method. Unfortunately, managed C++ doesn't seem to support setting breakpoints with just the method name.

Since setting a breakpoint with just the method name works quite well and will save you time debugging, you might be wondering what would happen if you had a large project and you wanted to set a breakpoint on a common method or an overloaded method. For example, suppose you have the WDBG MFC project from Chapter 4 open and you want to set a breakpoint on the OnOK method, which is the default method for handling the OK button click. If you enter OnOK in the Breakpoint dialog box and click OK, something wonderfully interesting happens and is shown in Figure 5-2.

click to expand
Figure 5-2: The Choose Breakpoints dialog box

What you see in Figure 5-2 is the IntelliSense listing from all classes in the WDBG project that have OnOK as a method. I don't know about you, but I think this is an outstanding feature—especially because you can see that clicking the All button allows you to set breakpoints on all those methods in one fell swoop, and it works for all languages supported by Visual Studio .NET except managed C++! The Choose Breakpoints dialog box also displays for overloaded methods in the same class. I don't know how many times I've gone to set a breakpoint and the Choose Breakpoints dialog box reminded me that I should consider stopping on other methods as well.

Another of my favorite things to do in C++ code is to type just the class name in the Breakpoint dialog box. The wonderful Choose Breakpoints dialog box pops up and offers every method in the class! Sadly, this killer feature works only on C++ code (and amazingly in managed C++ as well) and not on C#, J#, or Visual Basic .NET. But for C++, this incredible feature is a boon for testing. If you want to ensure your test cases are hitting every method in a class, simply type the class name in the Breakpoint dialog box and click All in the resulting Choose Breakpoints dialog box. This instantly sets breakpoints on every method so that you can start seeing whether your test cases are calling every method. This feature is also great for looking for dead code because if you set all the breakpoints and clear them as you hit them, any remaining breakpoints are code that's never executed.

At the time I wrote this book, I was using Visual Studio .NET 2003 RC2. When I was trying to figure out if C# and J# could set class breakpoints like native C++, I ran into a huge bug that will crash Visual Studio .NET. I hope this bug will be fixed in the final release; but if it's not, I want to make sure you know about it. For C# or J# projects, if you type in the class name followed by a period in the Breakpoint dialog box, for example, Form1. and click the OK button, you'll get a Visual C++ run time error message box telling you the application has attempted to terminate in an unusual way. When you click the OK button, the IDE will simply disappear. Attempting the same type of breakpoint in a Visual Basic .NET application does not close the IDE. Although this is a very nasty bug, at least only a few of you should run into it.

I'm simply amazed that the ability to select from multiple methods in your application when setting a breakpoint in the debugger isn't pointed out in big, bold type as a killer feature of Visual Studio .NET. In fact, there's only a passing reference to the Choose Breakpoints dialog box in the MSDN documentation. However, I'm very happy to toot the debugger team's horn for them.

As you can probably guess by this point, you don't have to go through the Choose Breakpoints dialog box if you know the class and method because you can type them directly in. Of course, you have to use the appropriate class and method separator based on the language. If you want to be extremely specific, you can even specify the parameter types to the overloaded method for Visual Basic .NET and C++, keeping in mind the particular syntactic requirements of the languages. Interestingly, C# and J# don't look at parameters and always pop up the Choose Breakpoints dialog box, which is probably a bug.

If you're debugging when you want to set a quick breakpoint, things get a little more interesting, especially in managed code. For native code, I'll save the discussion for Chapter 7 because when you're debugging, there's quite a bit more you have to manually check when setting breakpoints. For managed code, I noticed some very interesting power in the Breakpoint dialog box. During a brain cramp one day while debugging, I typed in the name of a class and method in the Microsoft .NET Framework class library instead of the class and method from my project, and a whole bunch of really weird-looking breakpoints popped up in the Breakpoints window. Let's say I have a console-managed application that calls Console.WriteLine, and while debugging, you type Console.WriteLine into the Breakpoint dialog box. You'll get the usual message about IntelliSense not knowing what to do. If you click Yes and go to your Breakpoints window, you'll see something that looks like Figure 5-3. (You might have to expand the top tree node.)

click to expand
Figure 5-3: Child breakpoints in the Breakpoints window

What you're looking at in Figure 5-3 are child breakpoints. Basically, the Visual Studio .NET documentation says that they exist and that's it. For example, the documentation says child breakpoints occur when you set breakpoints on overloaded functions, but the Breakpoints window always shows them as top-level breakpoints. You can see child breakpoints when you're debugging multiple executables and both programs load the same control into their AppDomain/address spaces, and you set breakpoints in the same spot in that control in both programs. What's wild is that the Breakpoints window in Figure 5-3 is showing you a single program that's currently running in which I set a breakpoint on Console.WriteLine.

If you right-click on a child breakpoint while debugging and select Go To Disassembly, the Disassembly window displays the disassembly code. However, you'll get a clue as to what's happening if you right-click on a child breakpoint, select Properties from the shortcut menu, and in the resulting Breakpoint dialog box, click on the Address tab, shown in Figure 5-4. You'll see that the debugger reports that the breakpoint is set on System.Console.WriteLine at the very start of the function.

click to expand
Figure 5-4: Breakpoint on any call to Console.WriteLine

If that's not clear enough, you can always execute your program and notice that you stop deep down in the x86 assembly language soup. If you pull up the Call Stack window, you'll see that you're stopped inside a call to Console.WriteLine and you'll even see the parameter(s) passed. The beauty of this undocumented means of handling a breakpoint is that you'll always be able to get your applications stopped at a known point of execution.

Although I made only a single call to Console.WriteLine in my program, the Breakpoints window shows 19 child breakpoints, as shown in Figure 5-3. Based on some trial and error, I discovered that the child breakpoints count is related to the number of overloaded methods. In other words, setting breakpoints by typing in the .NET Framework class and method name, or typing where you don't have source code, will set a breakpoint on all overloaded methods. In case you check the documentation and see that Console.WriteLine only has 18 overloaded methods, let me tell you that if you look at the System.Console class with ILDASM, you'll see that there are really 19 overloaded methods.

The ability to stop on a method that's called anywhere in my AppDomain is amazingly cool. I've learned quite a bit about how the .NET Framework class library fits together by choosing to stop at specific .NET Framework class library methods. Although I've been using a static method as an example, this technique works perfectly well on instance methods as well as properties as long as those methods or properties are called in your AppDomain. Keep in mind that to set breakpoints on a property, you'll need to prefix the property with get_ or set_, depending on what you want to break on. For example, to set a breakpoint on the Console.In property, you'd specify Console.get_In. Also, the language selected in the Breakpoint dialog box is still important. When setting these AppDomain-wide breakpoints in places where you don't have source code, I like to use Basic so that if I mess up the case, the breakpoint is still set.

There are two issues you need to be aware of when setting these AppDomain-wide breakpoints. The first is that breakpoints set this way aren't saved across executions. Consider the example in which we added a breakpoint while debugging on Console.WriteLine. The Breakpoints window will show "Console.WriteLine" when you re-execute the program, but the breakpoint changes to a question mark glyph and the breakpoint changes to unresolved and will never be set. Second, you can set these AppDomain-wide breakpoints only when the instruction pointer is sitting in code in which you have a PDB file. If you try to set the breakpoint in the Disassembly window with no source code available, the breakpoint will be set only as unresolved and never activated.

Although you might think I've beaten the topic of setting quick location breakpoints to death, there's still one completely non-obvious but extremely powerful place to set location breakpoints. Figure 5-5 shows the Find combo box on the toolbar. If you type the name of the class and method you want to break on in that combo box and press F9, a breakpoint on that method is set if that method exists. If you specify an overloaded method, the system will automatically set breakpoints on all the overloaded methods. The Find combo box is a little more discriminating in that if it can't set the breakpoint, it won't. Additionally, like the Breakpoint dialog box, if you're working on a C++ project, specifying the class name in the Find combo box and pressing F9 will set a breakpoint on each method in that class.

click to expand
Figure 5-5: The Find combo box

That little Find combo box has two other hidden secrets as well. For the default keyboard layout, if you type in the name of a project file or an include file in the INCLUDE environment variable and press Ctrl+Shift+G, the Find combo box opens the file ready for editing. Finally, if you like the Command window in Visual Studio .NET, try this: in the Find combo box, enter the right arrow key symbol (>), and see the window turn into a mini Command window with its own IntelliSense. With all this undocumented magic in the Find combo box, I often wonder if I could type in "Fix my bugs!" and with a magic keystroke have it do just that.

Location Breakpoint Modifiers

Now that you know how to set location breakpoints anywhere with aplomb, I can turn to some of the scenarios discussed in the opening section on breakpoints. The whole idea is to add some real smarts to breakpoints so that you can use the debugger even more efficiently. The vehicles for these smarts are hit counts and conditional expressions.

Hit Counts

The simplest modifier applicable to location breakpoints is a hit count, also sometimes referred to as a skip count. A hit count tells the debugger that it should put the breakpoint in but not stop on it until the line of code executes a specific number of times. With this modifier, breaking inside loops at the appropriate time is trivial.

Adding a hit count to a location breakpoint is easy. First, set a regular location breakpoint either on a line or a subexpression of the line. For managed code, right-click on the red area of the line for the location breakpoint, and select Breakpoint Properties from the shortcut menu. For native code, right-click on the red dot in the left margin. Alternatively, you could also select the breakpoint in the Breakpoints window and click the Properties button, or right-click it in the window and select Properties. No matter how you do it, you'll end up in the Breakpoint dialog box, where you click the Hit Count button.

In the resulting Breakpoint Hit Count dialog box, you'll see that Microsoft improved the hit-count options from previous debuggers. In the When The Breakpoint Is Hit drop-down list, you can choose how you want the hit count calculated four different ways, as shown in Table 5-2. After choosing the evaluation you want, type the hit-count number in the edit box next to the drop-down list.

Table 5-2: Hit.Count Evaluations

Hit-Count Evaluation

Description

Break always

Stop every time this location is executed.

Break when the hit count is equal to

Only stop when the exact number of executions of this location has occurred. Note that the count is a 1-based count.

Break when the hit count is a multiple of

Break every x number of executions.

Break when the hit count is greater than or equal to

Skip all executions of this location until the hit count is reached and break every execution thereafter. This was the only hit count in previous editions of Microsoft debuggers.

What makes hit counts so useful is that when you're stopped in the debugger, the debugger tells you how many times the breakpoint has executed. If you have a loop that's crashing or corrupting data but you don't know which iteration is causing the problem, add a location breakpoint to a line in the loop and add a hit-count modifier that is larger than the total number of loop iterations. When your program crashes or the corruption occurs, bring up the Breakpoints window and, in the Hit Count column for that breakpoint, you'll see the number times that loop executed in parentheses. The Breakpoints window shown in Figure 5-6 displays the remaining hit count after a data corruption. For native code, keep in mind that the remaining count works only when your program is running at full speed. Single-stepping over a breakpoint doesn't update the hit count.

click to expand
Figure 5-6: An example of remaining hit count breakpoint expressions

A new feature of hit counts is that you can reset the count back to zero at any time. Jump back into the Breakpoint dialog box, click the Hit Count button, and click the Reset Hit Count button in the Breakpoint Hit Count dialog box. One additional nice feature is that you can change the hit-count evaluation at any time or the hit-count number without changing the current hit count.

Conditional Expressions

The second modifier for location breakpoints—and the one that if used correctly saves you more time than any other type of breakpoint—is a conditional expression. A location breakpoint that has a conditional expression triggers only if its expression evaluates to true or changes from the last time it was evaluated. A conditional expression is a powerful weapon for gaining control exactly when you need it. The debugger can handle just about any expression you throw at it. To add a conditional expression to your breakpoint, open the Breakpoint dialog box for a location breakpoint and click the Condition button, which brings up the Breakpoint Condition dialog box.

In the Condition edit box, enter the condition you want to check and click OK. Both managed and native code have sufficiently varying support for conditions, each with their own set of "gotchas" that I'll have to discuss in their respective upcoming chapters. In a nutshell, the differences are that in managed code, you can call methods and functions from the conditional expressions (be very, very careful!) and have no support for pseudo registers/values (the special codes that begin with the @ or $ symbol). For native code, you can't call functions from your conditional expressions, but you do have access to the pseudo registers/values.

However, both environments support general expressions that you can think of like this: "What's in the parentheses of an if statement that I'd enter on the breakpoint line?" You have full access to local and global variables because they are evaluated in the context of the currently executing scope. The conditional expression breakpoint modifier is extremely powerful because it allows you to directly test the specific hypothesis that you're out to prove or disprove. The important point to remember is that the expression syntax must follow the same rules as the language in which the breakpoint is set, so remember to mind your And's and ||'s as appropriate.

The default condition handling breaks into the debugger if the conditional expression you enter evaluates to true. However, if you want to break when the condition changes value, you can change the option button selection from Is True to Has Changed. One thing that can be confusing, but makes sense after some thought, is that the expression isn't evaluated when you enter the condition. The first time it's evaluated is when the associated location breakpoint is hit. Since the condition has never been evaluated, the debugger sees no value stored in the internal expression result field, so it saves off the value and continues execution. Thus it can possibly take two executions of that location before the debugger stops.

What's extra nice about the Has Changed evaluation is that the condition you enter doesn't have to be an actual condition. You can also enter a variable that is accessible from the scope of the location breakpoint, and the debugger evaluates and stores the result of that variable so that you can stop when it changes. Make sure to read the breakpoint discussions in both Chapter 6 and Chapter 7 to learn about the managed and native code twists on conditional breakpoint modifiers. Additionally, native debugging offers other types of breakpoints to make debugging easier. I do want to answer the question on the tip of your tongue (thus ruining the suspense): managed debugging does not support global breakpoints.

Multiple Breakpoints on a Single Line

One of the most common questions I'm asked when it comes to setting breakpoints is about setting multiple breakpoints on a single line. Prior to Visual Studio .NET, it wasn't possible, but now you can. However, you won't use multiple breakpoints on a line every day because they're not that easy to set. You'll also be using only conditional expression location breakpoint modifiers on each of the breakpoints. As you can imagine, setting two regular stop-always location breakpoints on a line isn't going to buy you very much!

After setting the first breakpoint using normal methods, the trick to setting the second one is to use the Breakpoint dialog box to do the magic. The easiest way to show you how to set multiple breakpoints is through an example. The following code snippet shows the line number in comments before each line.

/* Source file: CSharpBPExamples.cs. */ /*84*/    for ( i = 0 , m = 0 ; i < 10 ; i++ , m-- ) /*85*/    { /*86*/        Console.WriteLine ( "i = {0} m = {0}" , i , m ) ; /*87*/    } 

I set the first breakpoint on line 86 by right-clicking in the margin and setting the conditional expression to i==3. To set the second breakpoint, shift to the Breakpoints window so that you can see the line number for the original breakpoint and bring up the Breakpoint dialog box by pressing Ctrl+B. Click the File tab in the Breakpoint dialog box because that's where you need to set the breakpoint. Type the file name into the File edit box, enter the line number in the Line edit box, and leave the Character edit box set to 1. Using this example, I use CSharpBPExamples.CS as the file and 86 for the line. Finally, click the Condition button and enter the condition you want for the second breakpoint. In my case, I'll enter m==-1.

The Breakpoints window shows two breakpoints set on the same line, and the icon glyph on the left-hand side shows full red circles, indicating that they are both active. When you start debugging, you'll see that the program stops on both breakpoints when the appropriate conditions are met. To check which condition is causing the breakpoint, look at the Breakpoints window to see which one is bold. Interestingly, right-clicking on the breakpoint in the source window and selecting Breakpoint Properties from the shortcut menu always displays the properties of the first breakpoint set for the line. Additionally, if you click on the margin dot, both breakpoints are cleared. While the example I showed you here was a little contrived—I would have combined both conditions with an or (||) operator—multiple breakpoints on a line come in very handy when you need to look at two different complicated expressions.




Debugging Applications for Microsoft. NET and Microsoft Windows
Debugging Applications for MicrosoftВ® .NET and Microsoft WindowsВ® (Pro-Developer)
ISBN: 0735615365
EAN: 2147483647
Year: 2003
Pages: 177
Authors: John Robbins

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net