Tracing





Tracing

Implement tracing.

  • Configure and use trace listeners and trace switches.

  • Display trace output.

Instrument and debug a Windows service, a serviced component, a .NET Remoting object and an XML Web service: Create and apply debugging code to components and applications.

The process of testing can reveal the presence of errors in a program, but to find the actual cause of a problem, you sometimes need the program to generate information about its own execution. Analysis of this information may help you understand why the program is behaving in a particular way and may lead to possible resolution of the error.

This process of collecting information about program execution is called tracing. You trace a program's execution in Visual C# .NET by generating messages about the program's execution with the use of the Debug and Trace classes.

The Trace and Debug classes have several things in common:

  • They both belong to the System.Diagnostics namespace.

  • They have members with the same names.

  • All their members are static.

  • They are conditionally compiled (that is, their statements are included in the object code only if a certain symbol is defined).

The only difference between the Debug and Trace classes is that the members of the Debug class are conditionally compiled, but only when the DEBUG symbol is defined. On the other hand, members of the Trace class are conditionally compiled, but only when the TRACE symbol is defined.

Visual C# .NET provides two basic configurations for a project: Debug and Release. Debug is the default configuration. When you compile a program by using the Debug configuration, both TRACE and DEBUG symbols are defined, as shown in Figure. When you compile a program in the Release configuration, only the TRACE symbol is defined. You can switch between the Debug and Release configurations by using the Solution Configurations combo box on the standard toolbar (as shown in Figure) or by using the Configuration Manager dialog box (as shown in Figure) from the project's Property Pages dialog box.

Figure. Both the TRACE and DEBUG symbols are defined in Debug configuration.

graphics/09fig02.jpg

3. The standard toolbar for Visual Studio .NET contains a solutions configuration combo box to allow users to easily change solution configuration.

graphics/09fig03.jpg

4. The Configuration Manager dialog box enables you to set configuration for projects in a solution.

graphics/09fig04.jpg

Later in this chapter, you will learn how to make these changes from within the program, through the command-line compilation options and through the configuration files.

When you compile a program by using the Debug configuration, the code that uses the Debug and the Trace classes is included in the compiled code. When you run such a program, both the Debug and Trace classes generate messages. On the other hand, when you compile a program by using the Trace configuration, it does not include any calls to the Debug class. Thus, when such a program is executed, you get only the messages generated by the Trace class.

Figure summarizes the members of both the Trace and Debug classes.

Figure Members of Debug and Trace Classes

Member

Type

Description

Assert()

Method

Checks for a condition and displays a message if the condition is false

AutoFlush

Property

Specifies whether the Flush() method should be called on the listeners after every write

Close()

Method

Flushes the output buffer and then closes the listeners

Fail()

Method

Displays an error message

Flush()

Method

Flushes the output buffer and causes the buffered data to be written to the listeners

Indent()

Method

Increases the current IndentLevel property by one

IndentLevel

Property

Specifies the indent level

IndentSize

Property

Specifies the number of spaces in an indent

Listeners

Property

Specifies the listeners that are monitoring the trace output

Unindent()

Method

Decreases the current IndentLevel property by one

Write()

Method

Writes the given information to the trace listeners in the Listeners collection

WriteIf()

Method

Writes the given information to the trace listeners in the Listeners collection only if a condition is true

WriteLine()

Method

Acts the same as the Write() method, but appends the information with a newline character

WriteLineIf()

Method

Acts the same as the WriteIf() method, but appends the information with a newline character

NOTE

Tracing Helps in Resolving Hard-to-Reproduce Errors When programs run in a production environment, they sometimes report errors (mostly related to performance or threading problems) that are difficult to reproduce in a simulated testing environment. Tracing a production application can help you get runtime statistics for the program; this might help you trap these hard-to-reproduce errors.


Using Trace and Debug to Display Information

Step-by-Step 9.1 demonstrates how to use some of the methods of the Trace and Debug classes.

STEP BY STEP

