Debugging



Debugging

Instrument and debug a Windows service, a serviced component, a .NET Remoting object, and an XML Web service.

  • Configure the debugging environment.

  • Use interactive debugging.

Log test results.

  • Resolve errors and rework code.

  • Control debugging in the Web.config file.

  • Use SOAP extensions for debugging.

Debugging is the process of finding the causes of errors in a program, locating the lines of code that are causing those errors, and fixing those errors.

Without tools, the process of debugging can be very time-consuming and tedious. Thankfully, Visual Studio .NET comes loaded with a large set of tools and features to help you with various debugging tasks. Some of these features include

  • Support for the cross-language debugging of Visual C# .NET, Visual Basic .NET, Visual C++ .NET, Managed Extensions for C++, script, and SQL.

  • Support for debugging both managed and unmanaged applications.

  • The ability to attach a debugger to a running program outside the Visual Studio .NET environment on a local machine or remote machine.

  • Support for end-to-end debugging for distributed applications.

NOTE

Runtime Errors and Compile-Time Errors Compile-time errors are produced when a program does not comply with the syntax of the programming language. These errors are trivial and are generally pointed out by compilers themselves. Runtime errors occur in programs that are compiled successfully but do not behave as expected. The process of testing and debugging applies to runtime errors only. Testing reveals these errors, and debugging repairs them.


Configuring the Debugging Environment

When starting a program from Visual Studio .NET for debugging, you should ensure that the program is started in the Debug configuration. In addition to this, to enable debugging in the ASP.NET Web application or a Web service, make sure that the debug attribute of the <compilation> element in the web.config file is set to true.


<compilation

    defaultLanguage="c#"

    debug="true"/>

WARNING

Applications Run Slowly in Debug Mode When debugging is enabled for an application, the compiler includes extra debugging information in the page, creating a large output file that executes slowly.


Also, in ASP.NET Web applications and Web services, ensure that the Enable ASP.NET Debugging option is set to true. The Enable ASP.NET Debugging option is found in the Configuration Properties, Debugging option in the project's Property Pages dialog box.

NOTE

Debugging from Visual Studio .NET When you run an application from Visual Studio .NET, it automatically attaches a debugger to the process in which your application is running. In the case of ASP.NET Web applications and Web services, the debugger is also attached to the ASP.NET worker process (aspnet_wp.exe), if the Enable ASP.NET Debugging option is set to true in the project's Property Pages dialog box.


Setting Breakpoints and Stepping Through Program Execution

A common technique for debugging is to execute a program step by step. This systematic execution allows you to track the flow of logic, to ensure that the program is following the same paths of execution that you expect it to follow. If it does not, you can immediately identify the location of the problem.

Using step-by-step execution of a program also gives you an opportunity to monitor the program's state before and after a statement is executed. For example, you can check the values of variables, the records in a database, and other changes in the environment. Visual Studio .NET provides tools to make these tasks convenient.

The Debug menu provides three options for step-by-step execution of a program (see Figure). The keyboard shortcuts listed in Figure correspond to the default keyboard scheme of the Visual Studio .NET IDE. If you have personalized the keyboard scheme either through the Tools, Options, Environment, Keyboard menu, or through the Visual Studio .NET Start Page, you might have a different keyboard mapping. You can check out the keyboard mappings available for your customization through Visual Studio .NET's context-sensitive help.

Debug Options for Step-by-Step Execution

Debug Menu Item

Keyboard Shortcut

Purpose

Step Into

F11

Use this option to execute code in step mode. If a method call is encountered, the program execution steps into the code of the method and executes the method in step mode.

Step Over

F10

Use this option when a method call is encountered and you do not want to step into the method code. When this option is selected, the debugger executes the entire method without any step-by-step execution (interruption), and then it steps to the next statement after the method call.

Step Out

Shift+F11

Use this option inside a method call to execute the rest of the method without stepping, and you resume step execution mode when control returns to the calling method.

Breakpoints are markers in code that signal the debugger to pause execution. When the debugger pauses at a breakpoint, you can take your time to analyze variables, data records, and other settings in the environment to determine the state of the program. You can also choose to execute the program in step mode from this point onward.

If you have placed a breakpoint in a button's Click event handler, the program pauses when you click the button and the execution reaches the breakpoint. You can then step through the execution for the rest of the event handler. When the execution of the event handler code finishes, the control is transferred back to the form. If you have another button on the form for which a breakpoint is not set in the event handler, then the program is no longer under step execution. You should mark breakpoints at all the places you would like execution to pause.

Step-by-Step 9.7 shows you how to set breakpoints and perform step-by-step execution using Visual Studio .NET.

