Jaa


The Evil EnvDTE namespace

If you have ever written an AddIn for Visual Studio or a Package, you must likely recognize the interfaces EnvDTE.Solution, EnvDTE.Project, EnvDTE.ProjectItem, EnvDTE80.SolutionFolder and EnvDTE.DTE, and you may ask what's so wrong about these interfaces?

In order to answer this question we must look at the history of Visual Studio and Visual Basic. One of the many features introduced with the release of Visual Basic 4.0 in 1995 (https://en.wikipedia.org/wiki/Visual_basic ) was the ability to create "Add In" that ran inside the VB IDE or inside the Office Applications.

The idea was that using a set of COM interfaces one can easily extend the running host with more functionality. Now days this sound fairly easy to do but that's because today we have Java, .NET and may design patterns that are specifically designed to do this (check out the Composite Application Block from P&P). But in those days, Microsoft had unmanaged C++ and VB4 or VB5.

I can understand that If Microsoft had chosen C++ as the default language for writing those Add Ins, the design would probably be much better but instead they went with VB for a number of reasons ( The most important one was cost of development ). So if VB was to be the language for developing these Add In the COM interfaces used for extending the host MUST be VB-compatible.

And been VB-compatible meant a lot of constraints right there. Remember, in those days VB did not have inheritance, it barely did encapsulation, there was no interface concept in the language until VB6.

So VB lacked some pretty fundamental elements in the language, it also added some elements that we may call "detrimental" to the language, yes you're right, I am talking about default properties. You know the language thing that allows to write: Value = recordSet["Name"] instead of value = RecordSet.Columns["Name"].Value. I know that the first looks "shorter" than the later, but the later is explicit about the underlying data structure, however, that's another topic.

So, the firsts Add Ins for VB and Office (Called VBA) were designed with these constraints and if fact Microsoft was right (at the particular time). They were a success in terms of a community writing new Add Ins every day.

Then 2001 came and .NET was born. In 2002 Visual Studio 7.0 was released. With this VB acquire new powers, suddenly it was a full Object Oriented language and we also were given his big brother C#.

However, if you look at the way you extended Visual Stuido 7.0 and for that matter Visual Studio 8.0 and probably Orcas also, we continue to use the same old interfaces that were great for a "detrimental" scripting language as VB4,5,6, but not so great for VB.NET, C# and C++.

Let's see an example of what do I mean, let's take the EnvDTE.Project interface and let's analyzed it.

Photobucket - Video and Image Hosting

The EnvDTE.Project interface is provided by a Project in the current open Solution in Visual Studio (I am using the definition of Project broadly because a Solution Folder is also a Project in Visual Studio, well see why later on, keep reading).

 

The Figure shows elements in the Solution Explorer in Visual Studio. Every single object in the solution tree must implement the IVsHierarchy, those nodes that are projects must also implement the IVsProject interface. However, one may think that most objects that are "Projects" in the solution tree provide an EnvDTE.Project interface. I said most because the reality is that it's up the designer of the Project system to provide or not the EnvDTE.Project.

The reality is that EnvDTE.Project (and for that matter almost all interfaces in the EnvDTE namespace) is an Automation interface. That is, an interface designed to be used by a VB6 client. So, you see where I go with this?

 

 

Photobucket - Video and Image Hosting

Maybe, this picture helps to clear things up. When you have a Project node in the Solution Explorer, this project node must (yes must) implement the IVsHierarchy interface and the IVSProject interfaces. However, it is not the same story with EnvDTE.Project, this interface is provided by asking the IVsHierarchy for the "Extension Object". The designer of the Project System may answer NULL to that request.

It is completely valid for a FooBarProject system not to have an automation object.

The reality is that with the languages and Tools that we have today, there's little need for "automation" interfaces. I am not saying that we should not used them, on the contrary, they can be very useful in certain scenarios, but one needs to understand what's going behind the scenes. I wish somebody had told me all of this when Daniel(https://weblogs.asp.net/cazzu/) and I were designing the concept of Bound References in GAX.

One example when you should NOT use the EnvDTE interfaces is when you are trying to run through the Solution Tree, using the IVsHierarchy interface is much easier and a lot more robust that trying to use a bunch of automation objects.

Another problem with automation objects is that not every project system implements the same interfaces in the same way, one example is the get_Files method, C# implements it using a zero-based index, but the VB project system implements it using a one-based index.

If you are trying to write a Framework on top of Visual Studio, try to avoid going through the automation interfaces, try to see if there is a service in VS that can give you what you want. Example: Instead if using DTE.Selection, use IVsMonitorSelection service. If you need to store a Project pointer, persist the GUID of the project, not the path or canonical name.

All of this to say that, the EnvDTE are there mostly for historical reasons and to support old add ins and extensions. One must realize that they are second class citizens in the Visual Studio world of extensibility and that when writing extensions to VS one must talk with the real underlying objects we are extending.

I wish somebody had told me this before.

Comments

  • Anonymous
    December 26, 2007
    Thanks for this posting. I always wondered why extending VS for so horrible with all these interfaces and the lack of inheridance in the extensibility classes. Now, only if the documentation in the macro IDE would tell you how to use the new better interfaces instead of the old envdte namespace.

  • Anonymous
    March 11, 2008
    Wow.. I wish I had known this before, too.  This changes developing for VS from root-canal pain to actually decently sane.  Thanks.. this is going to save me a lot of time and effort.  I wish Microsoft's documentation would de-emphasize using EnvDTE and cousins where possible, and recommend these in their place.

  • Anonymous
    December 17, 2008
    Hi! Do you know how-to add item to project as child of some existing item without using EnvDTE? Code using EnvDTE: EnvDTE.ProjectItem parentItem=(EnvDTE.ProjectItem)obj;                    parentItem.ProjectItems.AddFromFile(System.IO.Path.GetFullPath(path));

  • Anonymous
    November 06, 2009
    Cast your IVsHirarchy to IVsProject and use IVsProject.AddItem

  • Anonymous
    January 18, 2010
    Thanks, pretty interesting reading, and it's something that just might help me out to know one day. (doing VS automation)

  • Anonymous
    February 25, 2010
    After reading your post, I tried to implement my "solutions deployer" without using of EnvDTE. Therefore, now I'm completely stuck with issues like: http://social.msdn.microsoft.com/Forums/en-US/vsx/thread/380e7a89-b062-4d3d-9492-04676840d4bd/. Do you think it's so drammatically impossible to manage with solution folders for example, without EnvDTE?

  • Anonymous
    May 03, 2010
    Hello Ruben: The following snipped will create a solution folder and return you the IVsHierarchy of the new solution folder:            const string SolutionFolderType = "2150E333-8FDC-42a3-9474-1A3956D46DE8";            IntPtr ptr = IntPtr.Zero;            Guid solutionFolderGuid = new Guid(SolutionFolderType);            Guid iidProject = typeof(IVsHierarchy).GUID;            int hr = vsSolution.CreateProject(                ref solutionFolderGuid,                null,                null,                folderName,                0,                ref iidProject,                out ptr);            if (hr == VSConstants.S_OK && ptr != IntPtr.Zero)            {                return (IVsHierarchy)Marshal.GetObjectForIUnknown(ptr);            }

  • Anonymous
    December 31, 2012
    Hi Oscar, Do you still think that IVsHierarchy are a better way to access Visual Studio solution tree? I noticed that both EnvDTE and Microsoft.VisualStudio.Shell.Interop namespaces seem to be getting regular updates. The interop interfaces are such a pain to code against. Oleg