PowerShell: Oh Happy Days Are Here (Dynamically Compiling C# Code for Strongly Typed Objects within PowerShell)

Ever wanted to build native .NET objects to use while in a PowerShell script? Well I certainly have, and finally took the time to figure out how easy it is to actually do! Enjoy!

function Compile-Code {
    param (
        [string[]] $code       = $(throw "The parameter -code is required.")
      , [string[]] $references = @()
      , [switch]   $asString   = $false
      , [switch]   $showOutput = $false
      , [switch]   $csharp     = $true
      , [switch]   $vb         = $false
    )
    
    $options    = New-Object "System.Collections.Generic.Dictionary``2[System.String,System.String]";
    $options.Add( "CompilerVersion", "v3.5")
    
    if ( $vb ) {
        $provider = New-Object Microsoft.VisualBasic.VBCodeProvider $options
    } else {
        $provider = New-Object Microsoft.CSharp.CSharpCodeProvider $options
    }
    
    $parameters = New-Object System.CodeDom.Compiler.CompilerParameters
    
    @( "mscorlib.dll", "System.dll", "System.Core.dll", "System.Xml.dll", ([System.Reflection.Assembly]::GetAssembly( [PSObject] ).Location) ) + $references | Sort -unique |% { $parameters.ReferencedAssemblies.Add( $_ ) } | Out-Null
    
    $parameters.GenerateExecutable = $false
    $parameters.GenerateInMemory   = !$asString
    $parameters.CompilerOptions    = "/optimize"
    
    if ( $asString ) {
        $parameters.OutputAssembly = [System.IO.Path]::GetTempFileName()
    }
    
    $results = $provider.CompileAssemblyFromSource( $parameters, $code )
    
    if ( $results.Errors.Count -gt 0 ) {
        if ( $output ) {
            $results.Output |% { Write-Output $_ }
        } else {
            $results.Errors |% { Write-Error $_.ToString() }
        }
    } else {
        if ( $asString ) {
            $content = [System.IO.File]::ReadAllBytes( $parameters.OutputAssembly )
            $content = [Convert]::ToBase64String( $content )
            
            [System.IO.File]::Delete( $parameters.OutputAssembly );
            
            return $content
        } else {
            return $results.CompiledAssembly
        }        
    }
}

Example usage:

Compile-Code -csharp -code @"
    using System;
    
    public class Foo {
        public Foo ( string message ) {
            Message = message;
        }
    
        public string Message { get; private set; }
        
        public void Bar() {
            Console.WriteLine( "Foo.Bar: {0}", Message );
        }
    }
"@

$foo = New-Object Foo "hello world"
$foo.Bar()

Update: Added ability to compile C# or VB.NEt code and also to return the bytes for the compiled assembly for loading at a later time.

No Comments