9.1 Using the Trace and Debug Classes to Display Debugging Information

  1. Launch Visual Studio .NET. Select File, New, Blank Solution and name the new solution 320C09.

  2. Add a new Visual C# .NET Windows application named StepByStep9_1 to the solution.

  3. In the Solution Explorer, rename the default Form1.cs to FactorialCalculator.cs. Open the form in code view and change all occurrences of Form1 to refer to FactorialCalculator instead.

  4. Add four Label controls (with the heading label named lblHeading), two TextBox controls (txtNumber and txtFactorial), and a Button control (btnCalculate) to the form and arrange the controls as shown in Figure.

    5. Design of a form that calculates the factorial of a given number.

    graphics/09fig05.jpg

  5. Switch to code view and add the following using directive:

    using System.Diagnostics;
    
    
  6. Double-click the Button control and add the following code to its Click event handler:

    private void btnCalculate_Click(object sender,
    
        System.EventArgs e)
    
    {
    
        // write a debug message
    
        Debug.WriteLine(
    
           "Inside Button Click event handler");
    
        // start indenting messages now
    
        Debug.Indent();
    
        int intNumber = Convert.ToInt32(txtNumber.Text);
    
        // make a debug assertion
    
        Debug.Assert(intNumber >= 0, "Invalid value",
    
            "negative value in debug mode");
    
        // write a trace assertion
    
        Trace.Assert(intNumber >= 0, "Invalid value",
    
           "negative value in trace mode");
    
    
    
        int intFac = 1;
    
        for (int i = 2; i <= intNumber; i++)
    
        {
    
            intFac = intFac * i;
    
            // write a debug message
    
            Debug.WriteLine(i,
    
               "Factorial Program Debug, Value of i");
    
        }
    
        // write a trace message if the condition is true
    
        Trace.WriteLineIf(intFac < 1,
    
           "There was an overflow",
    
           "Factorial Program Trace");
    
        // write a debug message if the condition is true
    
        Debug.WriteLineIf(intFac < 1,
    
           "There was an overflow",
    
           "Factorial Program Debug");
    
    
    
        txtFactorial.Text = intFac.ToString();
    
        // decrease the indent level
    
        Debug.Unindent();
    
    
    
        // write a debug message
    
        Debug.WriteLine(
    
           "Done with computations, returning...");
    
    }
    
    
  7. Run the solution. Keep the program running and switch to the Visual Studio .NET Integrated Development Environment (IDE). Select View, Other Windows, Output. Push the pin on the title bar of the output window so that it does not hide automatically. Now switch to the running program; enter 5 in the text box and click the Calculate button. You should see tracing messages that are generated by the program (see Figure).

    6. Debug and Trace messages are by default always displayed in the Output window.

    graphics/09fig06.jpg

  8. Now switch to the running program and enter the value 100 and click the Calculate button. Messages from both the Debug class and the Trace class (overflow message) are displayed in the output window. Note that the default configuration is the Debug configuration, where both the TRACE and DEBUG symbols are defined.

  9. Enter a negative value, such as -1, and click the Calculate button. This causes the assertion to fail, and you see a dialog box that shows an Assertion Failed message, as shown in Figure. This message box is generated by the Debug.Assert() method in the code. The dialog box gives you three choices: Abort, to terminate the program; Retry, to break the program execution so that you can debug the program; and Ignore, to continue the execution as if nothing has happened. Click Ignore, and you see another Assertion Failed dialog box. This one was generated by the Trace.Assert() method in the code. Click the Abort button to terminate the program execution.

    Figure. The Assertion Failed dialog box is displayed when an assertion made in the Assert() method fails.

    graphics/09fig07.jpg

  10. From the Solution Configurations combo box on the standard toolbar, select the Release configuration (Release configuration defines only the TRACE symbol). Run the program again. Enter the value 5 and click the Calculate button. The factorial is calculated, but no messages appear in the output window. Enter the value 100 and click the Calculate button. You should now see the overflow message generated by the Trace class in the output window. Finally, try calculating the factorial of -1. You should see just one dialog box, showing an Assertion Failed message. Click the Abort button to terminate the program.

