There is a feature that has been in pretty much every PowerShell host inside Visual Studio and that is the ability to access the DTE (the top level automation object). Here are a couple cool things that you can do using the DTE. I’ll start by defining a couple simple functions for finding code elements.
function global:Find-CodeElements($elements, [EnvDTE.vsCMElement]$type)
{
if ($elements -eq $null)
{
return
}
$elements | % { Find-CodeElements $_.Children $type }
$elements | ? {$_.Kind -eq [int]$type }
}
function global:Get-TopLevelElements()
{
$EnvDte.Solution.Projects | % { $_.ProjectItems | % { $_.FileCodeModel.CodeElements } }
}
Now you could look up code elements by doing something as simple as this.
$topLevelElements = Get-TopLevelElements $classElements = Find-CodeElements $topLevelElements [EnvDTE.vsCMElement]::vsCMElementClass
$classElements would now contain a list of all the classes found in the entire active solution. An interesting property of a class object is the ImplementedInterfaces property. Here’s a function to find all classes that implement a particular interface.
function global:Find-InterfaceImplementors([string]$interfaceName)
{
$topLevelElements = Get-TopLevelElements
$classElements = Find-CodeElements $topLevelElements ([EnvDTE.vsCMElement]::vsCMElementClass)
foreach($class in $classElements)
{
$ret = $class.ImplementedInterfaces | ? { $_.FullName -match $interfaceName }
if ($ret -ne $null)
{
$class
}
}
}
Not only can you locate code within a Visual Studio solution but you can also change it. If you look at the Get-Member table for a CodeModel2 object you will see this nice little methods:
AddAttribute Method CodeAttribute AddAttribute (string, string, Variant) AddBase Method CodeElement AddBase (Variant, Variant) AddClass Method CodeClass AddClass (string, Variant, Variant, Variant, vsCMAccess) AddDelegate Method CodeDelegate AddDelegate (string, Variant, Variant, vsCMAccess) AddEnum Method CodeEnum AddEnum (string, Variant, Variant, vsCMAccess) AddEvent Method CodeEvent AddEvent (string, string, bool, Variant, vsCMAccess) AddFunction Method CodeFunction AddFunction (string, vsCMFunction, Variant, Variant, vsCMAccess, Variant) AddImplementedInterface Method CodeInterface AddImplementedInterface (Variant, Variant) AddProperty Method CodeProperty AddProperty (string, string, Variant, Variant, vsCMAccess, Variant) AddStruct Method CodeStruct AddStruct (string, Variant, Variant, Variant, vsCMAccess) AddVariable Method CodeVariable AddVariable (string, Variant, Variant, vsCMAccess, Variant)
For example, if I wanted to add a new variable to the class MyNamespace.Class1, I could simply do this.
$class1 = Find-Class "MyNamespace.Class1"
$class1.AddVariable("Test", [EnvDTE.vsCMTypeRef]::vsCMTypeRefString, 0, [EnvDTE.vsCMAccess]::vsCMAccessPrivate, $null)
Now Class1 will have a new and shiny private string variable Test.
class Class1
{
private string Test;
}
Also notice that the Name property for CodeModel2 objects is Writeable. If you wanted to change the name of that newly created variable you could easily do this:
$property = $testHost.Children | select -First 1 $property.Name = "Test2"
With this type of automation you could easily find and change code when ReSharper isn’t quite cutting it. Using StudioShell or PowerGUI VSX you could even create commands (StudioShell is probably way easier) that you could hide behind context menu items. As a side note I performed all the above using PowerGUI VSX.
Here are the variables used to access the DTE in the various PowerShell hosts:
PowerGUI VSX: $EnvDTE
NuGet: $DTE
StudioShell: $DTE








[...] shortage of code generation techniques available in Visual Studio. There are even some neat PowerShell solutions that are based on the DTE. Let me show you an approach I’ve used a few times – it’s a [...]