Daniel Cazzulino's Blog : How to convert from EnvDTE.Project to IVsHierarchy and IVsProject and viceversa

Subscriptions

News

Source code published in this blog is public domain unless otherwise specified.

 

kzu in LinkedIn

  Microsoft MVP Profile

 Contact

Post Categories

How to convert from EnvDTE.Project to IVsHierarchy and IVsProject and viceversa

I've long been looking for an answer to how to go from an automation (DTE) project to its corresponding VSIP project instance. I've been given consistently the same non-satisfactory answer: there's no way because they are two fundamentally different object models that VSIP projects may or may not expose, and there's no consistent way of going from one to the other. Or that's what they said to me at the time.

As I'm not keen on accepting a "no" for an answer, I went on to investigate how to do it, and finally got to some working code that does it. It involves the DTE, VSIP and MSBuild. Here's the code:

public static class VsHelper
{
    public static IVsHierarchy GetCurrentHierarchy(IServiceProvider provider)
    {
        DTE vs = (DTE)provider.GetService(typeof(DTE));
        if (vs == null) throw new InvalidOperationException("DTE not found.");

        return ToHierarchy(vs.SelectedItems.Item(1).ProjectItem.ContainingProject);
    }
    public static IVsHierarchy ToHierarchy(EnvDTE.Project project)
    {
        if (project == null) throw new ArgumentNullException("project");

        string projectGuid = null;

        // DTE does not expose the project GUID that exists at in the msbuild project file.
        // Cannot use MSBuild object model because it uses a static instance of the Engine, 
        // and using the Project will cause it to be unloaded from the engine when the 
        // GC collects the variable that we declare.
        using (XmlReader projectReader = XmlReader.Create(project.FileName))
        {
            projectReader.MoveToContent();
            object nodeName = projectReader.NameTable.Add("ProjectGuid");
            while (projectReader.Read())
            {
                if (Object.Equals(projectReader.LocalName, nodeName))
                {
                    projectGuid = projectReader.ReadContentAsString();
                    break;
                }
            }
        }

        Debug.Assert(!String.IsNullOrEmpty(projectGuid));

        IServiceProvider serviceProvider = new ServiceProvider(project.DTE as
            Microsoft.VisualStudio.OLE.Interop.IServiceProvider);
        
        return VsShellUtilities.GetHierarchy(serviceProvider, new Guid(projectGuid));
    }
    public static IVsProject3 ToVsProject(EnvDTE.Project project)
    {
        if (project == null) throw new ArgumentNullException("project");

        IVsProject3 vsProject = ToHierarchy(project) as IVsProject3;

        if (vsProject == null)
        {
            throw new ArgumentException("Project is not a VS project.");
        }

        return vsProject;
    }
    public static EnvDTE.Project ToDteProject(IVsHierarchy hierarchy)
    {
        if (hierarchy == null) throw new ArgumentNullException("hierarchy");

        object prjObject = null;
        if (hierarchy.GetProperty(0xfffffffe, -2027, out prjObject) >= 0)
        {
            return (EnvDTE.Project)prjObject;
        }
        else
        {
            throw new ArgumentException("Hierarchy is not a project.");
        }
    }
    public static EnvDTE.Project ToDteProject(IVsProject project)
    {
        if (project == null) throw new ArgumentNullException("project");

        return ToDteProject(project as IVsHierarchy);
    }

}

The code above requires a reference to Microsoft.Build.Engine.dll, EnvDTE.dll and Microsoft.VisualStudio.Shell.Interop.dll (part of the VS SDK).

Now, whenever you have a DTE Project and need a VSIP IVsHierarchy, you just call VsHelper.ToHierarchy(project), and inversely, when you need a DTE project from a hierarchy or VS project, you call the ToProject methods.

Enjoy! 

posted on Friday, January 06, 2006 4:11 PM by kzu