Note from Step-by-Step 9.1 that you can use the methods of the Debug and Trace classes (for example, the WriteIf() and WriteLineIf() methods) to display messages based on conditions. This can be a very useful technique if you are trying to understand a program's flow of logic. Step-by-Step 9.1 also demonstrates the use of the Assert() method. The Assert() method tests your assumption about a condition at a specific place in the program. When an assertion fails, the Assert() method pinpoints the code that is not behaving according to your assumptions. A related method is Fail(). The Fail() method displays a dialog box similar to the one that Assert() shows, but it does not work conditionally. The Fail() method signals unconditional failure in a branch of code execution.

Trace Listeners

Listeners are the classes that are responsible for forwarding, recording, and displaying the messages generated by the Trace and Debug classes. You can associate multiple listeners with the Trace and Debug classes by adding listener objects to their Listeners property. The Listeners property is a collection that is capable of holding any objects derived from the TraceListener class. Both the Debug and the Trace classes share their Listeners collection, so an object added to the Listeners collection of the Debug class is automatically available in the Trace class and vice-versa.

The TraceListener class is an abstract class that belongs to the System.Diagnostics namespace and has three implementations:

  • DefaultTraceListener— An object of this class is automatically added to the Listeners collection. Its behavior is to write messages on the output window.

  • TextWriterTraceListener— An object of this class writes messages to any class that derives from the Stream class that includes the console or a file.

  • EventLogTraceListener— An object of this class writes messages to the Windows event log.

If you want a listener object to perform differently from these three listener classes, you can create your own class that inherits from the TraceListener class. When doing so, you must at least implement the Write() and WriteLine() methods.

EXAM TIP

The Same Listeners for Debug and Trace Messages sent through the Debug and Trace objects are directed through each listener object in the Listeners collection. Debug and Trace share the same Listeners collection, so any listener object that is added to the Trace.Listeners collection is also added to the Debug.Listeners collection.


Step-by-Step 9.2 shows how to create a custom listener class that implements the TraceListener class to send messages generated by the Debug and Trace class through email.

STEP BY STEP

9.2 Creating a Custom TraceListener Object

  1. Add a new Visual C# .NET Windows application named StepByStep9_2 to the solution.

  2. Add a reference to System.Web.dll to the project.

  3. Add a new class to the project named EmailTraceListener.cs. Modify the class with the following code (changing the From address to an appropriate email address):

    using System;
    
    using System.Diagnostics;
    
    using System.Text;
    
    using System.Web.Mail;
    
    
    
    namespace StepByStep9_2
    
    {
    
        public class EmailTraceListener : TraceListener
    
        {
    
            // Message log will be sent to this address
    
            private string mailto;
    
            // Store the message log
    
            private StringBuilder message;
    
    
    
            public EmailTraceListener(string mailto)
    
            {
    
                this.mailto = mailto;
    
            }
    
    
    
            // A custom listener must
    
            // override the Write() method
    
            public override void Write(string message)
    
            {
    
                if (this.message == null)
    
                    this.message = new StringBuilder();
    
                this.message.Append(message);
    
            }
    
    
    
            // A custom listener must
    
            // override the WriteLine() method
    
            public override void WriteLine(string message)
    
            {
    
                if (this.message == null)
    
                    this.message = new StringBuilder();
    
                this.message.Append(message);
    
                this.message.Append('\n');
    
            }
    
    
    
            // use the close method to send mail.
    
            public override void Close()
    
            {
    
                // ensure that the listener is flushed
    
                Flush();
    
                // MailMessage belongs to the
    
                // System.Web.Mail namespace
    
                // but can be used from
    
                // any managed application
    
                if (this.message != null)
    
                {
    
                    // Create a MailMessage object
    
                    MailMessage mailMessage =
    
                        new MailMessage();
    
                    mailMessage.From =
    
                        "[email protected]";
    
                    mailMessage.To = this.mailto;
    
                    mailMessage.Subject =
    
                   "Factorial Program Debug/Trace output";
    
                    mailMessage.Body =
    
                        this.message.ToString();
    
                    //send the mail
    
                    SmtpMail.Send(mailMessage);
    
                }
    
            }
    
    
    
            public override void Flush()
    
            {
    
                // nothing much to do here
    
                // so call the base class's implementation
    
                base.Flush();
    
            }
    
        }
    
    }
    
    

    NOTE

    Sending Email Messages The types in the System.Web.Mail namespace can be used from any managed application, including both Web and Windows applications. This functionality is supported only in the Windows 2000, Windows XP Professional, and Windows Server 2003 operating systems. For other operating systems, you can send email messages by manually establishing Simple Mail Transfer Protocol (SMTP) connections through the System.Net.TcpClient class or by using an SMTP component, which you might be able to get from a component vendor for free.

  4. In Solution Explorer, copy the FactorialCalculator.cs form from the StepByStep9_1 project to the current project. Change the Text property of the form to Factorial Calculator 9_2. Switch to the code view and change the namespace of the form to StepByStep9_2. Delete the default Form1.cs in the project.

  5. Double-click the form and add the following code to the Load event of the FactorialCalculator form, changing the email address [email protected] to a real address that can receive email:

    private void FactorialCalculator_Load(object sender,
    
        System.EventArgs e)
    
    {
    
        // Add a custom listener to
    
        // the Listeners collection
    
        Trace.Listeners.Add(new EmailTraceListener(
    
           "[email protected]"));
    
    }
    
    
  6. Attach an event handler to the Closing event of the FactorialCalculator form and add the following code to the event handler:

    private void FactorialCalculator_Closing(
    
        object sender,
    
        System.ComponentModel.CancelEventArgs e)
    
    {
    
        // Call the close methods for all listeners
    
        Trace.Close();
    
    }
    
    
  7. Set project StepByStep9_2 as the startup project.

  8. Run the solution, using the default Debug configuration. Enter a value and click the Calculate button. Close the form. Note that both Debug and Trace messages appear on the output window. A local SMTP server is used to email these messages to the specified addresses. Run the project again in the Release mode. Enter a large value, such as 100, and click the Calculate button. The overflow message appears in the output window. Close the form. While you are closing the form, the messages generated by the Trace class are sent to the specified email address.

