From the April 2002 issue of MSDN Magazine

MSDN Magazine

Visual Studio .NET, Debugging .NET Applications, and More

Q The Microsoft® .NET Framework lets you build applications without using Visual Studio® .NET or other tools. Visual Basic®, C#, JScript®, and C++ are all included, so shouldn't I just use an ASCII editor like Notepad?

A
This is a really good question. In fact, it reminds me of the early days when the MS-DOS® snobs wouldn't use Windows®. I remember when I switched from MS-DOS to Windows (in the days of Windows 3.0). I discovered that I was much more productive when using Windows because of the easy interface and the tools it provided. This same concept applies to Visual Studio .NET. Because of the rich tools that Visual Studio .NET provides and the other applications that are either part of the program or are available from other vendors, Visual Studio .NET goes far beyond the capabilities of any simple editor.
      Now, let's tackle the question regarding hours of labor. Traditionally, 80 percent of development time is spent on maintenance. Visual Studio .NET allows you to build applications much faster than you can with a text editor. There are templates that enable you to build many application types from scratch. For instance, it takes only a couple of clicks of the mouse to create a new ASP.NET page in Visual Studio .NET.
      Visual Studio .NET also makes data access easy. If you drag a DataAdapter onto a page or other design surface, Visual Studio .NET fires up a wizard to walk you through the creation of the code. If you don't want to use the wizards for data access, write the code once, drag it into the Toolbox, rename it, and you can drag and drop it anywhere you want later.
      Windows-based applications are also much easier to create with Visual Studio .NET. Quite a bit of the code that you would ordinarily have to write by hand is generated automatically when you use Visual Studio .NET to write your code.
      What about mobile applications for Windows CE or other handheld devices? You can use the Mobile Internet Toolkit that plugs into Visual Studio .NET to access features of the .NET Framework for these devices and let Visual Studio .NET do the work for you. Then you can use all of the other standard features of Visual Studio .NET. If you are building applications that run on Windows CE, check out the new .NET Compact Framework and the plug-in for Visual Studio .NET for building applications.
      Suppose you need to add support in your application for Performance Monitor counters. You can spend your time digging through the MSDN® documentation on how the .NET Framework can be used to access them, then research the various counters in Win32®. Or you can use the Server Explorer to find the counter you need (or create your own), then drag and drop it into your application. Then you need to add only one line to increment the counter or a few lines to read the counter in a loop. Server Explorer also allows you to access databases, event logs, Windows Services, and so forth. You'll also find plug-ins such as the one that allows you to add both read and write support for Windows Management Instrumentation (WMI) into your own applications.
      Now think about maintenance. Imagine your entire team of developers uses Visual Studio .NET to build applications. Much of the code will have the same structure. Plus, this core code will be maintainable by future versions of Visual Studio .NET. Even much of the custom code will be the same, or at least have the same structure, because Visual Studio .NET will add the code the same way for all developers. Visual Studio .NET Enterprise includes the Enterprise Templates that can be used to further standardize the applications that are created by the team. You can also create add-ins for Visual Studio .NET to automate common tasks.

Q
When I build my .NET application in Visual Studio .NET should I use the Debug or Release settings? What is the difference between these two? When do I use each?

A
You use the Debug and Release settings to control what happens when you build your application. You should use the Release setting when you're building for production and will not need to debug the application. When you build the application in Release mode, the debug symbols are not baked into the assembly, so you cannot debug it using Visual Studio .NET or other source code debuggers. What's cool is that the code is also optimized during this build operation. And, all calls to Debug class methods in your code are disabled while calls to Trace class methods are left.
      Of course, when you compile in Debug mode, the application can be debugged at the source level and you have all the power of Visual Studio .NET and other source-level debug tools at your disposal (read on for more information). Obviously, code compiled in Debug mode does not perform quite as well as Release-mode code does.

Q
How does the Visual Studio .NET debugger compare to the Visual Basic 6.0 and Visual Studio 6.0 debuggers for applications created using Visual Basic?

