Debugging in Visual Studio .NET
Visual Studio Team
Summary: This paper describes some of the new and improved features of the Microsoft® Visual Studio® .NET debugger, such as cross-language debugging, debugging multiple processes across machines, and the generation of minidumps for C++ applications. (7 printed pages)
Debugging Across Languages
Debugging Multiple Processes Across Machines
Debugging Visual C++ Applications
Remote Debugging Components Install
Visual Studio .NET is a powerful development environment that supports the development of a variety of applications, ranging from ATL Server and ASP.NET Web applications to desktop applications written in Visual Basic® .NET. One element of Visual Studio .NET is a powerful debugger, which enables debugging all these scenarios.
Visual Studio .NET achieves three high-level goals for debugging:
- A single user interface for all the languages.
If you are a developer who programs in multiple languages, you do not have to change your environment or keystrokes when switching languages. All debugging commands, the placement of debugging windows, and to a large extent, your debugging feature set, remain the same across different languages. In addition, the single user interface helps you work familiarly, more quickly, and more simply within the environment.
- The ability to debug multiple processes across multiple machines at the same time.
This is tremendously useful when debugging client server applications where your client might be installed on one machine, but talk to a Web server on a separate machine. With Visual Studio .NET, you can debug across both machines at the same time and analyze the execution of your application.
- Powerful cross debugging between separate languages.
If you are a Visual Basic .NET programmer who uses components written in C#, you can step between each language easily when debugging. In fact, you can step back and forth between any language that targets the common language runtime. In addition, Visual Studio .NET allows you to step from .NET Framework applications into Visual C++® code and vice versa. This is useful if you are interacting with existing COM components. Furthermore, you can also debug into stored procedures called by your code in Visual Studio .NET.
A powerful feature of Visual Studio .NET is its ability to debug across languages that target the common language runtime, and across execution environments. For example, if you write a Visual Basic .NET component that is called by a C# component that is in turn called by COBOL code (that targets the runtime), you can seamlessly step between languages when debugging. You can also see a single callstack that shows the different functions called in the languages you just stepped through.
You can also step between C++ code to any .NET Framework language and vice versa. Code written for .NET-based applications can work with C++ code through Platform Invoke, COM Interoperability, or through Managed Extensions for C++. You can seamlessly step into and debug from one language to the other, and again, the debugger will have a single callstack showing all the components. To enable debugging between the common language runtime code and C++ code running natively, you need to change an option in your launching project settings. In Solution Explorer, locate your startup project (this project's name will be bolded). This is the project that gets launched when you press F5. To debug both runtime code and native code, the startup project has to be configured to support both types of debugging. To do this, open your project properties and select the Debugging folder under Configuration Properties.
- For Visual Basic .NET or Visual C#™ projects:
Make sure the Enable Unmanaged Debugging option is selected. In Visual Basic, this option is selected via a checkbox. In Visual C# this is a drop-down list option.
- For Visual C++ projects:
Change the debugger type to Mixed.
Note The Auto option, another option, requires some explanation. It examines your executable file and attempts to determine whether the application will call both native and managed code. Based on the results of the analysis, the appropriate debuggers are used during debugging. A disadvantage of this approach is that if a project was written with managed extensions to C++ and dynamically loaded native code, this will not be detected and you will not be able to debug the native code. Setting Mixed allows a developer to directly control the debugging options.
Alternatively, you can also attach to a running process with both the C++ and runtime debuggers. When you attach to a process using the Processes dialog box, select both the Common Language Runtime and Native debugger options. Doing so allows you to step back and forth between native and runtime code.
Not only does the Visual Studio .NET debugger allow you to debug multiple processes at the same time, it also allows you to debug processes running on multiple machines simultaneously. With Visual Studio .NET, you can launch multiple processes and debug them at the same time. To do this, add multiple projects to your solution. In your solution properties, set the Multiple Startup projects option so that all of your projects launch when a user presses F5. This will have the effect of launching all the processes under the debugger, allowing you to debug all the processes at the same time.
Alternatively, you can use the Processes dialog box to specifically attach to processes on different machines and debug them simultaneously. Remember that you need to have debugging components installed on the remote machine, and you need to have appropriate privileges to be able to debug the processes.
There are two ways you can discover at any time what processes you are currently debugging:
- The Processes dialog box
Click Processes on the Debug menu to open the Processes dialog box. Its bottom pane shows you what processes you are currently debugging.
- The Debug Location toolbar
This new toolbar to Visual Studio .NET allows you to quickly see the programs you are attached to, the threads inside each program as well as each thread's stack frame. This toolbar can also be used as a quick alternative to opening up the callstack and threads window.
ASP.NET is a unified Web development platform, which provides a new programming model and framework for developing stable, secure and scalable applications. Visual Studio .NET allows you to develop ASP.NET applications simply and easily. The environment is designed to make your Web development experience the same as developing client forms-based applications. Therefore, pressing F5 on a Web application works exactly like a forms-based application.
Web Service Debugging
Both Web applications and Web services can be easily debugged with Visual Studio .NET. By default, pressing F5 within a Web services project will display a simple test page, through which you can call the Web services you have developed. Debugging this code is simple. Just set your breakpoints in your Web service code and invoke the method through the test page. Your breakpoints will be hit as the Web method is invoked.
You can also step into an ASP.NET Web service from an application written for the runtime. To do this, make sure you have added the account used to run ASP.NET (typically ASPNET) to the Debugger Users group on your machine. Then start debugging the Web or client application. When you reach the line that makes the call into the Web service, step into the function call using the Step Into command in the debugger. In the background, the debugger will automatically step into the Web service for you.
Detaching in ASP.NET
The common language runtime allows you to detach from a process without killing it. When debugging ASP.NET this is especially useful because you can choose to detach from it and not lose any state information it might have had. You can detach from a process without killing it by clicking the Stop Debugging command on the Debug menu. By default, when you stop debugging an ASP.NET project, the debugger detaches from the process but does not terminate it.
While much work has gone into having the debugger debug runtime code, Visual Studio .NET also contains improvements in the C++ debugger. Visual Studio .NET now supports generating minidumps for C++ applications, and opening up their corresponding dump (.dmp) files.
A mini-dump is essentially a light snapshot of an executable that can be sent to a developer to diagnose a problem. For more information on dumps, see the Platform SDK documentation. To save your application while debugging as a minidump, from the Debug menu select the Save Dump As command, and then type in a file name for the dump. (You can change the Save as type drop-down list to save the dump with heap information as well, though this creates a bigger file). This dump encapsulates the state of the application and can be opened and debugged on another developer's machine. The developer can open this dump file in Visual Studio .NET and view the original state of the program. The minidump stores the build path of the executable, so if the minidump is opened on a machine that the project was built on, it will automatically show you your source code and state.
If the developer opens the dump on a machine that doesn't have the binaries and .pdb files, he or she can still open the dump. He or she can either put the binaries and the .pdb files, next to the .dmp file, or he or she can specify them in the Command Arguments grid of the Property Pages dialog box. The syntax should look like this:
When a dump is loaded, the MODPATH argument specified in the Command Arguments points the debugger to the location of the binaries and .pdb files of the program.
Once the module paths are defined, all the developer needs to do is press F5 on his or her project, and the state of the program as you had it on your machine will be displayed. He or she will not be able to continue execution, but will be able to examine its state, and view callstacks to figure out any problems.
No More Additional DLLs
Another new feature improvement is that now you do not need to specify additional DLLs to set breakpoints in DLLs on processes running on your local or remote machine. Visual Studio .NET now dynamically binds the breakpoints to the DLL as soon as it detects that the DLL loads. All you need to do is press F9, or set a breakpoint in the code you want to debug, and the debugger binds it when the DLL loads.
Note When you add the breakpoint, you might see a question mark in it. This is a warning breakpoint. It means that the code at that location has not been loaded. When the code is loaded in the future, as it is with DLLs, this breakpoint will become solid.
Detaching from a Process
In the past, Visual C++ applications that were being debugged were terminated when you chose to stop debugging. You can now detach the native debugger from applications from Visual Studio .NET loaded on Microsoft® Windows® XP. On Microsoft® Windows NT® 4.0 and Microsoft® Windows® 2000 operating systems, you can have this behavior as well, but you need to start a service on your machine to achieve it. From the Services dialog box, find and start the Visual Studio Debugger Proxy Service. Once this service has started, attach or launch your process. You can then detach from native processes on these operating systems.
Stepping Into Specific Functions
Another cool, albeit hidden, feature of the debugger is the ability to step into a specific function. When you are at a line with multiple nested functions, or a function call, that will cause you to step into a constructor, right-click on the line, and select Step Into Specific from the shortcut menu. This will enable you to select which specific function you want to step into. You can thus avoid stepping into constructor code you don't care about, and instead go directly to the function you want to debug.
Figure 1. Step Into Specific
One of the lesser-known features of Visual Studio is its ability to debug stored procedures, functions, and triggers in SQL Server 7.0 and 2000. This is a feature of the Enterprise edition of Visual Studio .NET.
There are two ways to debug TSQL routines:
- Through Server Explorer
Connect to the database through Server Explorer. Open the stored procedure you want, right–click, and select the Step Into command. Voila! You should step into the stored procedure and be able to see it execute as it goes.
- By hitting breakpoints in stored procedures called from an application
Another technique is to debug your stored procedure as it is called through a client application. To do this, you need to enable SQL debugging in your client project that calls the stored procedures. This is an option you set in your project properties under the Debugging node in Configuration Properties. Once the application has started, open the TSQL routine code in Server Explorer, and set a breakpoint in it. Press F5 on your application to hit the breakpoint in Server Explorer. This method of debugging also works for Web applications that call SQL Server stored procedures.
Visual Studio .NET comes with a powerful automation model. This model allows you to write powerful macros that automate your daily tasks in the environment. The Visual Studio .NET debugger also exposes automation interfaces that let you programmatically access various pieces of debugging information, such as the current running program, its callstacks, threads, and so on. Visual Studio .NET includes a set of sample macros that can be seen by opening the Samples node in the Macro Explorer tree. Under the VSDebugger samples you will see several samples that use the Debugger Automation model. One of my favorites is DumpStacks — a sample that can be used to get the current callstacks associated with all threads in our application. This information is particularly useful in bug reports.
Frequently, users want to remotely debug an application running on a separate machine. Visual Studio .NET provides a smaller subset of components that, when installed on a remote machine, and once you give yourself sufficient privileges, allows you to remotely debug processes on that machine. This installation is extremely useful when you do not want to install the entire Visual Studio .NET onto a remote machine. The installation of these components is started with the Remote Components Setup link at the bottom of the Visual Studio .NET Setup page of the CD. Clicking this link allows you to install remote debugging components for all languages that use DCOM to communicate with the debugger, or just C++ only (if you prefer the Visual C++ 6.0 method of remote debugging over TCP/IP).
This is a short list of new debugging features offered in Visual Studio .NET. There are numerous other productivity features discussed in detail in the documentation provided in Visual Studio .NET. We hope you have a great experience using the Visual Studio .NET debugger when developing your next killer application.