Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles
This article presents an overview of the motivation behind new techniques that decompose problems into independent pieces for optimal use of parallel programming.

By David Callahan (October 2008)
We take a look at planned support for parallel programming for both managed and native code in the next version of Visual Studio.

By Stephen Toub and Hazim Shafi (October 2008)
Here we describe some of the more common challenges to concurrent programming and present advice for coping with them in your software.

By Joe Duffy (October 2008)
Here is an ASP.NET AJAX data-driven Web application that takes the best features from server- and client-side programming to deliver an efficient, user-friendly experience.

By Bertrand Le Roy (October 2008)
More ...
Articles by this Author
Bloated view state can be a real performance bottleneck for your Web app, but it can be difficult to diagnose. John Robbins creates a handy tool that records and reports the view state size for pages in your ASP.NET applications.

By John Robbins (November 2007)
Windows Vista has a new API called Wait Chain Traversal (WCT), which allows you to determine when and why a process is deadlocked. Read on.

By John Robbins (July 2007)


By John Robbins (March 2007)
This installment of Bugslayer covers the use of ADPlus to create a minidump of your Microsoft .NET Framework 2.0 pro¬cesses on specific exceptions.

By John Robbins (November 2006)
The latest releases of WinDBG and Visual Studio know exactly how to use source server, so its benefits are available to both .NET and native C++ developers. See why this is so important in tracking down bugs.

By John Robbins (August 2006)
Visual Studio 2005 brought so many new features to the table that it can seem almost overwhelming. One of the most exciting additions is the new unit testing features found in the Test menu on the main menu bar.

By John Robbins (March 2006)
Those of you who have been reading this old Bugslayer column over the last nine years have branded into your frontal lobe a single word: ASSERT! Anytime you can have the code tell you about a problem instead of having to find it by slaving away with a debugger is a huge timesaver.

By John Robbins (November 2005)
By now, you've certainly heard about the big changes coming in Visual Studio® 2005, but when it's time to move your code over it will be the small things that trip you up. In this column, I want to cover two of the many excellent changes that you could easily overlook as you make the move to the new runtime and development tools.

By John Robbins (July 2005)
More ...
Popular Articles
We build a Silverlight 2.0 application using the InkPresenter to let users annotate a pre-defined collection of images, perform handwriting recognition, and save the annotations and recognized text into a server-side database.

By Julia Lerman (August 2008)
See how to build a document-level Visual Studio Tools for Office customization and integrate it with a content type in SharePoint.

By Steve Fox (May 2008)
The goal of the ADO.NET Data Services Framework is to create a simple REST-based framework for exposing and consuming data-centric services easily.

By Elisa Flasko and Mike Flasko (August 2008)
Learn how you can peer-enable business applications by allowing them to share state in a serverless peer network.

By Kevin Hoffman (July 2008)
More ...
Read the Blog
Well designed code keeps things that have to change together as close together in the code as possible and allows unrelated things in the code to change independently, while minimizing duplication in the code. In the October 2008 issue of MSDN Magazine, Jeremy Miller shows you some design ...
Read more!
The process for ink capture and analysis on the Tablet PC is straightforward in managed code. To the uninitiated developer, however, creating unmanaged Tablet PC applications can be rather daunting. In the October 2008 issue of MSDN Magazine, Gus Class a quick introduction to the Tablet PC ...
Read more!
Multicore systems are becoming increasingly prevalent, but the majority of software today will not automatically take advantage of this additional processing ability. And multithreaded programming, for anything but the most trivial of systems, is incredibly difficult and error prone today. In the October 2008 issue of MSDN ...
Read more!
Concurrent programming is notoriously difficult, even for experts. You have all of the correctness and security challenges of sequential programs plus all of the difficulties of parallelism and concurrent access to shared resources. In the October 2008 issue of MSDN Magazine, David Callahan describes ...
Read more!
A major advantage of AJAX and Silverlight applications is that they can transparently and continuously interact with a back-end service. The problem is that they run over HTTP, which wasn't designed with security in mind. In the September 2008 issue of MSDN Magazine, Dino Esposito shows you ...
Read more!
Unhandled exception processing shouldn't be a mystery. It's actually quite useful since it gives a crashing application an opportunity to perform last-minute diagnostic logging about what went wrong. In the September 2008 issue of MSDN Magazine, Gaurav Khanna discusses how ...
Read more!
More ...
From the January 2002 issue of MSDN Magazine
MSDN Magazine
Extending the Visual Studio .NET IDE
John Robbins
Download the code for this article: Bugslayer0201.exe (115KB)
Browse the code for this article at Code Center: SuperSaver Utility

