ASP.NET Weblogs

Welcome to ASP.NET Weblogs Sign in | Join | Help
in Search

The Technical Adventures of Adam Weigert

September 2008 - Posts

  • 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.

More Posts