A
Debugging Web applications is much easier with Visual Studio .NET for many reasons. For starters, the Debug and Trace classes in the .NET Framework make it much easier to add debug features to your application. You can find out more about these classes in the Bugslayer column in the October 2001 issue of MSDN Magazine.
      The Visual Studio .NET IDE also includes a number of features to make application debugging easier. Many of the features are familiar to developers who have been using either Visual InterDev® 6.0 or Visual Basic 6.0 and previous versions to build and debug applications. It's also interesting to note that Visual InterDev 6.0 and Visual Basic 6.0 each had different debuggers and different debugging features. If you used Visual InterDev 6.0 or Visual Studio 6.0 to debug applications, the Visual Studio .NET debugger will feel more at home than it does to a developer who is used to Visual Basic 6.0. Other features are either new or have been dramatically improved to make the debugging process go smoother.
      One of the most dramatic changes is the single designer interface. Since all the languages and application types are designed and debugged in Visual Studio .NET, you have only one debugger interface to deal with. This interface works across language and application types. For instance, there aren't separate debuggers for Windows-based applications and Web applications.
      At the basic level of Visual Studio .NET debugging, you set breakpoints and debug an application as you did in previous versions. You can set a breakpoint on a line of code by clicking the left margin in the editor, by pressing F9 to add a default breakpoint, or by pressing Ctrl+B to open the New Breakpoint dialog (more on this dialog later). By the way, the keyboard mappings can be set to either standard layouts (such as Default, Visual Basic 6.0, Visual Studio 6.0, Visual C++® 2.0, and Visual C++ 6.0) or you can customize a layout to your favorite keystrokes. The keystrokes I use in this column are the default layout settings, where the debugging keystrokes are somewhat different from Visual Basic 6.0 but similar to Visual InterDev 6.0.
      Now, let's take a look at the debug windows. Like similar windows in Visual Basic 6.0 or Visual InterDev 6.0, they provide a view of what is happening in your application. You will spend most of your time in the Command (Immediate) window. Using it, you can change variables and properties, access private variables and properties, execute methods, dump object contents, view the call stack, and view threads.
      Figure 1 shows a simple debug session for an ASP.NET application. The complete code for this application is shown in Figure 2, which contains a single DataGrid with its default properties. Before running the application, I placed a breakpoint on the line that sets the sSQL variable. Of course, the debugger obligingly stops on this line when I press F5 to run the application. Now, observe the Command window (bottom-right corner) of Figure 1.
      You can see that I typed in "? ssql" to display the contents of the sSQL variable. You can also see that the Autos window is already open on the left side of the interface and it shows the contents of this variable along with the other variables in this code.
      The Command window is useful for executing ad hoc tasks. The command line in the window retains the previous commands you have issued so you can scroll through them using the up and down arrow keys. The window itself is also scrollable, so you can scroll through all of your tests and even copy things to the clipboard or paste code into the command window. You can even right-click in the window and clear the contents using the Clear All command in the popup menu.
      I like the Command window for its interactive nature. For instance, what happens if the DataGrid in this example did not display any data? The problem could lie in the code that accesses the DataGrid or it could be in the code that accesses the database. In this example, I could execute the following command right there in the Command window to determine how many rows were in the DataSet's first table:

  ? ds.tables(0).rows.count

      By the way, there is one feature in Visual Basic 6.0 that is not yet in Visual Studio .NET—"Edit and Continue." This feature allows you to edit a project while in Debug mode, make changes to the project, and continue the debug process. Visual Studio .NET does not support this, but a future release or service pack should. So, how do you work around this? I use two approaches. First, I use the Command window, as I mentioned. I can test code and copy it to the clipboard, then paste it back into the Editor window after the debug process is complete. What if I need to change something in the original code on several lines? I copy that code out of the editor and into Notepad and make my changes there. When I stop debugging, I copy the code back to the editor. This process is not exactly seamless, but it works.
      Now, back to the windows. There are many debug windows in Visual Studio .NET. You can display a particular window from the Debug | Windows menu.
      The Autos window that was shown in Figure 1 allows you to show automatic variables, drill down through objects, change variable and property values, see private member variables, and stay in sync as code executes.
      The Autos window shows the variables used by the current and previous statements. It also provides useful info like the value returned by a function, as well as by any function that you stepped over during tracing. The Autos window stays in sync with your code by showing these variables while the Command window does not synchronize with your code. You can also change variables in the Autos window and perform certain other interactions.
      The Locals window is just like the Autos window, except it shows all of the variables in the current scope instead of just in a specific selection.
      The Call Stack window shows the calling path to the current function, allowing you to determine how the code reached this point (to a certain degree). The Call Stack also works across different languages and across multiple projects.
      The Modules window is new and displays the modules (DLLs) that are loaded on the system. This information includes the address at which the module is loaded, the path to the executable, the version number, and more. You can also see the order in which the DLLs were loaded, which allows you to see what is going on when the application is running.
      The Watch window functions like the Autos window, except you have to load the Watch window with specific variables. In the past, there was a single Watch window you could use to see the contents of variables and interact with them. Now instead of a single Watch window, you have up to four Watch windows in which to place the variables you want to monitor. You can now easily group variables together in a window, then display that Watch window when you need it. You can display up to four watch windows by using hotkeys (the default is Ctrl+Alt + W, 1, 2, 3, 4).
      The Memory window allows you to interrogate particular areas of memory. As with the Watch window, there are four Memory windows available to you.
      One of the coolest features of Visual Basic .NET, particularly for Windows-based applications, is the ability to write an application that has multiple threads of execution. For instance, if you're writing a server application, you can have multiple threads and let the OS spread those threads across multiple processors. Using Visual Studio .NET, you can set breakpoints to debug multithreaded applications just like any other application. Then you can use the Threads window to switch between the various threads and debug them independently.
      You can also use multithreading in Web applications. Since Visual Studio .NET has these powerful and consistent debugging features, you can also debug your multithreaded Web application the same way you debug a desktop application.
      Figure 3 shows the Exceptions window, found at Debug | Exceptions. This window is not so much a debug window as it is a dialog that exposes information and allows you to control certain behaviors. This window shows you the exceptions from the .NET Framework. Each category of exceptions is represented as an expandable tree item. This window is useful for two reasons. First, the intended purpose of the window is to use it to control what happens when a particular exception occurs and is not handled. Second, I find it tremendously useful for discovering particular exceptions. For instance, if you happen to need to set up a Try / Catch block for a particular ADO.NET exception, you can find the exception quickly using this window.