E
ven though Microsoft works very hard to think of everything that developers want in an IDE, they don't always succeed. That's not a slight against the hard-working IDE developers, but when people are as opinionated and strong-willed as developers tend to be, you can't please all of them all the time. While developers might be opinionated, they're certainly not afraid to add any missing functionality themselves. Since Visual C++® 1.0, the extensibility of the IDE has been improving gradually, but in my opinion has never been strong enough. Because I have worked on tools to integrate with the IDE, I have been inundated with e-mail over the years from developers with ideas about how to fix limitations or add completely new functionality to it. When developers realized that integrating with previous IDEs required heroic feats of reverse engineering and hacking, it thwarted their dreams of IDE improvements.
      Fortunately, Microsoft has heard the pleas of developers, and designed Visual Studio® .NET to offer an extensibility model that should meet the needs of nearly anyone who needs to create an add-in. At first, when I took a cursory glance at the add-in extensibility model, I didn't think much had changed. That was partly because some of the documentation in the Beta 2 release is missing and partly because Beta 2 did not ship with any add-in samples. However, when I took a hard look at everything, I was very happily surprised to see how much a paragon of extensibility the new Visual Studio .NET IDE has become.
      In this month's Bugslayer I will cover some of the capabilities of the new extensibility model. To get started, I will briefly cover some of the features that never existed before in the IDE. One of the biggest stumbling blocks with developing add-ins is the actual add-in Wizard itself. In order to spare you the pain I went through, I'll show you how to fix the Add-in Wizard-generated code so it works properly. Finally, since MSDN® Magazine always includes code, I'll discuss the SuperSaver add-in I wrote for this month's column.
      The first thing SuperSaver does is add a save command that strips off trailing white space from source files, a feature inexplicably missing from Visual Studio .NET. The second command is an automatic save to save everything in the IDE at predefined intervals so that changes aren't lost.

Extensibility Overview

      Figure 1 shows a partial list of the various objects accessible in the IDE. While previous instances of the IDE allowed access to the Documents and Debugger objects, the really interesting improved Window and new Code Model objects are where the action is. For the first time, everyone can add their own user interface windows to the IDE! The Code Model objects are what you can use to find, change, and update the actual code without having to rely on parsing. The great news is that the Code Model is updated in real time. Many developers have told me about their ideas for tools that work on source code, but have been stymied because of the daunting prospects of writing a parser for a single language, let alone the multiple parsers necessary in today's projects.
      There have also been great improvements in the events that tell you when various actions occur in the IDE. Figure 2 lists the various event objects and what they mean.
      The extensibility model is very good, but be aware that there are actually two of them. One extensibility model is for managed projects and the other is for Visual C++. While I can understand why the Visual C++ unmanaged code model is different, the fact that there are two completely separate project models is quite annoying to me. The debugger object is also different between managed and unmanaged projects. It certainly would have made life much easier if Microsoft had derived all languages and project types from a common ancestry.
      The other thing to keep in mind is that the object model(s) documentation is certainly not complete at this point because it's still in beta. The good news is that there is enough documentation to get you started.
      There's far more to the extensibility model than I can cover in this month's column. However, I can give you some very strong hints to make figuring out the model much easier. Before you ever contemplate writing an add-in, take some time and write lots of macros first. The IDE fully supports Visual Studio for Applications (VSA) and those macros support all objects as well as the events. In fact, I prototyped the SuperSaver add-in core logic as macros before I even considered writing the add-in.