STEP BY STEP

9.7 Setting Breakpoints and Performing Step-By-Step Execution

  1. Add a new Visual C# Windows application named StepByStep9_7 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 9_7. Switch to the code view and change the namespace of the form to StepByStep9_7. Delete the default Form1.cs.

  3. Add the following method to the class:

    
    private int Factorial(int intNumber)
    
    {
    
        int intFac = 1;
    
        for (int i = 2; i <= intNumber; i++)
    
        {
    
            intFac = intFac * i;
    
        }
    
        return intFac;
    
    }
    
    
  4. Modify the Click event handler of btnCalculate so that it looks like this:

    
    private void btnCalculate_Click(object sender,
    
       System.EventArgs e)
    
    {
    
        int intNumber, intFactorial;
    
        try
    
        {
    
            intNumber = Convert.ToInt32(txtNumber.Text);
    
            intFactorial = Factorial(intNumber);
    
            txtFactorial.Text = intFactorial.ToString();
    
        }
    
        catch(Exception ex)
    
        {
    
            Debug.WriteLine(ex.Message);
    
        }
    
    }
    
    
  5. In the event handler added in step 4, right-click the beginning of the line that makes a call to the Factorial() method and select Insert Breakpoint from the context menu. Note that the line of code is highlighted with red and also that a red dot appears on the left margin, as shown in Figure. You can alternatively create a breakpoint by clicking on the left margin adjacent to a line.

    15. You can enter step-by-step execution mode by setting a breakpoint in a program.

    graphics/09fig15.jpg

  6. Set project StepByStep9_7 as the startup project.

  7. Run the solution. Enter a value in the text box and click the Calculate button. Note that execution pauses at the location where you have marked the breakpoint. You should see an arrow on the left margin of the code as shown in Figure. This arrow indicates the next statement to be executed.

    16. When the breakpoint is reached during execution, the execution of the program pauses at that point.

    graphics/09fig16.jpg

  8. Press the F11 key to proceed to step into the code of the Factorial() method. Hover the cursor over the various varibles in the Factorial() method to see the current values of these variables.

  9. Select Debug, Windows, Breakpoints. The Breakpoints window appears, as shown in Figure. Right-click the breakpoint listed in the window and select Go To Disassembly. The Disassembly window appears, showing the program's object code along with the disassembled source code.

    17. The Breakpoints window gives you convenient access to all breakpoint-related tasks in one place.

    graphics/09fig17.jpg

  10. Close the Disassembly window. Select Debug, Step Out to automatically execute the rest of the Factorial() method and again start the step mode in the event handler at the next statement. Step through the execution until you see the form again.

  11. Select Debug, Stop Debugging. The debugging session ends and the application is terminated.

    NOTE

    The Disassembly Window Shows Native Code Instead of MSIL Although C# programs are compiled to Microsoft Intermediate Language (MSIL), they are just-in-time compiled to native code only at the time of their first execution. This means the executing code is never in IL; it is always in native code. Thus, you will always see native code instead of IL in the Disassembly window.

    Disabling Versus Removing a Breakpoint When you remove a breakpoint, you loose all the information related to it. Instead of removing a breakpoint, you can choose to disable it. Disabling a breakpoint does not pause the program at the point of the breakpoint, but Visual C# .NET will remember the breakpoint settings. At any time, you can select Enable Breakpoint to reactivate the breakpoint.

  12. In the code view, right-click the statement where you have set the breakpoint and select Disable Breakpoint from the context menu.

To set advanced options in a breakpoint, you can choose to create a new breakpoint by selecting New from the context menu of the code or from the toolbar in the Breakpoints window. The New Breakpoint dialog box (see Figure) has four tabs. You can use these tabs to set a breakpoint in a function, in a file, at an address in the object code, and when a data value (that is, the value of a variable) changes.

18. The New Breakpoint dialog box enables you to create a new breakpoint.

graphics/09fig18.jpg

Clicking the Condition button opens the Breakpoint Condition dialog box, as shown in Figure. The Breakpoint Condition dialog box enables you to set a breakpoint based on the runtime value of an expression.

19. The Breakpoint Condition dialog box allows you to set a breakpoint that is based on the value of an expression at runtime.

graphics/09fig19.jpg

Clicking the Hit Count button opens the Breakpoint Hit Count dialog box, as shown in Figure. This dialog box enables you to break the program execution only if the specified breakpoint has been hit a given number of times. This can be especially helpful if you have a breakpoint inside a lengthy loop and you want to step-execute the program only near the end of the loop.

20. The Breakpoint Hit Count dialog box enables you to break the program execution only if the specified breakpoint has been hit a specified number of times.