Trace Switches

So far in this chapter, you have learned that the Trace and Debug classes can be used to display valuable information related to program execution. You have also learned that it is possible to capture messages in a variety of formats. In this section, you will learn how to control the nature of messages that you want to get from a program.

You can use trace switches to set the parameters that control the level of tracing that needs to be done on a program. You set these switches via an Extensible Markup Language (XML)–based external configuration file. This is especially useful when the application you are working with is in production mode. You might initially want the application not to generate any trace messages. However, if the application later has problems or you just want to check on the application's health, you might want to instruct the application to emit a particular type of trace information by just changing the configuration file. You are not required to recompile the application; the application automatically picks up the changes from the configuration file when it restarts.

There are two predefined classes for creating trace switches: the BooleanSwitch class and the TraceSwitch class. Both of these classes derive from the abstract Switch class. You can also define your own trace switch classes by deriving classes from the Switch class.

You use the BooleanSwitch class to differentiate between two modes of tracing: trace-on and trace-off. Its default value is zero, which corresponds to the trace-off state. If it is set to any nonzero value, it corresponds to the trace-on state.

Unlike BooleanSwitch, the TraceSwitch class provides five different levels of tracing switches. These levels are defined by the TraceLevel enumeration and are listed in Figure. The default value of TraceLevel for a TraceSwitch object is 0 (Off).

Figure The TraceLevel Enumeration

Enumerated Value

Integer Value

Type of Tracing

Off

0

None

Error

1

Only error messages

Warning

2

Warning messages and error messages

Info

3

Informational messages, warning messages, and error messages

Verbose

4

Verbose messages, informational messages, warning messages, and error messages

Figure displays the important properties of the TraceSwitch class.

EXAM TIP

Out-of-Range Values for BooleanSwitch and TraceSwitch For a BooleanSwitch object, if any nonzero (negative or positive) value is specified in the configuration file, the BooleanSwitch object's Enabled property is set to true. For a TraceSwitch object, if a value greater than 4 is specified, the Level property of the object is set to TraceLevel.Verbose (4). If a negative value is specified, a StackOverflow exception occurs at runtime.


Figure Important Properties of the TraceSwitch Class

