PowerShell for Programmers – Automating Code Generation with PowerShell

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

You can leave a response, or trackback from your own site.

One Response to “PowerShell for Programmers – Automating Code Generation with PowerShell”

  1. [...] 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 [...]

Leave a Reply


one × = 4

Subscribe to RSS Feed Follow me on Twitter!