graphics/09fig20.jpg

Analyzing Program State to Resolve Errors

When you break the execution of a program, the program is at a particular state in its execution cycle. You can use various debugging tools to analyze the values of variables, the results of expressions, the path of execution, and so on, to help identify the cause of the error that you are debugging.

Step-by-Step 9.8 demonstrates various Visual C# .NET debugging tools, such as the Watch, Autos, Locals, This, Immediate, Output, and Call Stack windows.

STEP BY STEP

9.8 Analyzing Program State to Resolve Errors

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

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

  3. Change the code in the Factorial() method to the following:

    
    private int Factorial(int intNumber)
    
    {
    
        int intFac = 1;
    
        for (int i = 2; i < intNumber; i++)
    
        {
    
            intFac = intFac * i;
    
        }
    
        return intFac;
    
    }
    
    

    Note in this code that I have introduced a logical error that I will later "discover" through debugging.

  4. Set project StepByStep9_8 as the startup project.

  5. Run the program. Enter the value 5 in the text box and click the Calculate button. You should see that the result is not correct; this program needs to be debugged.

  6. Set a breakpoint in the Click event handler of btnCalculate at the line where a call to the Factorial() method is being made. Execute the program. Enter the value 5 again, and click the Calculate button.

  7. Press the F11 key to step into the Factorial() method. Select Debug, Windows, Watch, Watch1 to add a watch window. Similarly, select the Debug, Windows menu and add the Locals, Autos, This, Immediate, Output, and Call Stack windows. Pin down the windows so that they always remain in view and are easy to watch as you step through the program.

  8. Look at the Call Stack window shown in Figure. It shows the method call stack, giving you information about the path taken by the code to reach its current point of execution. The currently executing method is at the top of the stack, as indicated by a yellow arrow. When this method is finished executing, the next entry in the stack will be the method receiving the control of execution.

    21. The Call Stack window enables you to view the names of methods on the call stack, the parameter types, and their values.

    graphics/09fig21.jpg

  9. Look at the This window, shown in Figure. In the This window you can examine the members associated with the current object (the Factorial form). You can scroll down to find the txtNumber object. You can change the values of these objects here. At this point, however, you don't need to change any values.

    22. The This window enables you to examine the members associated with the current object.

    graphics/09fig22.jpg

  10. Activate the Autos window, which is shown in Figure. The Autos window displays the variables used in the current statement and the previous statement. The debugger determines this information for you automatically; that is why the name of this window is Autos.

    23. The Autos window displays the variables used in the current statement and the previous statement.

    graphics/09fig23.jpg

  11. Invoke the Locals window, which is shown in Figure. The Locals window displays the variables that are local to the current context (that is, the current method under execution) with their current values. Figure shows the local variables in the Factorial() method.

    24. The Locals window displays the variables local to the current method under execution.

    graphics/09fig24.jpg

  12. Invoke the Immediate window. Type intNumber in the Immediate window and press Enter. The Immediate window immediately evaluates and displays the current value of this variable in the next line. Now type the expression Factorial(intNumber). The Immediate window calls the Factorial() method for a given value and prints the result. The Immediate window can therefore be used to print values of variables and expressions while you are debugging a program.

  13. Invoke the Watch 1 window. The Watch window enables you to evaluate variables and expressions. Select the variable intFac from the code and drag and drop it in the Watch 1 window. You can also double-click the next available row and add a variable to it. Add the variables i and intNumber to the Watch 1 window, as shown in Figure.

    26. The Watch window enables you to evaluate variables and expressions.

    graphics/09fig26.jpg

    NOTE

    Two Modes of the Command Window The command window has two modes: the command mode and the immediate mode. When you select View, Other Windows, Command Window, the Command window is invoked in the command mode. You can distinctly identify the command mode because in this mode the Command window shows the > prompt (see Figure). You can use the command mode to evaluate expressions or to issue commands such as Edit to edit text in a file. You can also use regular expressions with the Edit command to make editing operations quick and effective.

    25. The Command window can appear in two modes: the command mode and the immediate mode.

    graphics/09fig25.jpg

    On the other hand, when you invoke the Command window by selecting Debug, Window, Immediate, it opens in the immediate mode. You can use the immediate mode to evaluate expressions in the currently debugged program. The immediate mode does not show any prompt (see Figure). You can switch from immediate mode to command mode by typing >cmd, and you can switch from command mode to immediate mode by typing immed in the Command window.

  14. Step through the execution of the program by pressing the F11 key. Keep observing the way values change in the Watch 1 (or Autos or Locals) window. After a few steps, the method terminates. Note that the program executed only until the value of i was 4 and that the loop was not iterated back when the value of i was 5. This causes the incorrect output in the program.

  15. Change the condition in the for loop to use the <= operator instead of < and press F11 to step through. The Unable to Apply Code Changes dialog box appears, as shown in Figure. This dialog box appears because after you have identified the problem and corrected the code, the source code is different from the compiled version of the program. If you choose to continue at this stage, your source code and program in execution are different, and that might mislead you. I recommend that you always restart execution in this case by clicking the Restart button. The code is then recompiled, and the program is started again.

    27. The Unable to Apply Code Changes dialog box appears if you edit code and then try to continue execution.

    graphics/09fig27.jpg

  16. Enter the value 5 in the text box and click the Continue button. The program breaks into the debugger again because the breakpoint is still active. Step through the program and watch the values of the variables. The loop is executed the correct number of times, and you get the correct factorial value.