Figure 3 Exceptions Window
Figure 3 Exceptions Window

      Now, let's return to breakpoints. As I mentioned earlier, the breakpoints work almost like they did in previous versions of Visual Studio and Visual Basic. For instance, you can use the Breakpoint Properties window to control how a breakpoint behaves. Let's say you want to monitor a variable named iStuff and you want to break when this variable has been hit 498 times in a loop. You can set a breakpoint on the line in question, then right-click the line and select Breakpoint Properties. Then click the Hit Count button and select the conditions, as shown in Figure 4.

Figure 4 Breakpoint Conditions
Figure 4 Breakpoint Conditions

      Now when I run this application, it will break when the value of iStuff is 497 (the 498th time the breakpoint is hit, starting from zero when incrementing iStuff by 1). I could also have set a particular condition such as i = 498 for this breakpoint. The flexibility of breakpoints makes it much easier to debug complex loops and other complex code structures.
      Let's consider what happens when you debug an application that is running on a desktop or server. Normally, you debug an application when it is running inside of Visual Studio .NET. But suppose you're testing your app outside of the Visual Studio .NET environment and it appears to be having a problem with a calculation. Wouldn't it be great to drop into this process? Well, you can do that with Visual Studio .NET by attaching to the running process and debugging the process.
      To do this, start Visual Studio .NET and select Debug | Processes. In the Processes window, select the application in question and click the Attach button. In the Attach to Process window, you select Common Language Runtime, then click OK. At the bottom of the Processes window, you can control what happens when you stop debugging the process.
      Once you have attached to a process, it becomes the current debug application and you can debug it normally.

Q
How do I debug applications running on a server?

A
Visual Studio .NET includes a tool called DbgClr.exe—a stripped-down version of Visual Studio .NET, primarily consisting of the debug functionality. It is installed in C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\GuiDebug by default when you perform a Visual Studio .NET server-side setup.
      Performing a server-side setup also configures the remote debugging for Visual Studio .NET applications. Once you complete the setup, you must add to the Debugger Users group any users who are allowed to perform remote debugging of server applications. This way, when a developer is debugging an application that calls a Web Service, he can single-step through the client's code and right into the Web Service.

Q
Can I debug stored procedures using Visual Studio .NET?

A
Yes. The stored procedure debugging features have been vastly improved in Visual Studio .NET. In addition, since the same IDE is now used to debug both Web applications and Windows-based applications, they now have the same stored procedure debugging features available.
      The SQL Server™ debugging support is much more extensive now than in the past. You can work with and debug stored procedures (sprocs) in several ways. First, how about trying to eliminate errors in the sprocs as you create them? If you right-click the Stored Procedures folder in Server Explorer, you can select New Stored Procedure and create the sproc in the editor. A new feature in Visual Studio .NET lets you use Query Designer to work with sprocs. Just right-click your new empty sproc, select Insert SQL, and then use Query Designer to lay out the SQL. Query Designer now even works with parameters, so it's easy to use and you can also edit stored procedures in it. Simply open the sproc, right-click the SQL statement, and select Design SQL Block.
      Now, if you need to debug the sproc, you can right-click it in Server Explorer and select Step into Stored Procedure. You can do the same thing in the Stored Procedure editor. This starts the debugger and single-steps through the sproc. You can also set a breakpoint, then debug the calling application to step through it.
      Before using stored procedure debugging, see the MSDN documentation on setting up SQL debugging at Microsoft SQL Server 7.0: Stored Procedure Debugging How-To.
      There are a couple of limitations when debugging sprocs:

  • You cannot use Step Into to go between managed code and T-SQL code.
  • You cannot use Run To Cursor in the callstack window.
  • You cannot use Break while a SQL statement is processing.
  • With SQL Server 7.0, you cannot evaluate the SQL Server version of global variables.
  • Triggers cannot be debugged directly, but are stepped into if called by a sproc.

      However, if you keep these few limitations in mind, your debugging experiences will be smooth sailing all the way.

Send questions and comments for Ken to basics@microsoft.com.

Ken Spencer works for the 32X Tech Corporation (https://www.32X.com), which produces developer courseware. Ken also spends much of his time consulting and teaching.