Add-in Overview

      While you can do a tremendous amount of IDE extension with VSA macros, there are good reasons for using add-ins instead. Where macros are distributed in source form, add-ins are compiled so they execute faster and can protect your intellectual property. Additionally, add-ins can support user interface elements such as modal and modaless dialogs, tool windows, and dialogs in the Options dialog. In case you aren't familiar with them, tool windows are windows in the IDE such as the Solution Explorer or Command Window. You can turn any tool window into an MDI window by undocking it (right-clicking in the title bar, and unchecking "Dockable").
      The big news with add-ins is that they can be written in any language that supports COM. If you are familiar with C# or Visual Basic® .NET, you no longer have to crawl through the weird ATL macros just to get a simple add-in working. While the ease of developing in managed languages boosts productivity, I do have to mention that managed languages cannot host tool windows directly. Since add-ins are really ActiveX® controls, in order to have managed code as a tool window you'll need to have a shim ActiveX control that hosts the common language runtime (CLR). The good news is that in the final release of Visual Studio .NET, Microsoft should have a sample hosting control to show you exactly what you need to do to accomplish this.
      Those of you who have written Office XP add-ins should feel right at home with the IDE extensibility since the core interface for add-ins is IDTExtensibility2. With all the documentation on writing COM add-ins for Office XP, it's fairly easy to figure out how to proceed even when the background explanation is missing. The main job of IDTExtensibility2 is to take care of communicating with the IDE. The most interesting method, IDTExtensibility2.OnConnection, is where the IDE tells your add-in that it's being loaded. The second parameter to OnConnection, ConnectionMode, is important since it's the only place you are notified of the ext_ConnectMode.ext_cm_UISetup. The IDE will only tell your add-in exactly one time the first time it's loaded on the machine. I'll discuss how to reset the IDE so you can get the initial startup message when you need it.
      The other important interface is the IDTCommandTarget, which allows you to have your own named commands. Obviously, having an add-in without commands to offer the user is fairly useless, so all your add-ins will implement a class to handle the two methods in this interface. The first method is the IDTCommandTarget.QueryStatus, which is called after the user tries to execute your command or when the IDE needs to draw a menu item containing the command. Based on the IDE state (design mode, debugging, and so on) you'll tell the IDE if the command is enabled. Additionally, you'll also be asked to provide the text for the commands you add. This allows you to return appropriate text for localized editions of the IDE. The second method you'll implement is IDTCommandTarget.Exec which, as the name implies, is where you are asked to perform the requested command.
      While the IDE add-in samples do not come with the default installations of Visual Studio .NET, if you have a valid beta test ID, you can download the missing samples from the Visual Studio .NET Beta site at http://beta.visualstudio.net. Numerous examples will help clarify how add-ins work and show you what they can do. You can also find an ActiveX control that will host managed code Tool windows so you don't have to do it yourself. Unfortunately, the more involved samples that show many techniques in C++ are very poor programming techniques. Nothing annoys me more than seeing code written with macros that expand out to conditional statements, GOTOs on failures, and assumptions that you are using specific variable names and label names. Sadly, using the Add-in Wizard to create a C++-based add-in generates ugly code using the same poor-practice macros. I certainly hope Microsoft will fix the C++ code generation before release. Be prepared to spend lots of time reading code, but the examples do show how to take advantage of all the extensibility in the IDE.