Debugging on Exceptions

You can control the way the debugger behaves when it encounters a line of code that throws an exception. You can control this behavior through the Exceptions dialog box, which is shown in Figure and is invoked by selecting Debug, Exceptions. The Exceptions dialog box allows you to control the debugger's behavior for each type of exception defined on the system. In fact, if you have defined your own exceptions, you can also add them to this dialog box.

28. The Exceptions dialog box enables you to control the debugger's behavior for system and custom-defined exceptions.

graphics/09fig28.jpg

There are two levels at which you can control the behavior of the debugger when it encounters exceptions:

  • When the exception is thrown— You can instruct the debugger to either continue or break the execution of the program when an exception is thrown. The default setting for Common Language Runtime (CLR) exceptions is to continue the execution, possibly in anticipation that there will be an exception handler.

  • If the exception is not handled— If the program you are debugging fails to handle an exception, you can instruct the debugger to either ignore it and continue or to break the program's execution. The default setting for CLR exceptions is to break the execution, warning the programmer of the possibly problematic situation.

NOTE

Support for Cross-Language Debugging Visual Studio .NET supports debugging of projects that contain managed code written in several languages. The debugger can transparently step into and out of languages, making the debugging process smooth for you as a developer. Visual Studio .NET also extends this support to unmanaged code, but with minor limitations.


GUIDED PRACTICE EXERCISE 9.2

The Factorial Calculator program created in Step-by-Step 9.4 throws exceptions of type System.FormatException and System.OverflowException when users are not careful about the numbers they enter.

The later versions of this program (created in Step-by-Steps 9.7 and 9.8) catch the exception to prevent users from complaining about the annoying exception messages.

The goal of this exercise is to configure the debugger in Step-by-Step 9.8 so that when the reported exception occurs, you get an opportunity to analyze the program.

How would you configure the debugger?

In this exercise you will practice configuring the exception handling for the Visual Studio .NET debugger environment. 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. Open the Windows application project StepByStep9_8.

  2. Activate the Exceptions dialog box by selecting Debug, Exceptions.

  3. In the Exceptions dialog box, click the Find button. Enter System.FormatException and click the OK button. You are quickly taken to the desired exception in the exception tree view.

  4. Select Break into the Debugger from the When the Exception Is Thrown group box.

  5. Repeat steps 3 and 4 for System.OverFlowException.

  6. Run the solution. Enter a nonnumeric value for which to find the factorial. This causes a System.FormatException error, and the debugger prompts you to either break or continue the execution. Select to break. You can see the values of various variables at this stage either by moving the mouse pointer over them or by adding the variables to the Watch window. On the next execution of the program, enter a very large value. This causes a System.OverFlowException error. Select to break when prompted by the debugger, and then analyze the values of the various variables.

If you have difficulty following this exercise, review the section "Debugging on Exceptions" and "Setting Breakpoints and Stepping Through Program Execution." After doing that review, try this exercise again.

REVIEW BREAK

  • Debugging is the process of finding the causes of errors in a program, locating the lines of code that are causing the error, and fixing the errors.

  • The three options available while performing step-by-step execution are Step Into, Step Over, and Step Out.

  • Breakpoints enable you to mark code that signals the debugger to pause execution. After you encounter a breakpoint, you can choose to continue step-by-step execution or resume the normal execution by pressing F5 or by clicking the Resume button or the Continue button.

  • The various tool windows, such as This, Locals, Immediate, Autos, Watch, and Call Stack, can be of great help in tracking the execution path and the status of variables in the process of debugging an application in Visual Studio .NET.

  • When an application throws an exception, you can choose to either continue execution or break into the debugger (to start debugging operations such as step-by-step execution). You can customize this behavior for each exception object by using the Exceptions dialog box.

Debugging a Running Process