Property

Description

Description

Describes the switch (inherited from Switch).

DisplayName

Specifies a name used to identify the switch (inherited from Switch).

Level

Specifies the trace level that helps select which trace and debug messages will be processed. Its value is one of the TraceLevel enumeration values (refer to Figure).

TraceError

Returns true if Level is set to Error, Warning, Info, or Verbose; otherwise, it returns false.

TraceInfo

Returns true if Level is set to Info or Verbose; otherwise, it returns false.

TraceVerbose

Returns true if Level is set to Verbose; otherwise, it returns false.

TraceWarning

Returns true if Level is set to Warning, Info, or Verbose; otherwise, it returns false.

Step-by-Step 9.3 demonstrates how to use trace switches in an application.

STEP BY STEP

9.3 Using the TraceSwitch Class

  1. Add a new Visual C# .NET Windows application named StepByStep9_3 to the solution.

  2. In Solution Explorer, copy the FactorialCalculator.cs form from the StepByStep9_1 project to the current project. Change the Text property of the form to Factorial Calculator 9_3. Switch to the code view and change the namespace of the form to StepByStep9_3. Delete the default Form1.cs.

  3. Declare the following static variable at the class level, just after the Main() method:

    static TraceSwitch traceSwitch =
    
           new TraceSwitch("FactorialTrace",
    
           "Trace the factorial application");
    
    
  4. Change the Click event handler of the Calculate button so that it has the following code:

    private void btnCalculate_Click(object sender,
    
       System.EventArgs e)
    
    {
    
        if (traceSwitch.TraceVerbose)
    
            // write a debug message
    
            Debug.WriteLine(
    
               "Inside the Button Click event handler");
    
    
    
        // start indenting messages now
    
        Debug.Indent();
    
        int intNumber = Convert.ToInt32(txtNumber.Text);
    
    
    
        if (traceSwitch.TraceError)
    
        {
    
            // make a debug assertion
    
            Debug.Assert(intNumber >= 0, "Invalid value",
    
               "negative value in debug mode");
    
        }
    
    
    
        int intFac = 1;
    
        for (int i = 2; i <= intNumber; i++)
    
        {
    
            intFac = intFac * i;
    
            // write a debug message
    
            if (traceSwitch.TraceInfo)
    
                Debug.WriteLine(i,
    
                   "Factorial Program Debug, Value of i");
    
        }
    
    
    
        if (traceSwitch.TraceWarning)
    
            // write a debug message
    
            // if the condition is true
    
            Debug.WriteLineIf(intFac < 1,
    
                "There was an overflow",
    
                "Factorial Program Debug");
    
    
    
        txtFactorial.Text = intFac.ToString();
    
        // decrease the indent level
    
        Debug.Unindent();
    
    
    
        if (traceSwitch.TraceVerbose)
    
            // write a debug message
    
            Debug.WriteLine(
    
               "Done with computations, returning...");
    
    }
    
    
  5. Add an XML file named StepByStep9_3.exe.config to the current project. In the XML editor, type the following configuration data in the XML file:

    <?xml version="1.0" encoding="utf-8" ?>
    
    <configuration>
    
        <system.diagnostics>
    
            <switches>
    
                <add name="FactorialTrace" value="4" />
    
            </switches>
    
        </system.diagnostics>
    
    </configuration>
    
    
  6. In Solution Explorer, select View All Files from the toolbar. Navigate to the bin\Debug folder. Copy the StepByStep9_3.exe.config file from the project folder to the bin\Debug folder.

    EXAM TIP

    Switches in a Web Application or a Web Service To enable trace switches in a Web application or a Web service, you need to add the <system.diagnostics> element to the application configuration file, web.config.

  7. Set project StepByStep9_3 as the startup project.

  8. Run the project, using the default Debug configuration. Enter the value 5; note that all messages appear in the output window. Enter a negative value and then a large value, and you see all the errors and warning messages. Close the form. Modify the XML file to change the value of FactorialTrace to 3. Run the project again; you should now see all messages except the one set with TraceLevel as Verbose. Repeat the process, with values of FactorialTrace in the configuration file changed to 2, 1, and 0.

  9. Modify the program to change all Debug statements to Trace statements. Copy the XML configuration file to the bin\Release folder in the project and then repeat step 9, using the Release configuration.

