Tải bản đầy đủ - 0 (trang)
Chapter 13. Tracing and Debugging in .NET

Chapter 13. Tracing and Debugging in .NET

Tải bản đầy đủ - 0trang

The TraceDemo Example

The TraceDemo example illustrates the use of the diagnostic functionality. If you run the

example, you will get the following output:

Trace Listeners:


This was compiled with a DEBUG directive!

This was compiled with a TRACE directive!

Debug Boolean Switch disabled at startup.

Debug Boolean Switch enabled!

Trace Switch Startup Value = Warning



Trace Listeners:

Console.Out Listener

Output File Listener

Refer to this output in the ensuing discussion. You will also find a file called output.txt

on your computer in the directory where this program ran.

Enabling Debug and Trace Output

To use the Debug class, the DEBUG flag must be defined or else the methods of this

class will not be compiled into the executable or library. Similarly, to use the Trace

class the TRACE flag must be defined. This way you can have different diagnostics for

release and debug builds. These constants can be set in the Visual Studio.NET Project |

Properties | Configuration Properties | Build Window's conditional compilation constants

shown in Figure 13-1.

Figure 13-1. Visual Studio window for setting conditional compilation


You can also define the constants in your source files or supply the definition to the

compiler's command line.

Using the Debug and Trace Classes

The useful methods and properties are static. The overloaded WriteLine and Write are used

to write debug or trace output. The overloaded WriteLineIf and WriteIf write output if the

condition in their first argument is true.