Until this point in the chapter, you have seen how to debug programs only by starting them from the Visual Studio .NET environment. However, Visual Studio .NET also allows you to debug processes that are running outside the Visual Studio .NET debugging environment. This feature can be quite helpful for debugging already-deployed applications.

To access external processes from Visual Studio .NET, you need to invoke the Processes dialog box, shown in Figure. You can do this in two ways:

  • When you have a solution open in Visual Studio .NET, you can invoke the Processes dialog box by selecting Debug, Processes.

  • When no solution is open in Visual Studio .NET, you don't see any Debug menu, but you can still invoke the Processes dialog box by selecting Tools, Debug Processes.

29. The Processes dialog box enables you to attach a debugger to a process that is under execution.

graphics/09fig29.jpg

In the Processes dialog box, select the process that needs to be debugged in the Available Processes section and then click the Attach button to attach the process to the debugger. You can then open the source code files in Visual Studio .NET and place breakpoints in the code. Visual Studio .NET debugger breaks the execution of the process when the breakpoint is reached. You can invoke various debugging windows, such as the Watch, Locals, and Autos windows, to analyze variables and step through the program execution.

Step-by-Step 9.9 demonstrates how to attach the debugger to a process that is being executed.

STEP BY STEP

9.9 Attaching the Debugger to a Process That Is Being Executed

  1. Using Windows Explorer, navigate to the bin\Debug folder inside the project folder for StepByStep9_8. Double-click the StepByStep9_8.exe file to launch the program.

  2. Start a new instance of Visual Studio .NET and select Tools, Debug Processes. The Processes dialog box appears, as shown in Figure. You may have a different process list from what is shown in the figure.

  3. Select the process named StepByStep9_8.exe in the Available Processes section and click the Attach button. This invokes an Attach to Process dialog box, as shown in Figure. Select the Common Language Runtime as the program type and keep all other options unchecked. Make sure you check the Show system processes option to view even the system processes on the machine. Click the OK button. You should now see the selected process in the Debugged Processes section of the Processes dialog box.

    30. The Attach to Process dialog box allows you to attach a debugger to a program that is running in a process outside of Visual Studio .NET.

    graphics/09fig30.jpg

  4. Click the Break button to break into the running process. Click the Close button to close the Processes dialog box for now.

  5. Open the source code file in Visual Studio .NET. Set a breakpoint on the line of code that makes a call to the Factorial() method. Press F11 to step into the program.

  6. Enter the value 5 in the form and click the Calculate button. The debugger breaks the execution when the breakpoint is reached.

  7. Invoke Watch, Locals, Autos window to analyze variables and step through the program execution.

  8. When the factorial result is displayed, invoke the Processes window again by selecting Debug, Processes. From the list of debugged processes, select StepByStep9_8 and click the Detach button.

  9. Click the Close button to close the Processes dialog box. StepByStep9_8.exe is still executing, as it was when you initiated the debugging process.

WARNING

Terminating aspnet_wp.exe The ASP.NET worker process (aspnet_wp.exe) processes requests from all ASP.NET applications. If, after debugging, you choose to terminate the aspnet_wp.exe process, it will affect all Web applications running on the server. You need to be especially careful when selecting this option on a production/shared server.

Don't Debug on a Production Server When you attach a debugger to the ASP.NET worker process aspnet_wp.exe, it freezes the execution of all other Web applications on that server. This might cause undesirable effects on a production server.


However, to debug a deployed or running Web application, or a Web service, you need to attach the Visual Studio .NET debugger to the aspnet_wp.exe process running on the Web server. After this debugging setup is done, the Web programs can be debugged just like any other program.

Debugging a Remote Process

For remote debugging to work, the Machine Debug Manager (mdm.exe) should be running on the remote computer. mdm.exe is a Windows service that provides remote debugging support. If the remote computer has never been set up for remote debugging, you take one of the following steps to do a one-time configuration on the remote machine:

  • Install Visual Studio .NET on the remote machine.

  • Install Remote Components Setup on the remote machine. (You can start this from the Visual Studio .NET Setup Disc 1.)

NOTE

Microsoft CLR Debugger (dbgclr.exe) The .NET Framework provides a tool called Microsoft CLR Debugger (dbgclr.exe). This tool is based on the Visual Studio .NET debugger and has most of the same features. This tool will be especially useful if you are not using Visual Studio .NET for developing your applications and still want all the powerful GUI-based debugging capabilities.


You also must ensure that your user account is a member of the Debugger Users group on the remote machine. If you want to debug an ASP.NET worker process, you must also have administrative privileges (that is, you should be a member of the Administrators group) on the remote machine.