Slaying the Add-in Wizard

      Armed with the briefest of add-in overviews, it's time to turn to some specifics. As with most nontrivial projects, there's a wizard in the IDE to get you started. Unfortunately, if you rely on the wizard to take care of everything for you, you won't get very far. Keep in mind that you are dealing with beta code and things should improve after the release. Even if you are reading this after the release ships, you shouldn't skip this section because I'll discuss several salient points about how add-ins are initialized and how you can avoid potential problems. If you are using the beta release, you can slay the wizard with the following steps:
  1. Use the Add-in Wizard, located in the Other Projects Extensibility Projects of the New Project dialog to create your add-in. Bear in mind that you can also create that work in the VSA Macros IDE as well.
  2. In the Configuration Manager dialog, make sure to uncheck "building the setup project." Unfortunately, each time you build your add-in, it causes the dependency in the setup project to be out of date, so the build has to create the 21MB (or bigger) setup project every time. Unless you like watching the hard disk spin or are paid by the hour regardless of productivity, you can skip building the installation until you are closer to being finished with the project.
  3. Build the Debug build of the add-in. Inexplicably, this is the only way to register the add-in. By building the add-in, you can extract the important registry entries so you can change values or reinitialize settings later.
  4. If you chose to mark the add-in as usable by all users, the registry key will start at HKLM; otherwise it will start at HLCU. Go to \Software\Microsoft\VisualStudio\7.0\AddIns and save your add-in's key and all values. If you are going to add additional information here, you might need to write your own setup program and remove the Add-in Wizard-created version. If you are building an add-in that works with the VSA IDE, you will need to enter your add-in key name under \Software\Microsoft\VSA\7.0\AddIn. After saving these files, add them to your project.
  5. To ensure that your add-in is properly registered, add-ins written with managed code will have a file created called ReCreateCommands.REG. Before you do anything else with your add-in, run this file. For add-ins written in Visual C++, the Add-in Wizard does not create this file. The following registry file shows the entry you will need to create:
    REGEDIT4
    [HKEY_CURRENT_USER\SOFTWARE\Microsoft\VSA\7.0\
     PreloadAddinState]"<YourAddInName>"=dword:1
    [HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\7.0\
     PreloadAddinState]"<YourAddInName>"=dword:1
    
    Substitute your add-in name for <YourAddInName>. If you ever change any of the commands in your add-in, you will need to rerun this file to ensure that your new commands get added to the IDE. Setting these keys tells the IDE to call the one-time initialization for the add-in.
      Before you get too far developing your add-in, you will probably want to fix some of the code that the Add-in Wizard generates for you. The first thing you might want to do is to remove the string "Connect" from the ProgID as well as all the commands. It's not really necessary and will just mean more typing for users who call your add-in commands from the Command window.
      The Add-in Wizard IDTExtensibility2.OnConnection is the second item you will likely want to fix. The default version just blindly tries to add the commands. Of course, the IDE does the right thing and throws an exception when you try to add a command that already exists. The Add-in Wizard code simply catches the error and ends the function. Since it's entirely possible that you'll be adding and changing commands as you're developing your add-in, having the OnConnection method skipping out early won't help much. Figure 3 shows the example code from SuperSaver where I do the initial command setup. In RemoveCommandsAndCommandBars, I manually go through and remove any command.
      As you develop your add-in, you might want to get rid of all traces of the add-in so you can start your testing fresh. While the uninstall might take care of it, you should be prepared to do it manually. After hunting around for a while, I found the following steps worked every time.
  1. In the IDE's Add-in Manager dialog, uncheck all checkboxes next to your add-in name. After clearing the checks, shut down all instances of the IDE.
  2. Run REGASM or REGSVR32 (depending on the type of code you used to write your add-in) to unregister your objects in the registry.
  3. If you chose to mark the add-in as usable by all users, the registry key will start at HKLM; otherwise it will start at HLCU. Go to \Software\Microsoft\VisualStudio\7.0\AddIns and remove your add-in's key. If you support running in the VSA IDE, remove your add-in's key from \Software\Microsoft\VSA\7.0\AddIns as well.

