By: Cary Jensen
Abstract: Non-breaking breakpoints are breakpoints that do not load Delphi's integrated debugger when they trigger. These special breakpoints are more powerful than they sound, and this article explains why.
At first you might wonder "What is the value of a breakpoint if it does
not load the integrated debugger?". Let me tell you, the value can be very
big indeed. Non-breaking breakpoints permit you to perform a variety of actions
without interrupting the execution of your application.
For example, a non-breaking breakpoint can write a message to the error log,
a Windows-based mechanism for logging information while your application is
running. Non-breaking breakpoints can also execute functions, evaluate expressions, instruct the debugger to
handle or ignore exceptions, and enable or disable entire
groups of breakpoints.
But before we go further, let me clarify the terminology. A non-breaking
breakpoint, so long as it is enabled, does triggers. Specifically, there is a
difference between a disabled breakpoint, which does not trigger, and a
non-breaking breakpoint, which does trigger, so long as its Pass count and
Condition properties permit it to trigger. The only difference between a
non-breaking breakpoint and a breaking breakpoint (the default) is that a
breaking breakpoint loads the integrated debugger when it triggers (given that
the code is running in the integrated development environment (IDE) and the
integrated debugger is enabled). By comparison, a non-breaking breakpoint, when
it triggers, does not load the integrated debugger.
To create a non-breaking breakpoint, begin by first creating a source
breakpoint, an address breakpoint, or a data breakpoint. Next, display the Breakpoint Properties dialog box.
If your breakpoint is a source breakpoint (one associated with an executable
line in your code), you display the Breakpoint Properties dialog box by
right-clicking the breakpoint symbol in the left gutter of the editor window and
select Breakpoint properties. If your breakpoint is an address breakpoint (one
associated with an instruction at a particular memory address being executed),
right-click the breakpoint symbol in the left gutter of the disassembled pane in the
CPU window and select Breakpoint properties. If your breakpoint is a data
breakpoint (one associated with the contents of a particular memory address
being changed), view the Breakpoints dialog box by selecting View | Debug
Windows | Breakpoints, right-click the data breakpoint, and select Properties.
The Breakpoint Properties dialog box is shown in the following figure.
Once you have the Breakpoint properties dialog box displayed, click the
button labeled Advanced to display the Actions section of the Breakpoint
Properties dialog box (shown in the following figure). To make the breakpoint a non-breaking breakpoint, uncheck
the Break checkbox in the Actions section. Unless you enable this checkbox again
in the future, the integrated debugger will not load when this breakpoint
You use the remaining options in the Actions section of the Breakpoints
Properties dialog box to specify what should happen when this breakpoint is
encountered. Each of these options is described in the following sections.
If you check the Ignore subsequent exceptions checkbox, the
integrated debugger will stop loading when an exception is raised, at least
until another breakpoint that enables subsequent exceptions is encountered. To
define a breakpoint that enables exceptions, place a second
breakpoint (breaking or non-breaking), and check the Enabled subsequent
Disabling and enabling exceptions using non-breaking breakpoints permits you
to conveniently run a section of code from the IDE that raises exceptions,
without interruption from the debugger. This technique is particularly useful
for sections of code that raise exceptions for the purpose of displaying errors
or warnings to the user, but which do not constitute bugs or other similar
errors in your code.
For example, when client-side validation code (code that tests the validity
of user input) raises an exception, it is really not an error, code-wise.
Instead, it is to inform the user that validation has failed, suggest to the
user how to correct the problem, and to abort any remaining validations or
operations that would proceed had the user's input been valid. While testing
your validation code it serves you no purpose to be interrupt by the integrated
debugger. I often disable the debuggers involvement in exceptions prior to
executing a block of code that performs client-side validation, enabling
exceptions once again after the validations have been performed.
Use the Log message field to enter a string that will be written to the event
log when the non-breaking breakpoint triggers. This option is a nice
alternative to using the Windows API call OutputDebugString, which requires
adding additional code to your project. After running your application in the
IDE, or while the integrated debugger is loaded, you can view messages written
to the event log using the Log message field by select View | Debug Windows |
Event Log. Message written to the event log using breakpoints are identified by
the label "Breakpoint Message."
The Eval expression field serves two purposes. First, it can be used to
evaluate any expression. This expression can include any object, method,
variable, constant, resource, function, or type that is within scope of the breakpoint. After having entered a value in
Eval expression, you have the option to enabled the Log result checkbox. When
Log result is checked, the value of the expression is written to the event
log. As with the Log Message field, Eval expression results that are logged
permit you to avoid writing expression to the event log using OutputDebugString.
One of my favorite uses of Eval expression is the execution of a function
that is within scope of the breakpoint, particularly a function that has
side-effects. For example, imagine that upon startup your application tests for the
existence of a CDS file that is used by a ClientDataSet. If the file is absent,
you call CreateDataSet to create the data structure that the ClientDataSet will
use, after which that structure is written to a CDS file.
During testing, you may always want to begin your application without an
existing CDS file. One way of doing this is the write a function that deletes
the CDS file if it exists. You can then call
this function with a breakpoint by adding the function name to the Eval expression field. So long as this
Eval expression is associated with a breakpoint that is encountered prior to the
first testing for the presence of the CDS file, the function will always ensure
that no file exists once the test occurs. Since breakpoints only trigger when
the application is run from the IDE, a deployed application, or one executed
outside of the IDE, will not destroy an existing CDS file.
In order to execute a function using the Eval expression field, that function
must be included in your compiled code by the linker. In other words, this
function must be referenced in code that might be executed, otherwise it
will not be included in the compiled code due to optimizations. You can ensure
that a function will not be removed during optimization by calling it from an
event handler that will never execute.
The final two options in the Actions section of the Breakpoint Properties dialog box
permit you to enable and disable groups of breakpoints. A breakpoint group
consists of one or more breakpoints to which you have assigned the same group
name, using the Group field on the Breakpoint Properties dialog box.
When a breakpoint group is disabled, none of the breakpoints in the group
will trigger when encountered by code running in the IDE. Enabling a group
restores the enabled property of any disabled breakpoints in the group.
Here is how I have used these two options. I had a segment of code that I
assumed ran only once each time the application launched. If ever this code
segment ran a second
time, I wanted to be able to break repeatedly during the segment, in order to
inspect the status of watch expressions. What I did was to place disabled
breakpoints throughout this code segment, assigning each one of them the same
group name, WhyTwice. In this particular instance, the WhyTwice breakpoints were
breaking breakpoints. Then, I placed a separate, non-breaking breakpoint on the
first executable line of this code segment, set the Pass count property of this
breakpoint to 2, and finally, set the Enable group field to the group name
During testing, if the non-breaking breakpoint was encountered a second time,
indicating that the code segment was executing a second time, the breakpoints in
the WhyTwice group became enabled, and loaded the integrated debugger when
encountered, permitting me to determine what conditions existed that caused the
code segment to be executed a second time.
Cary Jensen is President of Jensen Data Systems, Inc., a Texas-based training
and consulting company that won the 2002 and 2003 Delphi Informant Magazine Readers
Choice Awards for Best Training. He is the author and presenter for Delphi
Developer Days (www.DelphiDeveloperDays.com), an information-packed Delphi
(TM) seminar series that tours North America and Europe, and Delphi Developer
Days Power Workshops, focused Delphi (TM) training. Cary is also an
award-winning, best-selling co-author of nineteen books, including Advantage
Database Server: The Official Guide (2003, McGraw-Hill/Osborne), Building
Kylix Applications (2001, Osborne/McGraw-Hill), Oracle JDeveloper (1999, Oracle
Press), JBuilder Essentials (1998, Osborne/McGraw-Hill), and Delphi In Depth
(1996, Osborne/McGraw-Hill). For information about onsite training and
consulting you can contact Cary at email@example.com, or visit his
company's Web site at www.JensenDataSystems.com.
) 2003 Cary Jensen, Jensen Data Systems, Inc.
ALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORM WITHOUT
THE EXPRESS, WRITTEN CONSENT OF THE AUTHOR.
Download Delphi XE8 now!
Get Free Trial
Webinars on demand!
More social media choices:
Delphi on Google+
@RADTools on Twitter
Server Response from: ETNASC01