If SQL Server is installed on the remote machine, the setup process just described also configures the machine for SQL Server stored procedures debugging, which is demonstrated at the end of this chapter, in Exercise 9.2.

EXAM TIP

Debugging a Remote Process The local computer and the remote computer must be members of a trusted domain for remote debugging to be possible.

DCOM Error While Debugging Visual Studio .NET uses DCOM to enable remote debugging. If you get a DCOM configuration error while debugging, you didn't set up the remote machine to support remote debugging. To resolve the error make sure that you follow all the steps mentioned in this section.


In addition to the already-mentioned one-time setup of the remote machine, you also need to configure the Visual Studio .NET project on your local machine. To do this you take the following steps:

  1. Set the Enable Remote Debugging option to true in the project's Property Pages dialog box as shown in Figure. The EXE file must be in a shareable directory on the remote machine.

    31. You must set the Enable Remote Debugging option to True to enable remote debugging.

    graphics/09fig31.jpg

  2. Set the Remote Debug Machine option with the name of the machine on which the EXE file will run.

  3. The EXE file must be in a shareable directory on the remote machine. The location of the EXE file on the remote machine must match the value of the Output Path property, which is on the Build property page in the Configuration Properties folder.

After you have completed the required setup, the process of debugging a remote process is almost the same as the process of debugging an already running process. The only difference is that prior to selecting a running process from the Processes dialog box, you need to select the remote machine name from the Processes dialog box (refer to Figure).

Debugging the Code in DLL Files

The process of debugging a DLL file is similar to the process of debugging an EXE file. There is one difference though: The code in the DLL file cannot be directly invoked, so you need to have a calling program that calls various methods/components of the DLL files.

You typically need to take the following steps to debug code in a DLL file:

  1. Launch the program (such as an EXE file, a Web page, a Web service, and so on) that uses the components or methods in the DLL file.

  2. Launch Visual Studio .NET and attach the debugger to the calling program. Set a breakpoint in the calling program where the method in the DLL file is called or in the source code of the DLL file. Continue with the execution.

  3. The execution breaks when the breakpoint is reached. At this point, select Debug, Step Into to step into the DLL file's source code. Execute the code in step mode while you watch the value of its variables.

In addition, if the code files are executing on a remote machine, you need to make sure that the remote machine is set up with remote debugging support, as explained in the previous section.

Debugging Client-Side Scripts

Visual Studio .NET also allows you to debug client-side scripts. The process is similar to the process that I discussed earlier for Step-by-Step 9.9. However, you must note the following additional points for client-side scripting:

  • Client-side debugging works only with Microsoft Internet Explorer.

  • You have to enable script debugging in Internet Explorer. To do this, select Tools, Internet Options, select the Advanced tab, and uncheck the Disable Script Debugging option in the Browsing section.

  • Attach the debugger to the iexplore.exe process displaying the Web form. This is required only if you are debugging an already running process. While attaching the process in the Attach to Process dialog box, make sure that you also select the Script option.

Debugging a Windows Service

In most aspects, debugging a Windows service is like debugging any other application. However, a Windows service runs within the context of the Service Control Manager. Therefore, to debug a Windows service, you must attach a debugger to the process in which the Windows service is running. If the Windows service is not already started, then you need to start the Windows service to perform debugging.

WARNING

Debugging Windows Service Processes When you attach a debugger to a Windows service, the service suspends its processing, but continues to be in the Started state. This may affect the execution of the Windows service. Therefore, you need to be especially careful when selecting this option on a production/shared server.


Step-by-Step 9.10 shows you how to debug the OrderService service created in Step-by-Step 6.1 in Chapter 6, "Windows Services." If you haven't already created this service, you should create it now to follow Step-by-Step 9.10.

STEP BY STEP

9.10 Debugging Windows Services

  1. Open the 320C06 solution in Visual Studio .NET.

  2. Open the OrderService.cs file of the StepByStep6_1 project that contains the code for the Windows service.

  3. Place a breakpoint in the fswOrders_Created() method where the main task of the Windows service is performed.

  4. Make sure that the OrderService is already installed. If not, then install the service by following the steps in Step-by-Step 6.3 in Chapter 6.

  5. Open the Services administrative tool from the Administrative tool section of the Windows Control Panel. Browse through the services to locate the OrderService service. Ensure that the Windows service is running; if not, then start the Windows service.

  6. Select Tools, Debug Processes. The Processes dialog box appears. Select the process named StepByStep6_1.exe (OrderService Windows service) in the Available Processes section and click the Attach button. This invokes an Attach to Process dialog box. Select the Common Language Runtime option. Click the OK button. You should now see the selected process in the Debugged Processes section of the Processes dialog box. Click the Close button to close the Processes dialog box for now.

  7. Create an XML file named Orders.xml and copy this file to the c:\orders directory. (Refer to Step-by-Step 6.3 for more details.)

  8. You'll note that in a few moments the debugger breaks into the fswOrders_Created() method in the OrderService.cs file, where the breakpoint was placed. You can now step into the code of the Windows service and step through the program execution. Invoke the Watch, Locals, and Autos windows to analyze variables and step through the program execution.

  9. When the Orders.xml file is moved from c:\orders to a new subdirectory c:\orders\updated, invoke the Processes dialog box again by selecting Debug, Processes. From the list of debugged processes, select StepByStep6_1 and click the Detach button.

  10. Click the Close button to close the Processes dialog box. Note that the OrderService Windows service (StepByStep6_1.exe) is still running, as it was when you initiated the debugging process.