Conditional Compilation

The C# programming language provides a set of preprocessing directives. You can use these directives to skip sections of source files for compilation, to report errors and warnings, or to mark distinct regions of source code.

NOTE

C# and the Preprocessor There is no separate preprocessor in the C# compiler. The lexical analysis phase of the compiler processes all the preprocessing directives. C# uses the term preprocessor from a conventional point of view, in contrast to languages such as C and C++ that have separate preprocessors for taking care of conditional compilation.


Figure summarizes the preprocessing directives that are available in C#.

C# Preprocessing Directives

Directives

Description

#if, #else, #elif, and #endif

These directives conditionally skip the sections of code. The skipped sections are not part of the compiled code.

#define and #undef

These directives define or undefine symbols in the code.

#warning and #error

These directives explicitly generate error or warning messages. The compiler reports errors and warnings in the same way it reports other compile-time errors and warnings.

#line

This directive alters the line numbers and source file filenames reported by the compiler in warning and error messages.

#region and #endregion

These directives mark sections of code. A common example of these directives is the code generated by Windows Forms Designer. Visual designers such as Visual Studio .NET can use these directives to show, hide, and format code.

In addition to providing preprocessing directives, the C# programming language also provides a ConditionalAttribute class.

You can mark a method as conditional by applying the Conditional attribute to it. The Conditional attribute takes one argument that specifies a symbol. The conditional method is either included or omitted from the compiled code, depending on the definition of the specified symbol at that point. If the symbol definition is available, the method's code is included; otherwise, the method's code is excluded from the compiled code.

The conditional compilation directives and methods with the Conditional attribute enable you to keep debugging-related code in the source code but exclude it from the compiled version. This removes the extraneous messages and the production programs do not encounter performance hits because of processing additional code. In this case, if you want to resolve some errors, you can easily activate the debugging code by defining a symbol and recompiling the program.

Step-by-Step 9.4 demonstrates the use of the Conditional attribute and the conditional compilation directives.

EXAM TIP

Conditional Methods A method must have its return type set to void to have the Conditional attribute applied to it.


STEP BY STEP

9.4 Using Conditional Compilation

  1. Add a new Visual C# .NET Windows application named StepByStep9_4 to the solution.

  2. In Solution Explorer, copy the FactorialCalculator.cs form from the StepByStep9_1 project to the current project. Change the Text property of the form to Factorial Calculator 9_4. Switch to the code view and change the namespace of the form to StepByStep9_4. Delete the default Form1.cs.

  3. Add the following two conditional methods to the class definition:

    [Conditional("DEBUG")]
    
    public void InitializeDebugMode()
    
    {
    
        lblHeading.Text =
    
            "Factorial Calculator: Debug Mode";
    
    }
    
    [Conditional("TRACE")]
    
    public void InitializeReleaseMode()
    
    {
    
        lblHeading.Text =
    
            "Factorial Calculator Version 1.0";
    
    }
    
    
  4. Attach an event handler to the form's Load event and add the following code:

    private void FactorialCalculator_Load(
    
        object sender, System.EventArgs e)
    
    {
    
        #if !DEBUG && !TRACE
    
        #error you should have either DEBUG or TRACE defined
    
        #endif
    
    
    
        #if DEBUG
    
            Debug.WriteLine(
    
                "Program started in debug mode");
    
            InitializeDebugMode();
    
        #else
    
            Trace.WriteLine(
    
                "Program started in release mode");
    
            InitializeReleaseMode();
    
        #endif
    
    }
    
    
  5. Set project StepByStep9_4 as the startup project.

  6. Run the solution, using the default Debug configuration. The heading of the form displays "Factorial Calculator: Debug Mode" (see Figure). The output window also displays a string: Program started in debug mode. Close the program and start it again in the Release mode. A different heading appears in the form (see Figure) and a different message appears in the output window.

    8. The Factorial Calculator is compiled conditionally via the Debug configuration.

    graphics/09fig08.jpg

    9. The Factorial Calculator is compiled conditionally via the Release configuration.

    graphics/09fig09.jpg

  7. Add the following line as the very first line of the code:

    #undef DEBUG
    
    

    Run the program, using the Debug configuration. Note that the program is executed as if it were executed in the Trace configuration. The Debug configuration defines both the DEBUG and TRACE symbols, but the DEBUG symbol is undefined because of the #undef preprocessing directive. Therefore, the compiled code includes only the code specified in the #else part of the preprocessing directive.

  8. Add the following line just after the directive placed in step 7:

    #undef TRACE
    
    

    Try running the program. Rather than run the program, the compiler throws an error message, complaining that both the DEBUG and TRACE symbols are undefined. This message is caused by the conditional logic in the Load event handler of the form.

