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.
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 to find and are generally pointed out by compilers themselves . Runtime errors occur in those 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.
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 Basic .NET, Visual Basic .NET, Visual C++ .NET, Managed Extensions for C++, script, and SQL.
Support for debugging both managed and unmanaged applications.
Ability to attach a debugger to a running program outside the Visual Studio .NET environment on a local or remote machine.
Support for end-to-end debugging for distributed applications.
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="vb" debug="true"/>
Applications Will Run Significantly Slower in Debug Mode When debugging is enabled for an application, the compiler will include 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 property is set to True . The Enable ASP.NET Debugging property is found in the Configuration Properties, Debugging option in the project's Property Pages dialog box.
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 property is set to True in the project's Property Pages dialog box.
A common technique for debugging is the step-by-step execution of a program, sometimes called stepping . This systematic execution allows you to track the flow of logic to ensure that the program is following the path of execution that you expect it to follow. If there is a difference, you can immediately identify the location of a problem.
Stepping also gives you an opportunity to monitor its state before and after a statement is executed. This includes checking the values in variables , the records in a database, and other changes in environment. Visual Studio .NET provides you with tools to make these tasks convenient .
The Debug menu provides three options for step execution of a program, as listed in Table 9.6. The Keyboard shortcuts listed in the table correspond to the Visual Basic settings of the Visual Studio 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 Menu Item
Executes the code in step mode; if a method call is encountered , the program execution steps into the code of the function and executes the method in step mode.
Use this key when a method call is encountered and you do not want to step into the method code. When this key is pressed, the debugger will execute an entire method without any step-by-step execution (interruption) and then step to the next statement after the method call.
Use this key inside a method call to execute the rest of the method without stepping and resume step execution mode when the control reaches back to the calling method.
Breakpoints are markers in the code that signal the debugger to pause execution as soon as it encounters one. Once 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 choose to execute the program in step mode from this point on.
If you have placed a breakpoint in the Click event handler of a button, the program will be paused when you click the button and the execution reaches the point where you have marked the breakpoint. You can now step through the execution for the rest of the event handler. Once the handler code is over, control will be transferred back to the form under execution. This time, if the user clicks another button and if you don't have a breakpoint set in its event handler, the program is no longer under step execution. Be sure to insert breakpoints wherever you want execution to pause for debugging.
STEP BY STEP
9.7 Setting Breakpoints and Performing Step-By-Step Execution
Disabling Versus Removing a Breakpoint When you remove a breakpoint, you lose all information related to it. Alternatively, you can choose to disable a breakpoint. Disabling a breakpoint does not pause the program at that point, but Visual Basic .NET will still remember the breakpoint settings. At any time, you can select Enable Breakpoint to reactivate the breakpoint.
In addition to using the method described in Step By Step 9.7, you can set a breakpoint by choosing New Breakpoint from the Debug menu or from the context menu in a module. The New Breakpoint dialog box (see Figure 9.18) has four tabs that allow you to set a breakpoint in a function, in a file, at an address in the object code, or when the data value (that is, the value of a variable) changes.
Clicking the Condition button opens the Breakpoint Condition dialog box, as shown in Figure 9.19. The Breakpoint Condition dialog box allows you to set a breakpoint based on the runtime value of an expression.
Clicking the Hit Count button opens the Breakpoint Hit Count dialog box, as shown in Figure 9.20. 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.
When you break the execution of a program, the program is stuck at a particular state in its execution cycle. You can use various debugging tools to analyze the values of variables, the results of expressions, or the path of execution to help identify the cause of the error that you are debugging.
Step by Step 9.8 demonstrates various Visual Basic .NET debugging tools, such as the Watch, Autos, Locals, Me, Immediate, Output, and the Call Stack windows.
STEP BY STEP
9.8 Analyzing Program State to Resolve Errors
Two Modes of the Command Window The Command window has two modes: the Command mode and the Immediate mode. When you invoke the Command window by selecting View, Other Windows, Command Window, it is invoked in Command mode. In Command mode, the window can be used to issue commands such as Edit to edit the text in the file. You can use regular expressions with the Edit command to make editing operations extremely quick and effective. The Command window shows a > prompt (see Figure 9.25).
On the other hand, when you invoke the Command window by selecting Debug, Window, Immediate, you can use it to evaluate expressions in the currently debugged program. The Immediate mode does not show any prompt. You can switch from Immediate mode to Command mode by typing >cmd , and you can switch from Command mode to Immediate mode by typing the immed command.
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, shown in Figure 9.28, which is invoked via the Debug, Exceptions menu option. The Exceptions dialog box allows you to control the debugger's behavior for each type of exception defined in the system. In fact, if you have defined your own exceptions, you can add them to this dialog box.
There are two levels at which you can control the behavior of the debugger when it encounters exceptions:
If 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 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 that you are debugging fails to handle an exception, you can instruct the debugger to either ignore it and continue or to break the execution of the program. The default setting for common language runtime exceptions is to break the execution, warning the programmer of a possible problematic situation.
Support for Cross-Language Debugging Visual Studio .NET supports debugging projects that contain code written in several managed languages. The debugger can transparently step in and step out from one language to another, making the debugging process smooth for you as a developer. Visual Studio .NET also extends this support to unmanaged languages, 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?
You should try doing this on your own first. If you get stuck, or if you'd like to see one possible solution, follow these steps:
Up to this point, you have only seen examples of debugging programs by starting them from the Visual Studio .NET environment. The Visual Studio .NET debugging environment also allows you to debug processes that are started outside the debugging environment.
To access external processes from Visual Studio .NET, you need to invoke the Processes dialog box, shown in Figure 9.29, which you can do 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, the Debug menu is not displayed. However, you can invoke the Processes dialog box by selecting Tools, Debug Processes.
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. The 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, 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.
SMS 1.0 Based Questions The location of the SMS.INI file was the root of the C:\ drive in SMS version 1.0, which has been changed in version 1.2.
STEP BY STEP
9.9 Attaching the Debugger to a Process That Is Being Executed
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. The server will restart the process as necessary, but session and application state information will be lost.
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.
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.
The process of debugging a remote process is almost the same as debugging an already running process. The only difference is that, prior to selecting a running process from the Processes dialog box, you must select the remote machine name from the Name drop-down list box in the Processes dialog box.
Before you can debug a process remotely, you need to perform a one-time configuration on the remote machine (where the processes are running). You can do this in two ways:
Install Visual Studio .NET on the remote machine.
Install the Remote Components Setup on the remote machine (you can start this from the Visual Studio .NET Setup Disc 1).
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 almost 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.
In addition to this, you must ensure that your user account is a member of the Debugger Users group on the remote machine. If you want to debug the 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.
Debugging a Remote Process The local computer and the remote computer must be members of a trusted domain in order 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.
If SQL Server is installed on the remote machine, the setup process just scribed also configures the machine for SQL Server stored procedures debugging, which is demonstrated at the end of this chapter, in Exercise 9.2.
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 9.29).
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 in order to debug code in a DLL file:
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.
Launch Visual Studio .NET and attach the debugger to the calling program. Set a breakpoint in the calling program in which the method in the DLL file is called or else in the source code of the DLL file. Continue with the execution.
The execution breaks when the breakpoint is reached. At this point, select Debug, Step Into to step into the source code of the DLL file. Execute the code in the 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.
Visual Studio .NET also allows you to debug client-side scripts. The process is similar to the process that I discussed earlier for ASP.NET Web forms. However, you must note the following additional points for client-side scripting:
Client-side debugging only works 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 only required 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.
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, in order 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, you need to start the Windows service to perform debugging.
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 might affect the functionality 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 so that you can complete Step by Step 9.10.
STEP BY STEP
9.10 Debugging Windows Services
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 in order 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 already be started in order to attach a debugger.
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."
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 that 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, the serviced component runs in the process of the client application. 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 code of the serviced component. In the 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, 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 will only debug the client application; it will 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, 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 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 9.31.
You can use the PID of the dllhost.exe process to attach a debugger to that process using the Processes dialog box, as shown in Figure 9.32.
After attaching to the process, you can place breakpoints to step into the code of the serviced component just like 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 processesthe serviced component's process and the client application's process.
However, note that while debugging, serviced components involving transactions might raise COMException errors indicating a transaction timeout problem. The default computer level setting of a transaction timeout is 60 seconds. While debugging, you might want to increase the transaction timeout value. You can do so by overriding the default settings for your serviced component by selecting the Override global transaction timeout value option in the Transactions tab of the serviced component's Properties dialog box, as shown in Figure 9.33.
Note the default value for the timeout if overridden is 0, which means that the transactions will never timeout. In most cases, 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 9.34. You can open this dialog box by selecting Properties from the context menu of the My Computer node in the Component Services administrative tool.
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."
A .NET remoting object is stored in a DLL file in the same manner as a serviced component. A .NET remoting object executes in the process of the remoting server application irrespective of its activation mode.
Therefore, to debug a remoting object, you need to take the following steps:
If the remoting server is running in its own process, attach the debugger to that process.
If the remoting server is hosted in IIS, attach a debugger to the ASP.NET worker process ( aspnet_wp.exe ).
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.
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.
Attaching a debugger to the process in which a Remoting client application is running will only debug the client application and will not step into the code of the remoting object class definition. In order 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 XML Web services is similar to debugging Web applications. They also run in the ASP.NET worker process ( aspnet_wp.exe ). You only need to set breakpoints in the Web methods of the Web service. After setting breakpoints, you can debug Web services in any of the following methods:
You can run a Web service from Visual Studio .NET and then step into the code of the Web service by invoking the methods through the Web service test page.
You can also attach a debugger to the aspnet_wp.exe process to step into the code of an already running Web service.
You can create a client application for the Web service that invokes its Web methods. You can then step into the code of the Web service 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. Refer to "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 to help debug Web Services. 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.