In Step-by-Step 9.10, you could have placed breakpoints in the OnStop(), OnPause(), OnContinue() methods. You could have then used the Services administrative tool to stop, pause, or continue the Windows service to debug the code in these methods. However, it is not possible to debug the OnStart() method (that starts the service) or the Main() method (that creates the instance of the service) by the process explained in Step-by-Step 9.10 because a Windows service needs to be already started before you can attach a debugger.

To debug the constructor or the Main() method of a Windows service, you place a breakpoint in the Main() method and then run the application from Visual Studio .NET. Remember that this step just allows you to step into the Main() method but does not actually load the Windows service as the Service Control Manager does.

To debug the code in the OnStart() method, you can call the OnStart() method from the Main() method, as shown in the following code:


static void Main()

{

    System.ServiceProcess.ServiceBase[] ServicesToRun;



    // Place a breakpoint in the following line to

    // step into the OnStart() method

    // This line needs to be removed after the debug

    // process completes

    (new OrderService()).OnStart(null);



    ServicesToRun = new

        System.ServiceProcess.ServiceBase[]

        { new OrderService() };



    System.ServiceProcess.ServiceBase.Run(

       ServicesToRun);

}

In these lines of code, a call to the OnStart() method is added just to debug the code in the OnStart() method. After the debug process, you should remove the call to the OnStart() method.

NOTE

Debugging the OnStart() Method When the Service Control Manager starts a Windows service by calling the OnStart() method, it waits just 30 seconds for the OnStart() method to return. If the method does not return in this time, the Service Control Manager shows an error that the service cannot be started.


If a Windows service is executing on a remote machine, you need to make sure that the remote machine is set up with remote debugging support, as explained in the section "Debugging a Remote Process."

Debugging a Serviced Component

A serviced component is stored in a DLL file. The code in the serviced component cannot be directly invoked, so you need to have a client (calling) program that creates the serviced component object and calls various methods of the serviced component.

Therefore to debug a serviced component, you need to take steps similar to those of debugging any DLL file. However, the debugging differs slightly depending on whether the serviced component application is a Library or Server application.

If the serviced component is a Library application, then the serviced component runs in the client application's process. In this case, you can set breakpoints in the serviced component or the client application and run the client application in debug mode. When the breakpoint is reached, you can step into the serviced component's code. In case of an already running client application, you can set breakpoints in the serviced component or the client application and attach a debugger to the client application's process.

On the other hand, if the serviced component is a Server application, then the serviced component runs in a separate process called dllhost.exe. Setting breakpoints in the client code and attaching a debugger to the client application debugs only the client application; it does not step into the code of the serviced component.

In this case, to debug the serviced component you should place breakpoints in the serviced component code and attach a debugger to the dllhost.exe process in which the desired serviced component is running. If multiple COM+ server applications are running on a machine, then multiple dllhost.exe processes will be running on the machine.

You can identify the unique process identifier (PID) of the dllhost.exe that is running your serviced component with the help of the Component Services administrative tool. Drill down to the COM+ Applications node and select View, Status View. You should now be able to view the PID for the dllhost.exe process that hosts the serviced component, as shown in Figure.

32. The Status View of the Component Services administrative tool gives detailed information on the status of the COM+ applications.

graphics/09fig32.jpg

You can use the PID of the dllhost.exe process to attach a debugger to that process via the Processes dialog box, as shown in Figure.

Figure. An Enterprise Services application that has Server activation mode runs in a separate process named dllhost.exe.

graphics/09fig33.jpg

WARNING

Setting the Computer Level Default Timeout When you increase the Transaction timeout value in the My Computer Properties dialog box, the setting affects all the transactions in the computer. Therefore, you should try to reset the default value as soon as possible, so that other applications using transactions are not affected in the computer.