You can define the DEBUG and TRACE symbols for the compiler in the following ways:

  • By modifying the project's Property Pages dialog box.

  • By using the #define directive at the beginning of the code file.

  • By using the /define (/d for short) option with the command-line C# compiler.

Step-by-Step 9.4 demonstrated conditional compilation with the DEBUG and TRACE symbols. You can also use conditional compilation with any other custom-defined symbols to perform conditional compilation.

GUIDED PRACTICE EXERCISE 9.1

The goal of this exercise is to add an EventLogTraceListener object to the Factorial Calculator program so that it will write all Trace and Debug messages to the Windows event log.

This exercise will give you good practice on using trace listeners.

How would you create such a form?

You should try working through this problem on your own first. If you get stuck, or if you'd like to see one possible solution, follow these steps:

  1. Add a new Visual C# .NET Windows application named GuidedPracticeExercise9_1 to the solution.

  2. In Solution Explorer, copy the FactorialCalculator.cs form from the StepByStep9_1 project to the current project. Change the form's Text property to Factorial Calculator GuidedPracticeExercise9_1. Switch to the code view and change the form's namespace to GuidedPracticeExercise9_1. Delete the default Form1.cs.

  3. Double-click the form to add an event handler for the Load event. Add the following code to the event handler:

    private void FactorialCalculator_Load(
    
        object sender, System.EventArgs e)
    
    {
    
        //Add an event log listener to
    
        //the Listeners collection
    
        Trace.Listeners.Add(new EventLogTraceListener(
    
            "FactorialCalculator"));
    
    }
    
    
  4. Set project GuidedPracticeExercise9_1 as the startup project.

  5. Run the project. Enter a value for finding a factorial. Click the Calculate button. Close the program. Select View, Server Explorer. Navigate to your computer and expand the Event Logs node, the Application node, and the FactorialCalculator node. The messages generated by the Trace and Debug classes are added to the Application event log, as shown in Figure.

    10. You can view the Windows Event Log from Server Explorer.

    graphics/09fig10.jpg

If you have difficulty following this exercise, review the sections "Trace Listeners" and "Using Trace and Debug Classes to Display Information," earlier in this chapter. After doing that review, try this exercise again.

REVIEW BREAK

  • The Trace and Debug classes can be used to display informative messages in an application when the DEBUG and TRACE symbols are defined, respectively, at the time of compilation.

  • By default, both TRACE and DEBUG symbols are defined in the Debug configuration for compilation, and only the TRACE symbol is defined for the Release configuration for compilation.

  • Listeners are objects that receive trace and debug output. By default, both Trace and Debug classes have the DefaultTraceListener object in their Listeners collection. The DefaultTraceListener object displays messages in the output window.

  • Debug and Trace objects share the same Listeners collection. Therefore, any listener object added to the Trace.Listeners collection is also added to the Debug.Listeners collection.

  • Trace switches enable you to change the types of messages that are traced by a program, depending on the value stored in the XML configuration file. You need not recompile the application for this change to take effect; just restart it. You need to implement code to display the messages, depending on the value of the switch.

  • C# preprocessing directives enable you to define and undefine symbols in an application, report errors or warnings, mark regions of code, and conditionally skip code for compilation.

  • The Conditional attribute enables you to conditionally add or skip a method for compilation, depending on the value of the symbol that is passed as a parameter to the attribute.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows