Adding Solution Folders Via Visual Studio Automation

In the Visual Studio Solution Explorer, you can add solution folders to group projects or project items. These solution folders do not represent folders in the file system but are merely virtual. Adding these folders in the Solution Explorer is a trivial task, but how do you do that in code via EnvDTE? Continue reading

Add A Namespace Import Via Visual Studio Automation

Suppose you had a code document represented by a FileCodeModel object. And suppose you were modifying the containing DOM. Sometimes, you might need to add a namespace import to the document (i.e. “using …” in C# of “Import …” in VB). A namespace import is represented by the CodeImport interface. Reading out the namespace imports is easy:

var imports = fileCodeModel.CodeElements.OfType<CodeImport>();

But how do you add a new CodeImport element? Well, TIL that it’s worthwhile to look at all versions of an automation interface (e.g. FileCodeModel2 instead of just FileCodeModel). Sometimes, you might find just the method you were looking for πŸ™‚ So the answer to the question in this case is:

var fileCodeModel = (FileCodeModel2)projectItem.FileCodeModel;
fileCodeModel.AddImport("My.New.Namespace");

Get The ProjectItem of a T4 Template From Inside the T4 Template

So you got a T4 template and need access to the ProjectItem of that T4 template or to the parent Project of the T4 template? Here’s how:

1) Set the template’s hostspecific attribute to true and reference the Visual Studio Automation assemblies.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="envdte" #>
<#@ assembly name="envdte80" #>
<#@ import namespace="EnvDTE" #>

2) Get the Project or ProjectItem using Visual Studio Automation.

// By setting the template's 'hostspecific' property to true, 
// we get access to the text templating engine host 
// which implements IServiceProvider.
var serviceProvider = (IServiceProvider)this.Host;

// Get the automation root object
var dte = (EnvDTE.DTE)serviceProvider.GetService(typeof(EnvDTE.DTE));

// Get the project item
var projectItem = dte.Solution.FindProjectItem(this.Host.TemplateFile);

// Get the project
var project = projectItem.ContainingProject;

Tell if an EnvDTE.Project is a Web Application

A Visual Studio Extension or Add-In might need to know whether or not a project is a web application (in my case, I needed to know whether I’ll expect app.congig or web.config). Due to a bug in Visual Studio, that is not so straight-forward. But thanks to Carlos J. Quintero, I got some code together.

If you need it, suit yourselves.

using System.Linq;
using EnvDTE;

namespace MuniHusseini.Demos
{
        public static bool IsWebProject(this Project project)
        {
            return project.Object is VsWebSite.VSWebSite || project.ProjectHasExtender("WebApplication");
        }

        public static bool ProjectHasExtender(this Project proj, string extenderName)
        {
            // See http://www.mztools.com/articles/2007/mz2007014.aspx for more information.

            try
            {
                // We could use proj.Extender(extenderName) but it causes an exception if not present and 
                // therefore it can cause performance problems if called multiple times. We use instead:

                var extenderNames = (object[])proj.ExtenderNames;

                return extenderNames.Length > 0 && extenderNames.Any(extenderNameObject => extenderNameObject.ToString() == extenderName);
            }
            catch
            {
                // Ignore
            }

            return false;
        }
    }
}

Get Project References From EnvDTE.Project

Suppose you’re developing a Visual Studio Extension and need to enumerate the references of a Visual Studio project. Here is how to do that:

public static IEnumerable<AssemblyName> CollectSettings(EnvDTE.Project project)
{
    var vsproject = project.Object as VSLangProj.VSProject;
    // note: you could also try casting to VsWebSite.VSWebSite

    foreach (VSLangProj.Reference reference in vsproject.References)
    {
        if (reference.SourceProject == null)
        {
            // This is an assembly reference
            var fullName = GetFullName(reference);
            var assemblyName = new AssemblyName(fullName);
            yield return assemblyName;
        }
        else
        {
            // This is a project reference
        }
    }
}

public static string GetFullName(VSLangProj.Reference reference)
{
    return string.Format("{0}, Version={1}.{2}.{3}.{4}, Culture={5}, PublicKeyToken={6}",
                            reference.Name,
                            reference.MajorVersion, reference.MinorVersion, reference.BuildNumber, reference.RevisionNumber,
                            reference.Culture.Or("neutral"),
                            reference.PublicKeyToken.Or("null"));
}

For the types in the namespace VSLangProj, you’ll need to reference VSLangProj.dll. EnvDTE.Project ist located in EnvDTE.dll. And just for completness, here’s the extension method used in the code above:

static class Extensions
{
    public static string Or(this string text, string alternative)
    {
        return string.IsNullOrWhiteSpace(text) ? alternative : text;
    }
}

Convert IVsHierarchy to ProjectItem or Project

The title says it all… here you go:

Microsoft.VisualStudio.Shell.Interop.IVsHierarchy hierarchy = // ... get it.

// VSITEMID_ROOT gets the current project. 
// Alternativly, you might have another item id.
var itemid = VSConstants.VSITEMID_ROOT; 

object objProj;
hierarchy.GetProperty(itemid, (int)__VSHPROPID.VSHPROPID_ExtObject, out objProj);

var projectItem = objProj as EnvDTE.ProjectItem;
// ... or ...
var project = objProj as EnvDTE.Project;

Thanks to Ed Dore in the Visual Studio forums.