After attaching the process, you can place breakpoints to step into the code of the serviced component just like that of any other component. If you want to debug the client application as well, you should attach a debugger to the client application's process. So in this case, you attach debuggers to two processes: the serviced component's process and the client application's process.

However, note that while debugging, the serviced components involving transactions may raise COMException errors indicating transaction timeout problems. The default computer-level setting for transactions timeout is 60 seconds. While debugging, you may want to increase the transaction timeout value. You can do so by overriding the default settings for your serviced component: Select the Override global transaction timeout value option in the Transactions tab of the serviced component's Properties dialog box, as shown in Figure.

34. You can set the timeout value for the serviced component's transactions in the serviced component's Properties dialog box.

graphics/09fig34.jpg

Note the default value for the timeout if overridden is 0, which means the transactions will never time out. However, you should set a value greater than 0, so that there are no chances of distributed deadlocks.

If you are debugging multiple serviced components that involve transactions, instead of increasing the timeout value for each serviced component, you can choose to increase the computer-level setting of the default timeout value. You can do this by changing the Transaction Timeout value in the Options tab of the My Computer Properties dialog box, as shown in Figure. You can open this dialog box by selecting Properties from the context menu of the My Computer node in the Component Services administrative tool.

35. You can set the timeout value for all the distributed transactions in a computer in the My Computer Properties dialog box.

graphics/09fig35.jpg

If a serviced component is executing on a remote machine, you need to make sure that the remote machine is set up with remote debugging support, as explained in the section "Debugging a Remote Process."

Debugging a .NET Remoting Object

A .NET remoting object is stored in a DLL file, such as a serviced component. A .NET remoting object executes in the process of the remoting server application, without respect to its activation mode.

Therefore, to debug a remoting object, you need to take the following steps:

  1. If the remoting server is running in its own process, attach the debugger to that process.

  2. If the remoting server is hosted in IIS, attach a debugger to the ASP.NET worker process (aspnet_wp.exe).

  3. Set breakpoints in the remoting object class definition.

After taking these steps, the Visual Studio .NET debugger breaks the execution when it reaches the breakpoint in the code.

Attaching a debugger to the process in which the client application is running debugs only the client application and does not step into the code of the remoting object class definition. To debug both client and server applications, you can attach a debugger to both the applications and step seamlessly into the code of both the applications.

If a remoting object is executing on a remote machine, you need to make sure that the remote machine is set up with remote debugging support, as explained in the section "Debugging a Remote Process."

Debugging an XML Web Service

Debugging XML Web services is similar to debugging Web applications. They also run in the ASP.NET worker process (aspnet_wp.exe). The only difference is that, you need to take care of setting breakpoints in the Web methods, after which you can debug Web services in any of the following ways:

  • You can run a Web service from Visual Studio .NET and then step into the code of the Web service by invoking the respective method through the Web service test page.

  • You can also attach a debugger to the aspnet_wp.exe process to step into the code of the already-running Web service.

  • You can create a client application for the Web service, which invokes its Web methods. You can then step into the Web service's code by running the client application from Visual Studio .NET.

  • You can attach a debugger to an already-running client application and then step into the code of the Web service when the code reaches a breakpoint.

You should also make sure that the Web service application is configured for debugging. Please refer to the "Configuring the Debugging Environment" section, discussed earlier in the chapter, for more details. Also, if the Web service is executing on a remote machine, you need to make sure that the remote machine is set up with remote debugging support, as explained in the section "Debugging a Remote Process."

You can also use SOAP extensions of XML Web services for debugging. These SOAP extensions can be used to examine or modify the SOAP messages sent and received by the Web service or the client. Refer to the section "Creating and Using SOAP Extensions" in Chapter 5, "Advanced Web Services," for how to create and use SOAP extensions.

REVIEW BREAK

  • You can attach the debugger to a running process (either local or remote) with the help of the Processes dialog box.

  • When you attach a debugger to the ASP.NET worker process aspnet_wp.exe, it freezes the execution of all other Web applications on that server.

  • You should have mdm.exe running on the remote machine to perform remote debugging.

  • To debug a Windows service, you must attach a debugger to the process in which the Windows service is running.

  • To debug COM+ library applications, you must attach a debugger to the process in which the client application is running.

  • To debug COM+ server applications, you must attach a debugger to the dllhost.exe process in which the COM+ server application is running.

  • To debug a .NET remoting object, you must attach a debugger to the process in which the remoting server application is running. If the object is hosted in IIS, you need to attach a debugger to the aspnet_wp.exe process.

  • To debug an XML Web service, you need to attach a debugger to the aspnet_wp.exe or the client application that is calling Web methods of the Web service.