SuperSaver Can Save Your Bacon

      While you are chomping at the bit to start developing add-ins, let me turn your attention to the sample code that accompanies this month's column. As I have been using the IDE, I've seen two features that the IDE developers forgot to add. The first is that the IDE does not strip white space off the end of files when it saves your source, which is what the programming gods say you should do. Since it's trivial to create a regular expression to search for this condition, "[ \t]+$", I figured that all I needed to do was to use one of the regular expression classes to do the work for me. In the end, I found that the IDE TextDocument object has a ReplacePattern method just for this contingency.
      Implementing the SuperSaver.StripTrailingWhiteSpaceSave was trivial. I reset my keyboard bindings so that Ctrl+S uses SuperSaver.StripTrailingWhiteSpaceSave. While your add-in can easily do the key binding automatically, I left it as an optional exercise for you. If you do add the key binding, please make sure that you ask the user if it's OK for you to do that.
      The second feature needed in the IDE was an automatic save. All programs such as Microsoft® Excel, Word, and third-party programming editors now offer a feature that automatically saves your work at periodic intervals. By starting the SuperSaver add-in, it will take care of this for you every 10 minutes by default. The first time you load SuperSaver, you'll see the dialog shown in Figure 4. You can set the number of minutes between autosaves as well as strip trailing white space.

Figure 4 AutoSave
Figure 4 AutoSave

      Implementing the automatic save turned out to be much less work than I expected. I needed some kind of notification when time had elapsed, and that task was screaming out to use System.Timers.Timer. However, I was concerned when I noticed that the timer processing method executes in another thread. I did not know if the IDE allowed a different thread to access the open documents as well as the project lists. A little experimenting showed that after several weeks of using it this way there didn't seem to be any problems.
      The final point I want to mention about SuperSaver is how the IDE finds resources such as bitmaps, string tables, and icons. Since add-ins are really COM objects, just placing resources in your project as managed code resources does not work. I set up SuperSaver to use a satellite resources DLL. After you download the SuperSaver project (available from the link at the top of this article), you'll see it as a resource-only DLL called SuperSaverResources. Look at the registry entries in the SuperSaver.AddIn.REG file to see how to specify the string values to pull out of the satellite resource DLL. For example, the AboutBoxDetails entry, which appears in the IDE About box to show information about the add-in, is "#1001" in my SuperSaverAddIn.REG file. The number sign (#) followed by a number tells the IDE to pull the resources out of the satellite resource DLL.
      You can also look in the IDTExtensibility2.OnConnection to see the calls to Commands.AddNamedCommand where I pass the IDs of the specific satellite resource DLL bitmaps to associate with the commands. In order to get the satellite resource DLL properly hooked up so the IDE can find it, you'll need to change the very last entry in SuperSaverAddIn.REG file, SatelliteDllPath. That entry must point to the complete path, except for the local language ID. For example, if you are running the English language IDE, the complete path to the satellite resource DLL would be D:\SuperSaver\resources\1033. Your SatelliteDllPath entry in SuperSaver.AddIn.REG would point to D:\\SuperSaver\\resources\\. The registry editor needs the double slashes to process the text properly.

Wrap-up

      The new IDE extensibility in Visual Studio .NET should allow you to make the IDE do whatever you want. I can't wait to see what you come up with. If you want to extend SuperSaver, please do. Some ideas that I have considered are adding an option to save a copy of the file each hour of editing so that you can roll back the day's changes.

Tips

      Yes, that is Jack Frost nipping at your nose to remind you to send your tips to john@wintellect.com.
Tip 49 Information about all active add-ins is shown in the Visual Studio .NET About box, as shown in Figure 5. The Add-in Wizard gives you a default icon to display. What's interesting is that the icon is specified as hex data in your add-in's registry key. To change the icon, you'll need to use the GenerateIconData.exe program included with the sample download to get the hex bytes corresponding to your icon.

Figure 5 Installed Add-ins
Figure 5 Installed Add-ins

Tip 50 If you are having trouble with Visual Studio .NET crashing because of a bad add-in, you can start the IDE in safe mode by specifying the /safemode switch on the command line. Only the default environment and services are loaded in safe mode.

Send questions and comments for John to slayer@microsoft.com.
John Robbins is a cofounder of Wintellect, a software consulting, education, and development firm that specializes in programming for .NET and Windows. He is the author of Debugging Applications (Microsoft Press, 2000). You can contact John at http://www.wintellect.com.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.
Page view tracker