Logo
HOWTO: Get the IVsHierarchy and Item Id of EnvDTE.Project and EnvDTE.ProjectItem

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio 2005
Date: January 2014   Microsoft Visual Studio 2008
      Microsoft Visual Studio 2010
      Microsoft Visual Studio 2012
      Microsoft Visual Studio 2013
Introduction

The automation model provides the EnvDTE.Project and EnvDTE.ProjectItem classes that have many properties. However, sometimes you need some information that is not provided by those classes. In these cases you need to use the underlying IVsHierarchy object used by Visual Studio to handle hierarchies such as the one of the Solution Explorer.

The IVsHierarchy interface has a GetProperty method to get any information about any node in the hierarchy. Each hierarchy has a root node, and each node can have child nodes. Nodes in a hierarchy are identified by an item id, which is dynamic (it can change if you move nodes, for example), so the IVsHierarchy provides a method (ParseCanonicalName) to get the item id of something invariant of a node (its "canonical" name). Hierarchies can be nested too. For example, the solution node is the root node of a hierarchy, and each project is in turn the root node of another hierarchy.

How to navigate the solution, projects and files using native hierarchies is beyond the scope of this article (see the article HOWTO: Navigate the files of a solution using the IVsHierarchy interface from an add-in). This sample will use the EnvDTE.Solution, EnvDTE.Project and EnvDTE.ProjectItems classes to navigate instead. However, it will show how to:

  • Given an EnvDTE.Project, get its IVsHierarchy object.
  • Given an EnvDTE.ProjectItem, get its Item Id value inside the IVsHierarchy of its EnvDTE.Project parent.

More Information

The following add-in, when loaded, navigates the projects, folders and files of the loaded solution and shows the Item Id and file name of each node of the projects. To compile it you need to install the Visual Studio SDK to get the Microsoft.VisualStudio.OLE.Interop.dll and Microsoft.VisualStudio.Shell.Interop.dll assemblies (alternatively you can get them from the Global Assembly Cache (GAC) as explained in the article HOWTO: Reference a Visual Studio assembly in the GAC from an add-in).

Language: C#   Copy Code Copy Code (IE only)

// Note: add references to the following assemblies of the Visual Studio SDK or from the GAC:
// Microsoft.VisualStudio.OLE.Interop.dll
// Microsoft.VisualStudio.Shell.Interop.dll

using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell.Interop;

namespace VSHierarchyAddin
{
   public class Connect : IDTExtensibility2
   {
      private const int S_OK = 0;

      private DTE2 _applicationObject;
      private AddIn _addInInstance;

      public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
      {
         _applicationObject = (DTE2)application;
         _addInInstance = (AddIn)addInInst;

         switch (connectMode)
         {
            case ext_ConnectMode.ext_cm_Startup:
               
               // Do nothing; OnStartupComplete will be called
               break;

            case ext_ConnectMode.ext_cm_AfterStartup:
               
               InitializeAddIn();
               break;
         }
      }

      public void OnStartupComplete(ref Array custom)
      {
         InitializeAddIn();
      }

      private void InitializeAddIn()
      {
         Microsoft.VisualStudio.OLE.Interop.IServiceProvider serviceProvider;
         IVsSolution solutionService;

         try
         {
            serviceProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)_applicationObject;

            solutionService = (IVsSolution)GetService(serviceProvider, typeof(SVsSolution), typeof(IVsSolution));

            foreach (EnvDTE.Project project in _applicationObject.Solution.Projects)
            {
               ProcessProject(solutionService, project);
            }
         }
         catch (Exception ex)
         {
            MessageBox.Show(ex.ToString());
         }
      }

      private void ProcessProject(IVsSolution solutionService, EnvDTE.Project project)
      { 
         IVsHierarchy projectHierarchy = null;

         if (solutionService.GetProjectOfUniqueName(project.UniqueName, out projectHierarchy) == S_OK)
         {
            if (projectHierarchy != null)
            {
               ProcessProjectItems(solutionService, projectHierarchy, project.ProjectItems);
            }
         }
      }

      private void ProcessProjectItems(IVsSolution solutionService, IVsHierarchy projectHierarchy, EnvDTE.ProjectItems projectItems)
      {
         if (projectItems != null)
         {
            foreach (EnvDTE.ProjectItem projectItem in projectItems)
            {
               if (projectItem.SubProject != null)
               {
                  ProcessProject(solutionService, projectItem.SubProject);
               }
               else
               {
                  ProcessProjectItem(projectHierarchy, projectItem);

                  // Enter in recursion
                  ProcessProjectItems(solutionService, projectHierarchy, projectItem.ProjectItems);
               }
            }
         }
      }

      private void ProcessProjectItem(IVsHierarchy projectHierarchy, EnvDTE.ProjectItem projectItem)
      { 
         string fileFullName = null;
         uint itemId;

         try
         {
            fileFullName = projectItem.get_FileNames(0);
         }
         catch
         { 
         }

         if (!string.IsNullOrEmpty(fileFullName))
         {
            if (projectHierarchy.ParseCanonicalName(fileFullName, out itemId) == S_OK)
            {
               MessageBox.Show("File: " + fileFullName + "\r\n" + "Item Id: 0x" + itemId.ToString("X"));
            }
         }
      }

      private object GetService(Microsoft.VisualStudio.OLE.Interop.IServiceProvider serviceProvider, 
         System.Type serviceType, System.Type interfaceType)
      {
         object service = null;
         IntPtr servicePointer;
         int hr = 0;
         Guid serviceGuid;
         Guid interfaceGuid;

         serviceGuid = serviceType.GUID;
         interfaceGuid = interfaceType.GUID;

         hr = serviceProvider.QueryService(ref serviceGuid, ref interfaceGuid, out servicePointer);
         if (hr != S_OK)
         {
            System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr);
         }
         else if (servicePointer != IntPtr.Zero)
         {
            service = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(servicePointer);
            System.Runtime.InteropServices.Marshal.Release(servicePointer);
         }
         return service;
      }

      public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
      {
      }

      public void OnAddInsUpdate(ref Array custom)
      {
      }

      public void OnBeginShutdown(ref Array custom)
      {
      }
      
   }
}

Related articles



Go to the 'Visual Studio Extensibility (VSX)' web site for more articles like this (Articles section)


Top