Debug.WriteLine("This was compiled with a DEBUG


Trace.WriteLine("This was compiled with a TRACE



Debug.WriteLineIf(DebugBooleanSwitch.Enabled, "Debug

Boolean Switch enabled at startup.");


"Debug Boolean Switch disabled at startup.");

Output is indented with the Indent and Unindent methods. The indentation size is

controlled with the IndentSize property.



Trace.IndentSize = 10;

You can also set the indentation size in the application configuration file.

The Assert method can check an assertion. The AutoFlush property and the Flush method

control the flushing of the output buffer.

Using Switches to Enable Diagnostics

Switches give you finer grain control over the diagnostic output. You can use the

BooleanSwitch class to turn output on or off based on the value of its Enabled property.

The TraceSwitch class gives you five hierarchical levels of control for its Level property:

TraceError, TraceWarning, TraceInfo, TraceVerbose, and Off. These values are part of

the TraceLevelEnumeration. Setting a lower Trace level means that the higher ones are set

as well. For example, if the TraceWarning level is set, both the TraceError and

TraceWarning levels are enabled.

DebugBooleanSwitch.Enabled = true;

Debug.WriteLineIf(DebugBooleanSwitch.Enabled, "Debug

Boolean Switch enabled!");




The constructors for these switches take two parameters. The first is the name of the switch,

the second is a text description of the switch. Both BooleanSwitch and TraceSwitch classes

inherit from the abstract class Switch. You can write your own customized switch classes by

inheriting from the Switch class. Note that the Enabled property of the BooleanSwitch and

the Level and named level properties of the TraceSwitch are not part of the Switch class.

Enabling or Disabling Switches

You can use settings in your application configuration file to enable or disable a switch at

startup. This can also be done programmatically.

Configuration File Switch Settings

You can set the switch's initial setting in the application's configuration file.

If no values are found, the initial value of the Enabled property of the BooleanSwitch with

the name DebugSwitch is set to false and the TraceSwitch's Level property is set to


Programmatic Switch Settings

The Enabled property of the BooleanSwitch can be set to true or false. The Level property

of the TraceSwitch can be set to one of the options of the TraceLevel enumeration:

TraceOff, TraceError, TraceWarning, TraceInfo, TraceVerbose. You can get the level

of the TraceSwitch's setting by examining the TraceError, TraceWarning, TraceInfo,

TraceVerbose properties.

Using Switches to Control Output

You can test the value of the switch before you write, debug, or trace output. You can do this

with an if statement, or as an argument to one of the Trace or Debug classes' methods.









Since you can set these values outside of your program's code, you can select the

circumstances under which you get a particular level of debug or trace output. For example,

you can turn on TraceVerbose output if you really need a high level of diagnostics, but turn

it off after you have found the problem.


Classes derived from the abstract class TraceListener represent destinations for

the diagnostic output. The TextWriterTraceListener is designed to direct output

to a TextWriter, Stream, or FileStream. Console.Out is an example of a

commonly used output stream. The EventLogTraceListener class allows you to

send output to an EventLog. You can create your own event logs with the

EventLog's static method CreateEventSource method. The

DefaultTraceListener sends output to the debugging output window. Default

Debug output can be viewed in Visual Studio.NET's Output window or with

utilities (such as DBMon, which is included with this project). You can customize

where output appears by implementing your own class derived from


Listeners Collection

Both the Debug and Trace classes have a static Listeners collection. This collection of

TraceListeners represents a list of TraceListener objects that want to receive the output

from the Debug or Trace class. Listeners are added to or removed from the collection just as

with any other .NET collection.

TextWriterTraceListener ConsoleOutput = new


"Console.Out Listener");


Stream OutputFile = File.Create("output.txt");

TextWriterTraceListener OutputFileListener = new


"Output File Listener");



In this code extract, the OutputFileListener in the example will send the Trace output to a

file called output.txt. The DefaultTraceListener is added automatically to the Listener

collections. Any of the listeners, including the default listener, can be removed from the

collection by invoking the collection's Remove method. To list all listeners in the collection:

foreach(TraceListener tr in Trace.Listeners)


Console.WriteLine("\t" + tr.Name);



Instrumenting your application for degrees of debugging and diagnostic output is

a common program task. The diagnostic classes exemplify the way .NET

provides classes to handle standard programming tasks so you can concentrate on

developing the business logic of your programming, not on building

infrastructure. On the other hand, they also exemplify how the .NET classes are

partitioned so that you can customize the infrastructure using as much or as little

of the other classes as you require.

Chapter 14. Interoperability

Microsoft .NET is a powerful platform, and there are many advantages in writing

a new application within the .NET Framework. However, a typical application is

not a world unto itself, but is built from legacy components as well as new

components, and interoperability is very important. We discussed one kind of

interoperability in Chapter 11 in connection with Web Services. Using the SOAP

protocol it is possible for .NET applications to call Web Services on other

platforms, including Unix, mainframes, and mobile devices.

In this chapter we will look at another kind of interoperability, the interfacing of

managed and unmanaged code running under Windows. The dominant

programming model in modern Windows systems is the Component Object

Model, or COM. There exist a great many legacy COM components, and so it is

desirable for a .NET program, running as managed code, to be able to call

unmanaged COM components. The converse situation, in which a COM client

needs to call a .NET server, can also arise. [1] Apart from COM, we may also

have need for a .NET program to call any unmanaged code that is exposed as a

DLL, including the Win32 API. The .NET Framework supports all these

interoperability scenarios through COM Interoperability and the Platform

Invocation Services or PInvoke.


COM interop is the only mechanism provided for unmanaged code to

call managed code.

In this chapter we assume that you understand the concepts behind the legacy


Calling COM Components from Managed Code

The first interoperability scenario we will look at is managed code calling COM components.

The .NET Framework makes it easy to create a Runtime Callable Wrapper (RCW), which acts

as a bridge between managed and unmanaged code. The RCW is illustrated in Figure 14-1.

Figure 14-1. A Runtime Callable Wrapper between managed and unmanaged


You could implement an RCW assembly yourself, using the PInvoke facility (described in a

later section) to call into the necessary APIs, such as CoCreateInstance and the IUnknown

methods directly. But that is not necessary, because the Tlbimp.exe tool can read type library

information, and automatically generate the appropriate RCW for you. Visual Studio.NET

makes it even easier when you add a reference to a COM object in Solution Explorer. We will

examine both of these facilities, as we look at some examples of COM components and .NET


The Tlbimp.exe Utility

The Tlbimp.exe utility (Type Library to .NET Assembly Converter) program is provided in the

\Program Files\Microsoft.NET\FrameworkSDK\Bin directory. It is used to generate

managed classes that wrap unmanaged COM classes. The resulting RCW is a .NET component

(i.e., a managed DLL assembly) that managed client code can use to access the COM interface

methods that are implemented in the COM component. The Tlbimp tool is a command line

program that reads COM type library information, and generates a managed wrapper class

along with the associated metadata, and places the result into the RCW assembly. You can view

the resulting contents in this assembly using the Ildasm tool. The command line syntax for

Tlbimp is shown below.

Tlbimp TypeLibName [options]

Where options may contain the following:


Assembly file name


Assembly Namespace


Assembly version number


Reference assembly

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Chapter 13. Tracing and Debugging in .NET

Tải bản đầy đủ ngay(0 tr)