PoshCode Logo PowerShell Code Repository

Reflection Module 4.5 by Joel Bennett 13 months ago (modification of post by Joel Bennett view diff)
View followups from Joel Bennett | diff | embed code: <script type="text/javascript" src="http://PoshCode.org/embed/3993"></script>download | new post

Helpers for working with .Net classes: Get-Constructor, Get-Assembly, Add-Assembly, Get-Type

Now includes the New-ConstructorFunction (formerly in PowerBoots) which depends on the Autoload Module

In this version I added code for finding extension methods, and updated Read-Choice

  1. #requires -version 2.0
  2. # ALSO REQUIRES Autoload for some functionality (Latest version: http://poshcode.org/3173)
  3. # You should create a Reflection.psd1 with the contents:
  4. #    @{ ModuleToProcess="Reflection.psm1"; RequiredModules = @("Autoload"); GUID="64b5f609-970f-4e65-b02f-93ccf3e60cbb"; ModuleVersion="4.5.0.0" }
  5. #History:
  6. # 1.0  - First public release (March 19, 2010)
  7. # 2.0  - Private Build
  8. #      - Included the Accelerator function inline
  9. #      - Added a few default aliases
  10. # 3.0  - September 3, 2010
  11. #      - Included the New-ConstructorFunction feature (ripped from PowerBoots to serve a more generic and powerful purpose!)
  12. #      - New-ConstructorFunction and Import-ConstructorFunctions depend on the Autoload Module: http://poshcode.org/2312
  13. # 3.5  - January 28, 2011
  14. #      - Fixed several bugs in Add-Assembly, Get-Assembly, Get-MemberSignature
  15. #      - Fixed alias exporting so aliases will show up now
  16. #      - Added New-ModuleManifestFromSnapin to create module manifests from snapin assemblies
  17. # 3.6  - January 28, 2011
  18. #      - Added *basic* support for CustomPSSnapin to New-ModuleManifestFromSnapin
  19. # 3.7  - February 1, 2001 - NOT RELEASED
  20. #      - Added [TransformAttribute] type
  21. # 3.8  - May 4, 2011 - NOT RELEASED
  22. #      - Huge rewrite of Invoke-Generic (also published separately: http://poshcode.org/2649)
  23. # 3.9  - May 25, 2011 - NOT RELEASED
  24. #      - Added "Interface" parameter to Get-Type
  25. # 4.0  - Sept 27, 2011
  26. #      - Fix conflicts with PowerShell 3
  27. # 4.1  - Oct 27, 2011
  28. #      - Fix PowerShell 3 changes so they don't break PowerShell 2 (huy!)
  29. # 4.2  - Added Add-Enum and Add-Struct -- even though they're really more CodeGen than reflection
  30. #
  31. # 4.5  - March 2, 2013
  32. #      - Added Test-AssignableToGeneric and Get-ExtensionMethod
  33. #      - Updated Read-Choice from the standalone script I use.
  34.  
  35. Add-Type -TypeDefinition @"
  36. using System;
  37. using System.ComponentModel;
  38. using System.Management.Automation;
  39. using System.Collections.ObjectModel;
  40.  
  41. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
  42. public class TransformAttribute : ArgumentTransformationAttribute {
  43.   private ScriptBlock _scriptblock;
  44.   private string _noOutputMessage = "Transform Script had no output.";
  45.  
  46.   public override string ToString() {
  47.      return string.Format("[Transform(Script='{{{0}}}')]", Script);
  48.   }
  49.  
  50.   public override Object Transform( EngineIntrinsics engine, Object inputData) {
  51.      try {
  52.         Collection<PSObject> output =
  53.            engine.InvokeCommand.InvokeScript( engine.SessionState, Script, inputData );
  54.        
  55.         if(output.Count > 1) {
  56.            Object[] transformed = new Object[output.Count];
  57.            for(int i =0; i < output.Count;i++) {
  58.               transformed[i] = output[i].BaseObject;
  59.            }
  60.            return transformed;
  61.         } else if(output.Count == 1) {
  62.            return output[0].BaseObject;
  63.         } else {
  64.            throw new ArgumentTransformationMetadataException(NoOutputMessage);
  65.         }
  66.      } catch (ArgumentTransformationMetadataException) {
  67.         throw;
  68.      } catch (Exception e) {
  69.         throw new ArgumentTransformationMetadataException(string.Format("Transform Script threw an exception ('{0}'). See `$Error[0].Exception.InnerException.InnerException for more details.",e.Message), e);
  70.      }
  71.   }
  72.  
  73.   public TransformAttribute() {
  74.      this.Script = ScriptBlock.Create("{`$args}");
  75.   }
  76.  
  77.   public TransformAttribute( ScriptBlock Script ) {
  78.      this.Script = Script;
  79.   }
  80.  
  81.   public ScriptBlock Script {
  82.      get { return _scriptblock; }
  83.      set { _scriptblock = value; }
  84.   }
  85.  
  86.   public string NoOutputMessage {
  87.      get { return _noOutputMessage; }
  88.      set { _noOutputMessage = value; }
  89.   }  
  90. }
  91. "@
  92.  
  93.  
  94.  
  95. function Get-Type {
  96.    <#
  97.       .Synopsis
  98.          Gets the types that are currenty loaded in .NET, or gets information about a specific type
  99.       .Description
  100.          Gets information about one or more loaded types, or gets the possible values for an enumerated type or value.    
  101.       .Example
  102.          Get-Type
  103.          
  104.          Gets all loaded types (takes a VERY long time to print out)
  105.       .Example
  106.          Get-Type -Assembly ([PSObject].Assembly)
  107.          
  108.          Gets types from System.Management.Automation
  109.       .Example
  110.          [Threading.Thread]::CurrentThread.ApartmentState | Get-Type
  111.          
  112.          Gets all of the possible values for the ApartmentState property
  113.       .Example
  114.          [Threading.ApartmentState] | Get-Type
  115.          
  116.          Gets all of the possible values for an apartmentstate
  117.    #>
  118.    [CmdletBinding(DefaultParameterSetName="Assembly")]  
  119.    param(
  120.       # The Assemblies to search for types.
  121.       # Can be an actual Assembly object or a regex to pass to Get-Assembly.
  122.       [Parameter(ValueFromPipeline=$true)]
  123.       [PsObject[]]$Assembly,
  124.  
  125.       # The type name(s) to search for (wildcard patterns allowed).
  126.       [Parameter(Mandatory=$false,Position=0)]
  127.       [SupportsWildCards()]
  128.       [String[]]$TypeName,
  129.  
  130.       # A namespace to restrict where we selsect types from (wildcard patterns allowed).
  131.       [Parameter(Mandatory=$false)]
  132.       [SupportsWildCards()]
  133.       [String[]]$Namespace,
  134.  
  135.       # A Base type they should derive from (wildcard patterns allowed).
  136.       [Parameter(Mandatory=$false)]
  137.       [SupportsWildCards()]
  138.       [String[]]$BaseType,
  139.  
  140.       # An interface they should implement (wildcard patterns allowed).
  141.       [Parameter(Mandatory=$false)]
  142.       [SupportsWildCards()]
  143.       [String[]]$Interface,
  144.  
  145.       # An Custom Attribute which should decorate the class
  146.       [Parameter(Mandatory=$false)]
  147.       [SupportsWildCards()]
  148.       [String[]]$Attribute,
  149.  
  150.  
  151.       # The enumerated value to get all of the possible values of
  152.       [Parameter(ParameterSetName="Enum")]
  153.       [PSObject]$Enum,
  154.  
  155.       # Causes Private types to be included
  156.       [Parameter()][Alias("Private","ShowPrivate")]
  157.       [Switch]$Force
  158.    )
  159.  
  160.    process {
  161.       if($psCmdlet.ParameterSetName -eq 'Enum') {
  162.          if($Enum -is [Enum]) {
  163.             [Enum]::GetValues($enum.GetType())
  164.          } elseif($Enum -is [Type] -and $Enum.IsEnum) {
  165.             [Enum]::GetValues($enum)
  166.          } else {
  167.             throw "Specified Enum is neither an enum value nor an enumerable type"
  168.          }
  169.       }
  170.       else {
  171.          if($Assembly -as [Reflection.Assembly[]]) {
  172.             ## This is what we expected, move along
  173.          } elseif($Assembly -as [String[]]) {
  174.             $Assembly = Get-Assembly $Assembly
  175.          } elseif(!$Assembly) {
  176.             $Assembly = [AppDomain]::CurrentDomain.GetAssemblies()
  177.          }
  178.  
  179.          :asm foreach ($asm in $assembly) {
  180.             Write-Verbose "Testing Types from Assembly: $($asm.Location)"
  181.             if ($asm) {
  182.                trap {
  183.                   if( $_.Exception.LoaderExceptions -and $_.Exception.LoaderExceptions[0] -is [System.IO.FileNotFoundException] ) {
  184.                      $PSCmdlet.WriteWarning( "Unable to load some types from $($asm.Location), required assemblies were not found. Use -Debug to see more detail")
  185.                      continue asm
  186.                   }
  187.                   Write-Error "Unable to load some types from $($asm.Location). Try with -Debug to see more detail"
  188.                   Write-Debug $( $_.Exception.LoaderExceptions | Out-String )
  189.                   continue asm
  190.                }
  191.                $asm.GetTypes() | Where {
  192.                   ( $Force -or $_.IsPublic ) -AND
  193.                   ( !$Namespace -or $( foreach($n in $Namespace) { $_.Namespace -like $n  } ) ) -AND
  194.                   ( !$TypeName -or $( foreach($n in $TypeName) { $_.Name -like $n -or $_.FullName -like $n } ) -contains $True ) -AND
  195.                   ( !$Attribute -or $( foreach($n in $Attribute) { $_.CustomAttributes | ForEach { $_.AttributeType.Name -like $n -or $_.AttributeType.FullName -like $n } } ) -contains $True ) -AND
  196.                   ( !$BaseType -or $( foreach($n in $BaseType) { $_.BaseType -like $n } ) -contains $True ) -AND
  197.                   ( !$Interface -or @( foreach($n in $Interface) { $_.GetInterfaces() -like $n } ).Count -gt 0 )
  198.                }
  199.             }
  200.          }
  201.       }
  202.    }
  203. }
  204.  
  205. function Add-Assembly {
  206.    #.Synopsis
  207.    #  Load assemblies
  208.    #.Description
  209.    #  Load assemblies from a folder
  210.    #.Parameter Path
  211.    #  Specifies a path to one or more locations. Wildcards are permitted. The default location is the current directory (.).
  212.    #.Parameter Passthru
  213.    #  Returns System.Runtime objects that represent the types that were added. By default, this cmdlet does not generate any output.
  214.    #  Aliased to -Types
  215.    #.Parameter Recurse
  216.    #  Gets the items in the specified locations and in all child items of the locations.
  217.    #
  218.    #  Recurse works only when the path points to a container that has child items, such as C:\Windows or C:\Windows\*, and not when it points to items that do not have child items, such as C:\Windows\*.dll
  219.    [CmdletBinding()]
  220.    param(
  221.       [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true, Position=0)]
  222.       [Alias("PSPath")]
  223.       [string[]]$Path = ".",
  224.  
  225.       [Alias("Types")]
  226.       [Switch]$Passthru,
  227.  
  228.       [Switch]$Recurse
  229.    )
  230.    process {
  231.       foreach($file in Get-ChildItem $Path -Filter *.dll -Recurse:$Recurse) {
  232.          Add-Type -Path $file.FullName -Passthru:$Passthru | Where { $_.IsPublic }
  233.       }
  234.    }
  235. }
  236.  
  237. function Get-Assembly {
  238.    <#
  239.    .Synopsis
  240.       Get a list of assemblies available in the runspace
  241.    .Description
  242.       Returns AssemblyInfo for all the assemblies available in the current AppDomain, optionally filtered by partial name match
  243.    .Parameter Name
  244.       A regex to filter the returned assemblies. This is matched against the .FullName or Location (path) of the assembly.
  245.    #>
  246.    [CmdletBinding()]
  247.    param(
  248.       [Parameter(ValueFromPipeline=$true, Position=0)]
  249.       [string[]]$Name = ''
  250.    )
  251.    process {
  252.       [appdomain]::CurrentDomain.GetAssemblies() | Where {
  253.          $Assembly = $_
  254.          if($Name){
  255.             $(
  256.                foreach($n in $Name){
  257.                   if(Resolve-Path $n -ErrorAction 0) {
  258.                      $n = [Regex]::Escape( (Resolve-Path $n).Path )
  259.                   }
  260.                   $Assembly.FullName -match $n -or $Assembly.Location -match $n -or ($Assembly.Location -and (Split-Path $Assembly.Location) -match $n)
  261.                }
  262.             ) -contains $True
  263.          } else { $true }
  264.       }        
  265.    }
  266. }
  267.  
  268. function Update-PSBoundParameters {
  269.    #.Synopsis
  270.    #  Ensure a parameter value is set
  271.    #.Description
  272.    #  Update-PSBoundParameters takes the name of a parameter, a default value, and optionally a min and max value, and ensures that PSBoundParameters has a value for it.
  273.    #.Parameter Name
  274.    #  The name (key) of the parameter you want to set in PSBoundParameters
  275.    #.Parameter Default
  276.    #  A Default value for the parameter, in case it's not already set
  277.    #.Parameter Min
  278.    #  The Minimum allowed value for the parameter
  279.    #.Parameter Max
  280.    #  The Maximum allowed value for the parameter
  281.    #.Parameter PSBoundParameters
  282.    #  The PSBoundParameters you want to affect (this picks the local PSBoundParameters object, so you shouldn't have to set it)
  283.    Param(
  284.       [Parameter(Mandatory=$true,  Position=0)]
  285.       [String]$Name,
  286.  
  287.       [Parameter(Mandatory=$false, Position=1)]
  288.       $Default,
  289.  
  290.       [Parameter()]
  291.       $Min,
  292.  
  293.       [Parameter()]
  294.       $Max,
  295.  
  296.       [Parameter(Mandatory=$true, Position=99)]
  297.       $PSBoundParameters=$PSBoundParameters
  298.    )
  299.    end {
  300.       $outBuffer = $null
  301.       ## If it's not set, and you passed a default, we set it to the default
  302.       if($Default) {
  303.          if (!$PSBoundParameters.TryGetValue($Name, [ref]$outBuffer))
  304.          {
  305.             $PSBoundParameters[$Name] = $Default
  306.          }
  307.       }
  308.       ## If you passed a $max, and it's set greater than $max, we set it to $max
  309.       if($Max) {
  310.          if ($PSBoundParameters.TryGetValue($Name, [ref]$outBuffer) -and $outBuffer -gt $Max)
  311.          {
  312.             $PSBoundParameters[$Name] = $Max
  313.          }
  314.       }
  315.       ## If you passed a $min, and it's set less than $min, we set it to $min
  316.       if($Min) {
  317.          if ($PSBoundParameters.TryGetValue($Name, [ref]$outBuffer) -and $outBuffer -lt $Min)
  318.          {
  319.             $PSBoundParameters[$Name] = $Min
  320.          }
  321.       }
  322.       $PSBoundParameters
  323.    }
  324. }
  325.  
  326. function Get-Constructor {
  327.    <#
  328.    .Synopsis
  329.       Returns RuntimeConstructorInfo for the (public) constructor methods of the specified Type.
  330.    .Description
  331.       Get the RuntimeConstructorInfo for a type and add members "Syntax," "SimpleSyntax," and "Definition" to each one containing the syntax information that can use to call that constructor.
  332.    .Parameter Type
  333.       The type to get the constructor for
  334.    .Parameter Force
  335.       Force inclusion of Private and Static constructors which are hidden by default.
  336.    .Parameter NoWarn
  337.       Serves as the replacement for the broken -WarningAction. If specified, no warnings will be written for types without public constructors.
  338.    .Example
  339.       Get-Constructor System.IO.FileInfo
  340.      
  341.       Description
  342.       -----------
  343.       Gets all the information about the single constructor for a FileInfo object.
  344.    .Example
  345.       Get-Type System.IO.*info mscorlib | Get-Constructor -NoWarn | Select Syntax
  346.      
  347.       Description
  348.       -----------
  349.       Displays the constructor syntax for all of the *Info objects in the System.IO namespace.
  350.       Using -NoWarn supresses the warning about System.IO.FileSystemInfo not having constructors.
  351.      
  352.    .Example
  353.       $path = $pwd
  354.       $driveName = $pwd.Drive
  355.       $fileName = "$Profile"
  356.       Get-Type System.IO.*info mscorlib | Get-Constructor -NoWarn | ForEach-Object { Invoke-Expression $_.Syntax }
  357.      
  358.       Description
  359.       -----------
  360.       Finds and invokes the constructors for DirectoryInfo, DriveInfo, and FileInfo.
  361.       Note that we pre-set the parameters for the constructors, otherwise they would fail with null arguments, so this example isn't really very practical.
  362.  
  363.  
  364.    #>
  365.    [CmdletBinding()]
  366.    param(
  367.       [Parameter(Mandatory=$true, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$true, Position=0)]
  368.       [Alias("ParameterType")]
  369.       [Type]$Type,
  370.       [Switch]$Force,
  371.       [Switch]$NoWarn
  372.    )
  373.    process {
  374.       $type.GetConstructors() | Where-Object { $Force -or $_.IsPublic -and -not $_.IsStatic } -OutVariable ctor
  375.       if(!$ctor -and !$NoWarn) { Write-Warning "There are no public constructors for $($type.FullName)" }
  376.    }
  377. }
  378.  
  379. function Get-ExtensionMethod {
  380.    <#
  381.       .Synopsis
  382.          Finds Extension Methods which target the specified type
  383.       .Example
  384.          Get-ExtensionMethod String
  385.  
  386.          Finds all extension methods which target strings
  387.    #>
  388.    [CmdletBinding()]
  389.    param(
  390.       # The type name to find Extension Methods for
  391.       [Parameter(Mandatory=$false,Position=0)]
  392.       [SupportsWildCards()]
  393.       [String[]]$TargetTypeName,
  394.  
  395.       # A filter for the Extension Method name
  396.       [Parameter(Mandatory=$false)]
  397.       [SupportsWildCards()]
  398.       [String[]]$Name = "*",
  399.  
  400.       # The type to search for Extension Methods (defaults to search all types)
  401.       [Parameter(Mandatory=$false,Position=99)]
  402.       [SupportsWildCards()]
  403.       [String[]]$TypeName = "*"
  404.    )
  405.    process {
  406.       Get-Type -TypeName $TypeName -Attribute ExtensionAttribute |
  407.          Get-Method -Name $Name -BindingFlags "Static,Public,NonPublic" -Attribute ExtensionAttribute |
  408.          ForEach-Object {
  409.             $Method = $_
  410.             $ParameterType = $_.GetParameters()[0].ParameterType
  411.  
  412.             ForEach($T in $TargetTypeName) {
  413.                Write-Verbose "Is '$T' a '$ParameterType'?"
  414.                if($ParameterType.Name -like $T -or $ParameterType.FullName -like $T) {
  415.                   Write-Verbose "The name '$T' matches '$ParameterType'"
  416.                   Add-Member -Input $Method -Type NoteProperty -Name ParamBlock -Value (Get-MemberSignature $Method -ParamBlock) -Force
  417.                   Write-Output $Method
  418.                   continue
  419.                }
  420.                
  421.                if($ParameterType.IsGenericType) {
  422.                   $interface = $null
  423.                   if(Test-AssignableToGeneric $T $ParameterType -interface ([ref]$interface)) {
  424.                   # if([GenericHelper]::IsAssignableToGenericType( $T, $ParameterType )) {
  425.                      Write-Verbose "'$T' is a generic that's assignable to '$ParameterType'"
  426.                      Add-Member -Input $Method -Type NoteProperty -Name Extends -Value $interface.Value -Force
  427.                      Add-Member -Input $Method -Type NoteProperty -Name ParamBlock -Value (Get-MemberSignature $Method -GenericArguments $interface.GetGenericArguments() -ParamBlock) -Force
  428.                      Write-Output $Method
  429.                      continue
  430.                   }
  431.                } else {
  432.                   if($ParameterType.IsAssignableFrom($T)) {
  433.                      Write-Verbose "'$ParameterType' is assignable from '$T'"
  434.                      Add-Member -Input $Method -Type NoteProperty -Name ParamBlock -Value (Get-MemberSignature $Method -ParamBlock) -Force
  435.                      Write-Output $Method
  436.                      continue
  437.                   }    
  438.                }
  439.             }
  440.          }
  441.    }
  442. }
  443.  
  444. function Test-AssignableToGeneric {
  445.    <#
  446.       .Synopsis
  447.          Determine if a specific type can be cast to the given generic type
  448.    #>
  449.    param(
  450.       # The concrete type you want to test generics against
  451.       [Parameter(Position=0, Mandatory = $true)]
  452.       [Type]$type,
  453.  
  454.       # A Generic Type to test
  455.       [Parameter(ValueFromPipeline=$true, Position=1, Mandatory = $true)]
  456.       [Type]$genericType,
  457.  
  458.       # Check the GenericTypeDefinition of the GenericType (in case it's typed)
  459.       [Switch]$force,
  460.  
  461.       # If the type is assignable because of an interface, return that interface here
  462.       [Parameter(Position=2)]
  463.       [ref]$interface = [ref]$null
  464.    )
  465.  
  466.    process {
  467.       $interfaces = $type.GetInterfaces()
  468.       if($type.IsGenericType -and ($type.GetGenericTypeDefinition().equals($genericType))) {
  469.          return $true
  470.       }
  471.  
  472.       foreach($i in $interfaces)
  473.       {
  474.          if($i.IsGenericType -and $i.GetGenericTypeDefinition().Equals($genericType)) {
  475.             $interface.Value = $i
  476.             return $true
  477.          }
  478.          if($i.IsGenericType -and $i.GetGenericTypeDefinition().Equals($genericType.GetGenericTypeDefinition())) {
  479.             $genericTypeArgs = @($genericType.GetGenericArguments())[0]
  480.             if(($genericTypeArgs.IsGenericParameter -and
  481.                 $genericTypeArgs.BaseType.IsAssignableFrom( @($i.GetGenericArguments())[0] ) ) -or
  482.                $genericTypeArgs.IsAssignableFrom( @($i.GetGenericArguments())[0] )) {
  483.                
  484.                $interface.Value = $i
  485.                return $true
  486.             }
  487.          }
  488.       }
  489.       if($force -and $genericType -ne $genericType.GetGenericTypeDefinition()) {
  490.          if(Test-AssignableToGeneric $type $genericType.GetGenericTypeDefinition()) {
  491.             return $true
  492.          }
  493.       }
  494.  
  495.       $base = $type.BaseType
  496.       if(!$base) { return $false }
  497.  
  498.       Test-AssignableToGeneric $base $genericType
  499.    }
  500. }
  501.  
  502. function Get-Method {
  503.    <#
  504.    .Synopsis
  505.       Returns MethodInfo for the (public) methods of the specified Type.
  506.    .Description
  507.       Get the MethodInfo for a type and add members "Syntax," "SimpleSyntax," and "Definition" to each one containing the syntax information that can use to call that method.
  508.    .Parameter Type
  509.    .Parameter Name
  510.      
  511.    .Parameter Force
  512.    #>
  513.    [CmdletBinding(DefaultParameterSetName="Type")]
  514.    param(
  515.       # The type to get methods from
  516.       [Parameter(ParameterSetName="Type", Mandatory=$true, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$true, Position=0)]
  517.       [Type]$Type,
  518.       # The name(s) of the method(s) you want to retrieve (Accepts Wildcard Patterns)
  519.       [Parameter(Mandatory=$false, Position=1)]
  520.       [SupportsWildCards()]
  521.       [PSDefaultValue(Help='*')]
  522.       [String[]]$Name ="*",
  523.       # Force inclusion of Private methods and property accessors which are hidden by default.
  524.       [Switch]$Force,
  525.       # The Binding Flags filter the output. defaults to returning all methods, static or instance
  526.       [PSDefaultValue(Help='Instance,Static,Public')]
  527.       [System.Reflection.BindingFlags]$BindingFlags = $(if($Force){"Instance,Static,Public,NonPublic"} else {"Instance,Static,Public"}),
  528.  
  529.       # An Custom Attribute which should decorate the class
  530.       [Parameter(Mandatory=$false)]
  531.       [SupportsWildCards()]
  532.       [String[]]$Attribute
  533.  
  534.    )
  535.    process {
  536.       Write-Verbose "[$($type.FullName)].GetMethods(`"$BindingFlags`")"
  537.       Write-Verbose "[$($type.FullName)].GetConstructors(`"$BindingFlags`")"
  538.       Write-Verbose "Filter by Name -like '$Name'"
  539.  
  540.      
  541.       $Type.GetMethods($BindingFlags) + $type.GetConstructors($BindingFlags) | Where-Object {
  542.          # Hide the Property accessor methods
  543.          ($Force -or !$_.IsSpecialName -or $_.Name -notmatch "^get_|^set_") -AND
  544.          # And Filter by Name, if necessary
  545.          ($Name -eq "*" -or ($( foreach($n in $Name) { $_.Name -like $n } ) -contains $True)) -AND
  546.          (!$Attribute -or $( foreach($n in $Attribute) { $_.CustomAttributes | ForEach { $_.AttributeType.Name -like $n -or $_.AttributeType.FullName -like $n } } ) -contains $True )
  547.       }
  548.    }
  549. }
  550.  
  551.  
  552.  
  553.  
  554. if(!($RMI = Get-TypeData System.Reflection.RuntimeMethodInfo) -or !$RMI.Members.ContainsKey("TypeName")) {
  555.    Update-TypeData -TypeName System.Reflection.RuntimeMethodInfo -MemberName "TypeName" -MemberType ScriptProperty -Value { $this.ReflectedType.FullName }
  556.    Update-TypeData -TypeName System.Reflection.RuntimeMethodInfo -MemberName "Definition" -MemberType ScriptProperty -Value { Get-MemberSignature $this -Simple }
  557.    Update-TypeData -TypeName System.Reflection.RuntimeMethodInfo -MemberName "Syntax" -MemberType AliasProperty -Value "Definition"
  558.    Update-TypeData -TypeName System.Reflection.RuntimeMethodInfo -MemberName "SafeSyntax" -MemberType ScriptProperty -Value { Get-MemberSignature $this }
  559. }
  560.  
  561. function Get-MemberSignature {
  562.    <#
  563.       .Synopsis
  564.          Get the powershell signature for calling a member.
  565.    #>
  566.    [CmdletBinding(DefaultParameterSetName="CallSignature")]
  567.    param(
  568.       # The Method we're getting the signature for
  569.       [Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=0)]
  570.       [System.Reflection.MethodBase]$MethodBase,
  571.  
  572.       [Parameter(Mandatory=$false, Position=1)]
  573.       [Type[]]$GenericArguments,
  574.      
  575.       # Return the simplified markup
  576.       [Parameter(ParameterSetName="CallSignature")]
  577.       [Switch]$Simple,
  578.      
  579.       # Return a param block
  580.       [Parameter(ParameterSetName="ParamBlock")]
  581.       [Switch]$ParamBlock
  582.    )
  583.    process {
  584.       if($PSCmdlet.ParameterSetName -eq "ParamBlock") { $Simple = $true }
  585.  
  586.       $parameters = $(
  587.          foreach($param in $MethodBase.GetParameters()) {
  588.             # Write-Host $param.ParameterType.FullName.TrimEnd('&'), $param.Name -fore cyan
  589.                    # Write-Verbose "$($param.ParameterType.UnderlyingSystemType.FullName) - $($param.ParameterType)"
  590.             $paramType = $param.ParameterType
  591.  
  592.             Write-Verbose "$(if($paramType.IsGenericType){'Generic: '})$($GenericArguments)"
  593.             if($paramType.IsGenericType -and $GenericArguments) {
  594.                try {
  595.                   $paramType = $paramType.GetGenericTypeDefinition().MakeGenericType( $GenericArguments )
  596.                } catch { continue }
  597.             }
  598.          
  599.             if($paramType.Name.EndsWith('&')) { $ref = '[ref]' } else { $ref = '' }
  600.             if($paramType.IsArray) { $array = ',' } else { $array = '' }
  601.             if($ParamBlock) {
  602.                '[Parameter(Mandatory=$true)]{0}[{1}]${2}' -f $ref, $paramType.ToString().TrimEnd('&'), $param.Name
  603.             } elseif($Simple) {
  604.                '[{0}] {2}' -f $paramType.ToString().TrimEnd('&'), $param.Name
  605.             } else {
  606.                '{0}({1}[{2}]${3})' -f $ref, $array, $paramType.ToString().TrimEnd('&'), $param.Name
  607.             }
  608.          }
  609.       )
  610.       if($PSCmdlet.ParameterSetName -eq "ParamBlock") {
  611.          $parameters -join ', '
  612.       } elseif($MethodBase.IsConstructor) {
  613.          "New-Object $($MethodBase.ReflectedType.FullName) $($parameters -join ', ')"
  614.       } elseif($Simple) {
  615.          "$($MethodBase.ReturnType.FullName) $($MethodBase.Name)($($parameters -join ', '))"
  616.       } elseif($MethodBase.IsStatic) {
  617.          "[$($MethodBase.ReturnType.FullName)] [$($MethodBase.ReflectedType.FullName)]::$($MethodBase.Name)($($parameters -join ', '))"
  618.       } else {
  619.          "[$($MethodBase.ReturnType.FullName)] `$$($MethodBase.ReflectedType.Name)Object.$($MethodBase.Name)($($parameters -join ', '))"
  620.       }
  621.    }
  622. }
  623.  
  624. function Read-Choice {
  625.    [CmdletBinding()]
  626.    param(
  627.       [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)]
  628.       [hashtable[]]$Choices,
  629.  
  630.       [Parameter(Mandatory=$False)]
  631.       [string]$Caption = "Please choose!",
  632.  
  633.       [Parameter(Mandatory=$False)]
  634.       [string]$Message = "Choose one of the following options:",
  635.  
  636.       [Parameter(Mandatory=$False)]
  637.       [int[]]$Default  = 0,
  638.  
  639.       [Switch]$MultipleChoice,
  640.  
  641.       [Switch]$Passthru
  642.    )
  643.    begin {
  644.       [System.Collections.DictionaryEntry[]]$choices = $choices | % { $_.GetEnumerator() }
  645.    }
  646.    process {
  647.       $Descriptions = [System.Management.Automation.Host.ChoiceDescription[]]( $(
  648.                         foreach($choice in $choices) {
  649.                            New-Object System.Management.Automation.Host.ChoiceDescription $choice.Key,$choice.Value
  650.                         }
  651.                       ) )
  652.  
  653.       if(!$MultipleChoice) { [int]$Default = $Default[0] }
  654.  
  655.       [int[]]$Answer = $Host.UI.PromptForChoice($Caption,$Message,$Descriptions,$Default)
  656.  
  657.       if($Passthru) {
  658.          Write-Verbose "$Answer"
  659.          Write-Output  $Descriptions[$Answer]
  660.       } else {
  661.          Write-Output $Answer
  662.       }
  663.    }
  664. }
  665.  
  666. function Read-Choice {
  667.    <#
  668.       .Synopsis
  669.         Prompt the user for a choice, and return the (0-based) index of the selected item
  670.       .Parameter Message
  671.         This is the prompt that will be presented to the user. Basically, the question you're asking.
  672.       .Parameter Choices
  673.         An array of strings representing the choices (or menu items), with optional ampersands (&) in them to mark (unique) characters which can be used to select each item.
  674.       .Parameter ChoicesWithHelp
  675.         A Hashtable where the keys represent the choices (or menu items), with optional ampersands (&) in them to mark (unique) characters which can be used to select each item, and the values represent help text to be displayed to the user when they ask for help making their decision.
  676.       .Parameter Default
  677.         The (0-based) index of the menu item to select by default (defaults to zero).
  678.       .Parameter MultipleChoice
  679.         Prompt the user to select more than one option. This changes the prompt display for the default PowerShell.exe host to show the options in a column and allows them to choose multiple times.
  680.         Note: when you specify MultipleChoice you may also specify multiple options as the default!
  681.       .Parameter Caption
  682.         An additional caption that can be displayed (usually above the Message) as part of the prompt
  683.       .Parameter Passthru
  684.         Causes the Choices objects to be output instead of just the indexes
  685.       .Example
  686.         Read-Choice "WEBPAGE BUILDER MENU"  "&Create Webpage","&View HTML code","&Publish Webpage","&Remove Webpage","E&xit"
  687.       .Example
  688.         [bool](Read-Choice "Do you really want to do this?" "&No","&Yes" -Default 1)
  689.        
  690.         This example takes advantage of the 0-based index to convert No (0) to False, and Yes (1) to True. It also specifies YES as the default, since that's the norm in PowerShell.
  691.       .Example
  692.         Read-Choice "Do you really want to delete them all?" @{"&No"="Do not delete all files. You will be prompted to delete each file individually."; "&Yes"="Confirm that you want to delete all of the files"}
  693.        
  694.         Note that with hashtables, order is not guaranteed, so "Yes" will probably be the first item in the prompt, and thus will output as index 0.  Because of thise, when a hashtable is passed in, we default to Passthru output.
  695.    #>
  696.    [CmdletBinding(DefaultParameterSetName="HashtableWithHelp")]
  697.    param(
  698.       [Parameter(Mandatory=$true, Position = 10, ParameterSetName="HashtableWithHelp")]
  699.       [Hashtable]$ChoicesWithHelp
  700.    ,  
  701.       [Parameter(Mandatory=$true, Position = 10, ParameterSetName="StringArray")]
  702.       [String[]]$Choices
  703.    ,
  704.       [Parameter(Mandatory=$False)]
  705.       [string]$Caption = "Please choose!"
  706.    ,  
  707.       [Parameter(Mandatory=$False, Position=0)]
  708.       [string]$Message = "Choose one of the following options:"
  709.    ,  
  710.       [Parameter(Mandatory=$False)]
  711.       [int[]]$Default  = 0
  712.    ,  
  713.       [Switch]$MultipleChoice
  714.    ,
  715.       [Switch]$Passthru
  716.    )
  717.    begin {
  718.       if($ChoicesWithHelp) {
  719.          [System.Collections.DictionaryEntry[]]$choices = $ChoicesWithHelp.GetEnumerator() | %{$_}
  720.       }
  721.    }
  722.    process {
  723.       $Descriptions = [System.Management.Automation.Host.ChoiceDescription[]]( $(
  724.                         if($choices -is [String[]]) {
  725.                            foreach($choice in $choices) {
  726.                               New-Object System.Management.Automation.Host.ChoiceDescription $choice
  727.                            }
  728.                         } else {
  729.                            foreach($choice in $choices) {
  730.                               New-Object System.Management.Automation.Host.ChoiceDescription $choice.Key, $choice.Value
  731.                            }
  732.                         }
  733.                       ) )
  734.                      
  735.       # Passing an array as the $Default triggers multiple choice prompting.
  736.       if(!$MultipleChoice) { [int]$Default = $Default[0] }
  737.  
  738.       [int[]]$Answer = $Host.UI.PromptForChoice($Caption,$Message,$Descriptions,$Default)
  739.  
  740.       if($Passthru -or !($choices -is [String[]])) {
  741.          Write-Verbose "$Answer"
  742.          Write-Output  $Descriptions[$Answer]
  743.       } else {
  744.          Write-Output $Answer
  745.       }
  746.    }
  747.  
  748. }
  749.  
  750. function Get-Argument {
  751.    param(
  752.       [Type]$Target,
  753.         [ref]$Method,
  754.         [Array]$Arguments
  755.    )
  756.    end {
  757.       trap {
  758.          write-error $_
  759.          break
  760.       }
  761.  
  762.       $flags = [System.Reflection.BindingFlags]"public,ignorecase,invokemethod,instance"
  763.  
  764.       [Type[]]$Types = @(
  765.          foreach($arg in $Arguments) {
  766.             if($arg -is [type]) {
  767.                $arg
  768.             }
  769.             else {
  770.                $arg.GetType()
  771.             }
  772.          }
  773.       )
  774.       try {
  775.          Write-Verbose "[$($Target.FullName)].GetMethod('$($Method.Value)', [$($Flags.GetType())]'$flags', `$null, ([Type[]]($(@($Types|%{$_.Name}) -join ','))), `$null)"
  776.          $MethodBase = $Target.GetMethod($($Method.Value), $flags, $null, $types, $null)
  777.          $Arguments
  778.          if($MethodBase) {
  779.             $Method.Value = $MethodBase.Name
  780.          }
  781.       } catch { }
  782.      
  783.       if(!$MethodBase) {
  784.          Write-Verbose "Try again to get $($Method.Value) Method on $($Target.FullName):"
  785.          $MethodBase = Get-Method $target $($Method.Value)
  786.          if(@($MethodBase).Count -gt 1) {
  787.             $i = 0
  788.             $i = Read-Choice -Choices $(foreach($mb in $MethodBase) { @{ "$($mb.SafeSyntax) &$($i = $i+1;$i)`b`n" =  $mb.SafeSyntax } }) -Default ($MethodBase.Count-1) -Caption "Choose a Method." -Message "Please choose which method overload to invoke:"
  789.             [System.Reflection.MethodBase]$MethodBase = $MethodBase[$i]
  790.          }
  791.          
  792.          
  793.          ForEach($parameter in $MethodBase.GetParameters()) {
  794.             $found = $false
  795.             For($a =0;$a -lt $Arguments.Count;$a++) {
  796.                if($argument[$a] -as $parameter.ParameterType) {
  797.                   Write-Output $argument[$a]
  798.                   if($a -gt 0 -and $a -lt $Arguments.Count) {
  799.                      $Arguments = $Arguments | Select -First ($a-1) -Last ($Arguments.Count -$a)
  800.                   } elseif($a -eq 0) {
  801.                      $Arguments = $Arguments | Select -Last ($Arguments.Count - 1)
  802.                   } else { # a -eq count
  803.                      $Arguments = $Arguments | Select -First ($Arguments.Count - 1)
  804.                   }
  805.                   $found = $true
  806.                   break
  807.                }
  808.             }
  809.             if(!$Found) {
  810.                $userInput = Read-Host "Please enter a [$($parameter.ParameterType.FullName)] value for $($parameter.Name)"
  811.                if($userInput -match '^{.*}$' -and !($userInput -as $parameter.ParameterType)) {
  812.                   Write-Output ((Invoke-Expression $userInput) -as $parameter.ParameterType)
  813.                } else {
  814.                   Write-Output ($userInput -as $parameter.ParameterType)
  815.                }
  816.             }
  817.          }
  818.       }
  819.    }
  820. }
  821.  
  822. function Invoke-Member {
  823.    [CmdletBinding()]
  824.    param(        
  825.       [parameter(position=10, valuefrompipeline=$true, mandatory=$true)]
  826.       [allowemptystring()]
  827.       $InputObject,
  828.  
  829.       [parameter(position=0, mandatory=$true)]
  830.       [validatenotnullorempty()]
  831.       $Member,
  832.  
  833.       [parameter(position=1, valuefromremainingarguments=$true)]
  834.       [allowemptycollection()]
  835.       $Arguments,
  836.  
  837.       [parameter()]
  838.       [switch]$Static
  839.    )
  840.    #  begin {
  841.       #  if(!(get-member SafeSyntax -input $Member -type Property)){
  842.          #  if(get-member Name -inpup $Member -Type Property) {
  843.             #  $Member = Get-Method $InputObject $Member.Name
  844.          #  } else {
  845.             #  $Member = Get-Method $InputObject $Member
  846.          #  }
  847.       #  }
  848.       #  $SafeSyntax = [ScriptBlock]::Create( $Member.SafeSyntax )
  849.    #  }
  850.    process {
  851.       #  if ($InputObject)
  852.       #  {
  853.          #  if ($InputObject | Get-Member $Member -static:$static)
  854.          #  {
  855.  
  856.             if ($InputObject -is [type]) {
  857.                 $target = $InputObject
  858.             } else {
  859.                 $target = $InputObject.GetType()
  860.             }
  861.          
  862.             if(Get-Member $Member -InputObject $InputObject -Type Properties) {
  863.                $_.$Member
  864.             }
  865.             elseif($Member -match "ctor|constructor") {
  866.                $Member = ".ctor"
  867.                [System.Reflection.BindingFlags]$flags = "CreateInstance"
  868.                $InputObject = $Null
  869.             }
  870.             else {
  871.                [System.Reflection.BindingFlags]$flags = "IgnoreCase,Public,InvokeMethod"
  872.                if($Static) { $flags = "$Flags,Static" } else { $flags = "$Flags,Instance" }
  873.             }
  874.             [ref]$Member = $Member
  875.             [Object[]]$Parameters = Get-Argument $Target $Member $Arguments
  876.             [string]$Member = $Member.Value
  877.  
  878.             Write-Verbose $(($Parameters | %{ '[' + $_.GetType().FullName + ']' + $_ }) -Join ", ")
  879.  
  880.             try {
  881.                Write-Verbose "Invoking $Member on [$target]$InputObject with [$($Flags.GetType())]'$flags' and [$($Parameters.GetType())]($($Parameters -join ','))"
  882.                Write-Verbose "[$($target.FullName)].InvokeMember('$Member', [System.Reflection.BindingFlags]'$flags', `$null, '$InputObject', ([object[]]($(($Parameters | %{ '[' + $_.GetType().FullName + ']''' + $_ + ''''}) -join', '))))"
  883.                $target.InvokeMember($Member, [System.Reflection.BindingFlags]"$flags", $null, $InputObject, $Parameters)
  884.             } catch {
  885.                Write-Warning $_.Exception
  886.                 if ($_.Exception.Innerexception -is [MissingMethodException]) {
  887.                     write-warning "Method argument count (or type) mismatch."
  888.                 }
  889.             }
  890.          #  } else {
  891.             #  write-warning "Method $Member not found."
  892.          #  }
  893.       #  }
  894.    }
  895. }
  896.  
  897. function Invoke-Generic {
  898.    #.Synopsis
  899.    #  Invoke Generic method definitions via reflection:
  900.    [CmdletBinding()]
  901.    param(
  902.       [Parameter(Position=0,Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
  903.       [Alias('On')]
  904.       $InputObject,
  905.  
  906.       [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)]
  907.       [Alias('Named')]
  908.       [string]$MethodName,
  909.  
  910.       [Parameter(Position=2)]
  911.       [Alias("Types")]
  912.       [Type[]]$ParameterTypes,
  913.  
  914.       [Parameter(Position=4, ValueFromRemainingArguments=$true, ValueFromPipelineByPropertyName=$true)]
  915.       [Object[]]$WithArgs,
  916.  
  917.       [Switch]$Static
  918.    )
  919.    begin {
  920.       if($Static) {
  921.          $BindingFlags = [System.Reflection.BindingFlags]"IgnoreCase,Public,Static"
  922.       } else {
  923.          $BindingFlags = [System.Reflection.BindingFlags]"IgnoreCase,Public,Instance"
  924.       }
  925.    }
  926.    process {
  927.       $Type = $InputObject -as [Type]
  928.       if(!$Type) { $Type = $InputObject.GetType() }
  929.      
  930.       if($WithArgs -and -not $ParameterTypes) {
  931.          $ParameterTypes = $withArgs | % { $_.GetType() }
  932.       } elseif(!$ParameterTypes) {
  933.          $ParameterTypes = [Type]::EmptyTypes
  934.       }  
  935.      
  936.      
  937.       trap { continue }
  938.       $MemberInfo = $Type.GetMethod($MethodName, $BindingFlags)
  939.       if(!$MemberInfo) {
  940.          $MemberInfo = $Type.GetMethod($MethodName, $BindingFlags, $null, $NonGenericArgumentTypes, $null)
  941.       }
  942.       if(!$MemberInfo) {
  943.          $MemberInfo = $Type.GetMethods($BindingFlags) | Where-Object {
  944.             $MI = $_
  945.             [bool]$Accept = $MI.Name -eq $MethodName
  946.             if($Accept){
  947.             Write-Verbose "$Accept = $($MI.Name) -eq $($MethodName)"
  948.                [Array]$GenericTypes = @($MI.GetGenericArguments() | Select -Expand Name)
  949.                [Array]$Parameters = @($MI.GetParameters() | Add-Member ScriptProperty -Name IsGeneric -Value {
  950.                                           $GenericTypes -Contains $this.ParameterType
  951.                                        } -Passthru)
  952.  
  953.                                        $Accept = $ParameterTypes.Count -eq $Parameters.Count
  954.                Write-Verbose "  $Accept = $($Parameters.Count) Arguments"
  955.                if($Accept) {
  956.                   for($i=0;$i -lt $Parameters.Count;$i++) {
  957.                      $Accept = $Accept -and ( $Parameters[$i].IsGeneric -or ($ParameterTypes[$i] -eq $Parameters[$i].ParameterType))
  958.                      Write-Verbose "   $Accept =$(if($Parameters[$i].IsGeneric){' GENERIC or'}) $($ParameterTypes[$i]) -eq $($Parameters[$i].ParameterType)"
  959.                   }
  960.                }
  961.             }
  962.             return $Accept
  963.          } | Sort { @($_.GetGenericArguments()).Count } | Select -First 1
  964.       }
  965.       Write-Verbose "Time to make generic methods."
  966.       Write-Verbose $MemberInfo
  967.       [Type[]]$GenericParameters = @()
  968.       [Array]$ConcreteTypes = @($MemberInfo.GetParameters() | Select -Expand ParameterType)
  969.       for($i=0;$i -lt $ParameterTypes.Count;$i++){
  970.          Write-Verbose "$($ParameterTypes[$i]) ? $($ConcreteTypes[$i] -eq $ParameterTypes[$i])"
  971.          if($ConcreteTypes[$i] -ne $ParameterTypes[$i]) {
  972.             $GenericParameters += $ParameterTypes[$i]
  973.          }
  974.          $ParameterTypes[$i] = Add-Member -in $ParameterTypes[$i] -Type NoteProperty -Name IsGeneric -Value $($ConcreteTypes[$i] -ne $ParameterTypes[$i]) -Passthru
  975.       }
  976.  
  977.        $ParameterTypes | Where-Object { $_.IsGeneric }
  978.       Write-Verbose "$($GenericParameters -join ', ') generic parameters"
  979.  
  980.       $MemberInfo = $MemberInfo.MakeGenericMethod( $GenericParameters )
  981.       Write-Verbose $MemberInfo
  982.  
  983.       if($WithArgs) {
  984.          [Object[]]$Arguments = $withArgs | %{ $_.PSObject.BaseObject }
  985.          Write-Verbose "Arguments: $(($Arguments | %{ $_.GetType().Name }) -Join ', ')"
  986.          $MemberInfo.Invoke( $InputObject, $Arguments )
  987.       } else {
  988.          $MemberInfo.Invoke( $InputObject )
  989.       }
  990.    }
  991. }
  992.  
  993. # get a reference to the Type  
  994. $xlr8r = [psobject].assembly.gettype("System.Management.Automation.TypeAccelerators")
  995.  
  996. function Import-Namespace {
  997.    [CmdletBinding()]
  998.    param(
  999.       [Parameter(ValueFromPipeline=$true)]
  1000.       [string]$Namespace,
  1001.  
  1002.       [Switch]$Force
  1003.    )
  1004.    end {
  1005.      Get-Type -Namespace $Namespace -Force:$Force | Add-Accelerator
  1006.    }
  1007. }
  1008.  
  1009. function Add-Accelerator {
  1010.    <#
  1011.       .Synopsis
  1012.          Add a type accelerator to the current session
  1013.       .Description
  1014.          The Add-Accelerator function allows you to add a simple type accelerator (like [regex]) for a longer type (like [System.Text.RegularExpressions.Regex]).
  1015.       .Example
  1016.          Add-Accelerator list System.Collections.Generic.List``1
  1017.          $list = New-Object list[string]
  1018.          
  1019.          Creates an accelerator for the generic List[T] collection type, and then creates a list of strings.
  1020.       .Example
  1021.          Add-Accelerator "List T", "GList" System.Collections.Generic.List``1
  1022.          $list = New-Object "list t[string]"
  1023.          
  1024.          Creates two accelerators for the Generic List[T] collection type.
  1025.       .Parameter Accelerator
  1026.          The short form accelerator should be just the name you want to use (without square brackets).
  1027.       .Parameter Type
  1028.          The type you want the accelerator to accelerate (without square brackets)
  1029.       .Notes
  1030.          When specifying multiple values for a parameter, use commas to separate the values.
  1031.          For example, "-Accelerator string, regex".
  1032.          
  1033.          PowerShell requires arguments that are "types" to NOT have the square bracket type notation, because of the way the parsing engine works.  You can either just type in the type as System.Int64, or you can put parentheses around it to help the parser out: ([System.Int64])
  1034.  
  1035.          Also see the help for Get-Accelerator and Remove-Accelerator
  1036.       .Link
  1037.          http://huddledmasses.org/powershell-2-ctp3-custom-accelerators-finally/
  1038.    #>
  1039.    [CmdletBinding()]
  1040.    param(
  1041.       [Parameter(Position=0,ValueFromPipelineByPropertyName=$true)]
  1042.       [Alias("Key","Name")]
  1043.       [string[]]$Accelerator,
  1044.  
  1045.       [Parameter(Position=1,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  1046.       [Alias("Value","FullName")]
  1047.       [type]$Type
  1048.    )
  1049.    process {
  1050.       # add a user-defined accelerator  
  1051.       foreach($a in $Accelerator) {
  1052.          if($xlr8r::AddReplace) {
  1053.             $xlr8r::AddReplace( $a, $Type)
  1054.          } else {
  1055.             $null = $xlr8r::Remove( $a )
  1056.             $xlr8r::Add( $a, $Type)
  1057.          }
  1058.          trap [System.Management.Automation.MethodInvocationException] {
  1059.             if($xlr8r::get.keys -contains $a) {
  1060.                if($xlr8r::get[$a] -ne $Type) {
  1061.                   Write-Error "Cannot add accelerator [$a] for [$($Type.FullName)]`n                  [$a] is already defined as [$($xlr8r::get[$a].FullName)]"
  1062.                }
  1063.                Continue;
  1064.             }
  1065.             throw
  1066.          }
  1067.       }
  1068.    }
  1069. }
  1070.  
  1071. function Get-Accelerator {
  1072.    <#
  1073.       .Synopsis
  1074.          Get one or more type accelerator definitions
  1075.       .Description
  1076.          The Get-Accelerator function allows you to look up the type accelerators (like [regex]) defined on your system by their short form or by type
  1077.       .Example
  1078.          Get-Accelerator System.String
  1079.          
  1080.          Returns the KeyValue pair for the [System.String] accelerator(s)
  1081.       .Example
  1082.          Get-Accelerator ps*,wmi*
  1083.          
  1084.          Returns the KeyValue pairs for the matching accelerator definition(s)
  1085.       .Parameter Accelerator
  1086.          One or more short form accelerators to search for (Accept wildcard characters).
  1087.       .Parameter Type
  1088.          One or more types to search for.
  1089.       .Notes
  1090.          When specifying multiple values for a parameter, use commas to separate the values.
  1091.          For example, "-Accelerator string, regex".
  1092.          
  1093.          Also see the help for Add-Accelerator and Remove-Accelerator
  1094.       .Link
  1095.          http://huddledmasses.org/powershell-2-ctp3-custom-accelerators-finally/
  1096.    #>
  1097.    [CmdletBinding(DefaultParameterSetName="ByType")]
  1098.    param(
  1099.       [Parameter(Position=0, ParameterSetName="ByAccelerator", ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
  1100.       [Alias("Key","Name")]
  1101.       [string[]]$Accelerator,
  1102.  
  1103.       [Parameter(Position=0, ParameterSetName="ByType", ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
  1104.       [Alias("Value","FullName")]
  1105.       [type[]]$Type
  1106.    )
  1107.    process {
  1108.       # add a user-defined accelerator  
  1109.       switch($PSCmdlet.ParameterSetName) {
  1110.          "ByAccelerator" {
  1111.             $xlr8r::get.GetEnumerator() | % {
  1112.                foreach($a in $Accelerator) {
  1113.                   if($_.Key -like $a) { $_ }
  1114.                }
  1115.             }
  1116.             break
  1117.          }
  1118.          "ByType" {
  1119.             if($Type -and $Type.Count) {
  1120.                $xlr8r::get.GetEnumerator() | ? { $Type -contains $_.Value }
  1121.             }
  1122.             else {
  1123.                $xlr8r::get.GetEnumerator() | %{ $_ }
  1124.             }
  1125.             break
  1126.          }
  1127.       }
  1128.    }
  1129. }
  1130.  
  1131. function Remove-Accelerator {
  1132.    <#
  1133.       .Synopsis
  1134.          Remove a type accelerator from the current session
  1135.       .Description
  1136.          The Remove-Accelerator function allows you to remove a simple type accelerator (like [regex]) from the current session. You can pass one or more accelerators, and even wildcards, but you should be aware that you can remove even the built-in accelerators.
  1137.  
  1138.       .Example
  1139.          Remove-Accelerator int
  1140.          Add-Accelerator int Int64
  1141.  
  1142.          Removes the "int" accelerator for Int32 and adds a new one for Int64. I can't recommend doing this, but it's pretty cool that it works:
  1143.  
  1144.          So now, "$(([int]3.4).GetType().FullName)" would return "System.Int64"
  1145.       .Example
  1146.          Get-Accelerator System.Single | Remove-Accelerator
  1147.  
  1148.          Removes both of the default accelerators for System.Single: [float] and [single]
  1149.       .Example
  1150.          Get-Accelerator System.Single | Remove-Accelerator -WhatIf
  1151.  
  1152.          Demonstrates that Remove-Accelerator supports -Confirm and -Whatif. Will Print:
  1153.             What if: Removes the alias [float] for type [System.Single]
  1154.             What if: Removes the alias [single] for type [System.Single]
  1155.       .Parameter Accelerator
  1156.          The short form accelerator that you want to remove (Accept wildcard characters).
  1157.       .Notes
  1158.          When specifying multiple values for a parameter, use commas to separate the values.
  1159.          For example, "-Accel string, regex".
  1160.  
  1161.          Also see the help for Add-Accelerator and Get-Accelerator
  1162.       .Link
  1163.          http://huddledmasses.org/powershell-2-ctp3-custom-accelerators-finally/
  1164.    #>
  1165.    [CmdletBinding(SupportsShouldProcess=$true)]
  1166.    param(
  1167.       [Parameter(Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
  1168.       [Alias("Key","FullName")]
  1169.       [string[]]$Accelerator
  1170.    )
  1171.    process {
  1172.       foreach($a in $Accelerator) {
  1173.          foreach($key in $xlr8r::Get.Keys -like $a) {
  1174.             if($PSCmdlet.ShouldProcess( "Removes the alias [$($Key)] for type [$($xlr8r::Get[$key].FullName)]",
  1175.                                         "Remove the alias [$($Key)] for type [$($xlr8r::Get[$key].FullName)]?",
  1176.                                         "Removing Type Accelerator" )) {
  1177.                # remove a user-defined accelerator
  1178.                $xlr8r::remove($key)  
  1179.             }
  1180.          }
  1181.       }
  1182.    }
  1183. }
  1184.  
  1185. ###############################################################################
  1186. ##### Imported from PowerBoots
  1187.  
  1188. $Script:CodeGenContentProperties = 'Content','Child','Children','Frames','Items','Pages','Blocks','Inlines','GradientStops','Source','DataPoints', 'Series', 'VisualTree'
  1189. $DependencyProperties = @{}
  1190. if(Test-Path $PSScriptRoot\DependencyPropertyCache.xml) {
  1191.         #$DependencyProperties = [System.Windows.Markup.XamlReader]::Parse( (gc $PSScriptRoot\DependencyPropertyCache.xml) )
  1192.         $DependencyProperties = Import-CliXml  $PSScriptRoot\DependencyPropertyCache.xml
  1193. }
  1194.  
  1195. function Get-ReflectionModule { $executioncontext.sessionstate.module }
  1196.  
  1197. function Set-ObjectProperties {
  1198. [CmdletBinding()]
  1199. param( $Parameters, [ref]$DObject )
  1200.  
  1201.    if($DObject.Value -is [System.ComponentModel.ISupportInitialize]) { $DObject.Value.BeginInit() }
  1202.  
  1203.    if($DebugPreference -ne "SilentlyContinue") { Write-Host; Write-Host ">>>> $($Dobject.Value.GetType().FullName)" -fore Black -back White }
  1204.    foreach ($param in $Parameters) {
  1205.       if($DebugPreference -ne "SilentlyContinue") { Write-Host "Processing Param: $($param|Out-String )" }
  1206.       ## INGORE DEPENDENCY PROPERTIES FOR NOW :)
  1207.       if($param.Key -eq "DependencyProps") {
  1208.       ## HANDLE EVENTS ....
  1209.       }
  1210.       elseif ($param.Key.StartsWith("On_")) {
  1211.          $EventName = $param.Key.SubString(3)
  1212.          if($DebugPreference -ne "SilentlyContinue") { Write-Host "Event handler $($param.Key) Type: $(@($param.Value)[0].GetType().FullName)" }
  1213.          $sb = $param.Value -as [ScriptBlock]
  1214.          if(!$sb) {
  1215.             $sb = (Get-Command $param.Value -CommandType Function,ExternalScript).ScriptBlock
  1216.          }
  1217.          $Dobject.Value."Add_$EventName".Invoke( $sb );
  1218.          # $Dobject.Value."Add_$EventName".Invoke( ($sb.GetNewClosure()) );
  1219.  
  1220.          # $Dobject.Value."Add_$EventName".Invoke( $PSCmdlet.MyInvocation.MyCommand.Module.NewBoundScriptBlock( $sb.GetNewClosure() ) );
  1221.  
  1222.  
  1223.       } ## HANDLE PROPERTIES ....
  1224.       else {
  1225.          try {
  1226.             ## TODO: File a BUG because Write-DEBUG and Write-VERBOSE die here.
  1227.             if($DebugPreference -ne "SilentlyContinue") {
  1228.                Write-Host "Setting $($param.Key) of $($Dobject.Value.GetType().Name) to $($param.Value.GetType().FullName): $($param.Value)" -fore Gray
  1229.             }
  1230.             if(@(foreach($sb in $param.Value) { $sb -is [ScriptBlock] }) -contains $true) {
  1231.                $Values = @()
  1232.                foreach($sb in $param.Value) {
  1233.                   $Values += & (Get-ReflectionModule) $sb
  1234.                }
  1235.             } else {
  1236.                $Values = $param.Value
  1237.             }
  1238.  
  1239.             if($DebugPreference -ne "SilentlyContinue") { Write-Host ([System.Windows.Markup.XamlWriter]::Save( $Dobject.Value )) -foreground green }
  1240.             if($DebugPreference -ne "SilentlyContinue") { Write-Host ([System.Windows.Markup.XamlWriter]::Save( @($Values)[0] )) -foreground green }
  1241.  
  1242.             Set-Property $Dobject $Param.Key $Values
  1243.  
  1244.             if($DebugPreference -ne "SilentlyContinue") { Write-Host ([System.Windows.Markup.XamlWriter]::Save( $Dobject.Value )) -foreground magenta }
  1245.  
  1246.             if($DebugPreference -ne "SilentlyContinue") {
  1247.                if( $Dobject.Value.$($param.Key) -ne $null ) {
  1248.                   Write-Host $Dobject.Value.$($param.Key).GetType().FullName -fore Green
  1249.                }
  1250.             }
  1251.          }
  1252.          catch [Exception]
  1253.          {
  1254.             Write-Host "COUGHT AN EXCEPTION" -fore Red
  1255.             Write-Host $_ -fore Red
  1256.             Write-Host $this -fore DarkRed
  1257.          }
  1258.       }
  1259.  
  1260.       while($DependencyProps) {
  1261.          $name, $value, $DependencyProps = $DependencyProps
  1262.          $name = ([string]@($name)[0]).Trim("-")
  1263.          if($name -and $value) {
  1264.             Set-DependencyProperty -Element $Dobject.Value -Property $name -Value $Value
  1265.          }
  1266.       }
  1267.    }
  1268.    if($DebugPreference -ne "SilentlyContinue") { Write-Host "<<<< $($Dobject.Value.GetType().FullName)" -fore Black -back White; Write-Host }
  1269.  
  1270.    if($DObject.Value -is [System.ComponentModel.ISupportInitialize]) { $DObject.Value.EndInit() }
  1271.  
  1272. }
  1273.  
  1274. function Set-Property {
  1275. PARAM([ref]$TheObject, $Name, $Values)
  1276.    $DObject = $TheObject.Value
  1277.  
  1278.    if($DebugPreference -ne "SilentlyContinue") { Write-Host ([System.Windows.Markup.XamlWriter]::Save( $DObject )) -foreground DarkMagenta }
  1279.    if($DebugPreference -ne "SilentlyContinue") { Write-Host ([System.Windows.Markup.XamlWriter]::Save( @($Values)[0] )) -foreground DarkMagenta }
  1280.  
  1281.    $PropertyType = $DObject.GetType().GetProperty($Name).PropertyType
  1282.    if('System.Windows.FrameworkElementFactory' -as [Type] -and $PropertyType -eq [System.Windows.FrameworkElementFactory] -and $DObject -is [System.Windows.FrameworkTemplate]) {
  1283.       if($DebugPreference -ne "SilentlyContinue") { Write-Host "Loading a FrameworkElementFactory" -foreground Green}
  1284.  
  1285.       # [Xml]$Template = [PoshWpf.XamlHelper]::ConvertToXaml( $DObject )
  1286.       # [Xml]$Content = [PoshWpf.XamlHelper]::ConvertToXaml( (@($Values)[0]) )
  1287.       # In .Net 3.5 the recommended way to programmatically create a template is to load XAML from a string or a memory stream using the Load method of the XamlReader class.
  1288.       [Xml]$Template = [System.Windows.Markup.XamlWriter]::Save( $DObject )
  1289.       [Xml]$Content = [System.Windows.Markup.XamlWriter]::Save( (@($Values)[0]) )
  1290.  
  1291.       $Template.DocumentElement.PrependChild( $Template.ImportNode($Content.DocumentElement, $true) ) | Out-Null
  1292.  
  1293.       $TheObject.Value = [System.Windows.Markup.XamlReader]::Parse( $Template.get_OuterXml() )
  1294.    }
  1295.    elseif('System.Windows.Data.Binding' -as [Type] -and @($Values)[0] -is [System.Windows.Data.Binding] -and !$PropertyType.IsAssignableFrom([System.Windows.Data.BindingBase])) {
  1296.       $Binding = @($Values)[0];
  1297.       if($DebugPreference -ne "SilentlyContinue") { Write-Host "$($DObject.GetType())::$Name is $PropertyType and the value is a Binding: $Binding" -fore Cyan}
  1298.  
  1299.       if(!$Binding.Source -and !$Binding.ElementName) {
  1300.          $Binding.Source = $DObject.DataContext
  1301.       }
  1302.       if($DependencyProperties.ContainsKey($Name)) {
  1303.          $field = @($DependencyProperties.$Name.Keys | Where { $DObject -is $_ -and $PropertyType -eq ([type]$DependencyProperties.$Name.$_.PropertyType)})[0] #  -or -like "*$Class" -and ($Param1.Value -as ([type]$_.PropertyType)
  1304.          if($field) {
  1305.             if($DebugPreference -ne "SilentlyContinue") { Write-Host "$($field)" -fore Blue }
  1306.             if($DebugPreference -ne "SilentlyContinue") { Write-Host "Binding: ($field)::`"$($DependencyProperties.$Name.$field.Name)`" to $Binding" -fore Blue}
  1307.  
  1308.             $DObject.SetBinding( ([type]$field)::"$($DependencyProperties.$Name.$field.Name)", $Binding ) | Out-Null
  1309.          } else {
  1310.             throw "Couldn't figure out $( @($DependencyProperties.$Name.Keys) -join ', ' )"
  1311.          }
  1312.       } else {
  1313.          if($DebugPreference -ne "SilentlyContinue") {
  1314.             Write-Host "But $($DObject.GetType())::${Name}Property is not a Dependency Property, so it probably can't be bound?" -fore Cyan
  1315.          }
  1316.          try {
  1317.  
  1318.             $DObject.SetBinding( ($DObject.GetType()::"${Name}Property"), $Binding ) | Out-Null
  1319.  
  1320.             if($DebugPreference -ne "SilentlyContinue") {
  1321.                Write-Host ([System.Windows.Markup.XamlWriter]::Save( $Dobject )) -foreground yellow
  1322.             }
  1323.          } catch {
  1324.             Write-Host "Nope, was not able to set it." -fore Red
  1325.             Write-Host $_ -fore Red
  1326.             Write-Host $this -fore DarkRed
  1327.          }
  1328.       }
  1329.    }
  1330.    elseif($PropertyType -ne [Object] -and $PropertyType.IsAssignableFrom( [System.Collections.IEnumerable] ) -and ($DObject.$($Name) -eq $null)) {
  1331.       if($Values -is [System.Collections.IEnumerable]) {
  1332.          if($DebugPreference -ne "SilentlyContinue") { Write-Host "$Name is $PropertyType which is IEnumerable, and the value is too!" -fore Cyan }
  1333.          $DObject.$($Name) = $Values
  1334.       } else {
  1335.          if($DebugPreference -ne "SilentlyContinue") { Write-Host "$Name is $PropertyType which is IEnumerable, but the value is not." -fore Cyan }
  1336.          $DObject.$($Name) = new-object "System.Collections.ObjectModel.ObservableCollection[$(@($Values)[0].GetType().FullName)]"
  1337.          $DObject.$($Name).Add($Values)
  1338.       }
  1339.    }
  1340.    elseif($DObject.$($Name) -is [System.Collections.IList]) {
  1341.       foreach ($value in @($Values)) {
  1342.          try {
  1343.             $null = $DObject.$($Name).Add($value)
  1344.          }
  1345.          catch
  1346.          {
  1347.             # Write-Host "CAUGHT array problem" -fore Red
  1348.             if($_.Exception.Message -match "Invalid cast from 'System.String' to 'System.Windows.UIElement'.") {
  1349.                $null = $DObject.$($Name).Add( (New-System.Windows.Controls.TextBlock $value) )
  1350.             } else {
  1351.                Write-Error $_.Exception
  1352.             throw
  1353.             }
  1354.          }
  1355.       }
  1356.    }
  1357.    else {
  1358.       ## If they pass an array of 1 when we only want one, we just use the first value
  1359.       if($Values -is [System.Collections.IList] -and $Values.Count -eq 1) {
  1360.          if($DebugPreference -ne "SilentlyContinue") { Write-Host "Value is an IList ($($Values.GetType().FullName))" -fore Cyan}
  1361.          if($DebugPreference -ne "SilentlyContinue") { Write-Host "But we'll just use the first ($($Values[0].GetType().FullName))" -fore Cyan}
  1362.  
  1363.          if($DebugPreference -ne "SilentlyContinue") { Write-Host ([System.Windows.Markup.XamlWriter]::Save( $Values[0] )) -foreground White}
  1364.          try {
  1365.             $DObject.$($Name) = $Values[0]
  1366.          }
  1367.          catch [Exception]
  1368.          {
  1369.             # Write-Host "CAUGHT collection value problem" -fore Red
  1370.             if($_.Exception.Message -match "Invalid cast from 'System.String' to 'System.Windows.UIElement'.") {
  1371.                $null = $DObject.$($Name).Add( (TextBlock $Values[0]) )
  1372.             }else {
  1373.                throw
  1374.             }
  1375.          }
  1376.       }
  1377.       else ## If they pass an array when we only want one, we try to use it, and failing that, cast it to strings
  1378.       {
  1379.          if($DebugPreference -ne "SilentlyContinue") { Write-Host "Value is just $Values" -fore Cyan}
  1380.          try {
  1381.             $DObject.$($Name) = $Values
  1382.          } catch [Exception]
  1383.          {
  1384.             # Write-Host "CAUGHT value problem" -fore Red
  1385.             if($_.Exception.Message -match "Invalid cast from 'System.String' to 'System.Windows.UIElement'.") {
  1386.                $null = $DObject.$($Name).Add( (TextBlock $values) )
  1387.             }else {
  1388.                throw
  1389.             }
  1390.          }
  1391.       }
  1392.    }
  1393. }
  1394.  
  1395. function Set-DependencyProperty {
  1396. [CmdletBinding()]
  1397. PARAM(
  1398.    [Parameter(Position=0,Mandatory=$true)]
  1399.    $Property,
  1400.  
  1401.    [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
  1402.    $Element,
  1403.  
  1404.    [Parameter()]
  1405.    [Switch]$Passthru
  1406. )
  1407.  
  1408. DYNAMICPARAM {
  1409.    $paramDictionary = new-object System.Management.Automation.RuntimeDefinedParameterDictionary
  1410.    $Param1 = new-object System.Management.Automation.RuntimeDefinedParameter
  1411.    $Param1.Name = "Value"
  1412.    # $Param1.Attributes.Add( (New-ParameterAttribute -Position 1) )
  1413.    $Param1.Attributes.Add( (New-Object System.Management.Automation.ParameterAttribute -Property @{ Position = 1 }) )
  1414.  
  1415.    if( $Property ) {
  1416.       if($Property.GetType() -eq ([System.Windows.DependencyProperty]) -or
  1417.          $Property.GetType().IsSubclassOf(([System.Windows.DependencyProperty])))
  1418.       {
  1419.          $Param1.ParameterType = $Property.PropertyType
  1420.       }
  1421.       elseif($Property -is [string] -and $Property.Contains(".")) {
  1422.          $Class,$Property = $Property.Split(".")
  1423.          if($DependencyProperties.ContainsKey($Property)){
  1424.             $type = $DependencyProperties.$Property.Keys -like "*$Class"
  1425.             if($type) {
  1426.                $Param1.ParameterType = [type]@($DependencyProperties.$Property.$type)[0].PropertyType
  1427.             }
  1428.          }
  1429.  
  1430.       } elseif($DependencyProperties.ContainsKey($Property)){
  1431.          if($Element) {
  1432.             if($DependencyProperties.$Property.ContainsKey( $element.GetType().FullName )) {
  1433.                $Param1.ParameterType = [type]$DependencyProperties.$Property.($element.GetType().FullName).PropertyType
  1434.             }
  1435.          } else {
  1436.             $Param1.ParameterType = [type]$DependencyProperties.$Property.Values[0].PropertyType
  1437.          }
  1438.       }
  1439.       else
  1440.       {
  1441.          $Param1.ParameterType = [PSObject]
  1442.       }
  1443.    }
  1444.    else
  1445.    {
  1446.       $Param1.ParameterType = [PSObject]
  1447.    }
  1448.    $paramDictionary.Add("Value", $Param1)
  1449.    return $paramDictionary
  1450. }
  1451. PROCESS {
  1452.    trap {
  1453.       Write-Host "ERROR Setting Dependency Property" -Fore Red
  1454.       Write-Host "Trying to set $Property to $($Param1.Value)" -Fore Red
  1455.       continue
  1456.    }
  1457.    if($Property.GetType() -eq ([System.Windows.DependencyProperty]) -or
  1458.       $Property.GetType().IsSubclassOf(([System.Windows.DependencyProperty]))
  1459.    ){
  1460.       trap {
  1461.          Write-Host "ERROR Setting Dependency Property" -Fore Red
  1462.          Write-Host "Trying to set $($Property.FullName) to $($Param1.Value)" -Fore Red
  1463.          continue
  1464.       }
  1465.       $Element.SetValue($Property, ($Param1.Value -as $Property.PropertyType))
  1466.    }
  1467.         else {
  1468.       if("$Property".Contains(".")) {
  1469.          $Class,$Property = "$Property".Split(".")
  1470.       }
  1471.  
  1472.       if( $DependencyProperties.ContainsKey("$Property" ) ) {
  1473.          $fields = @( $DependencyProperties.$Property.Keys -like "*$Class" | ? { $Param1.Value -as ([type]$DependencyProperties.$Property.$_.PropertyType) } )
  1474.                         if($fields.Count -eq 0 ) {
  1475.             $fields = @($DependencyProperties.$Property.Keys -like "*$Class" )
  1476.          }                     
  1477.          if($fields.Count) {
  1478.             $success = $false
  1479.             foreach($field in $fields) {
  1480.                trap {
  1481.                   Write-Host "ERROR Setting Dependency Property" -Fore Red
  1482.                   Write-Host "Trying to set $($field)::$($DependencyProperties.$Property.$field.Name) to $($Param1.Value) -as $($DependencyProperties.$Property.$field.PropertyType)" -Fore Red
  1483.                   continue
  1484.                }
  1485.                $Element.SetValue( ([type]$field)::"$($DependencyProperties.$Property.$field.Name)", ($Param1.Value -as ([type]$DependencyProperties.$Property.$field.PropertyType)))
  1486.                if($?) { $success = $true; break }
  1487.             }
  1488.                                
  1489.             if(!$success) {
  1490.                                         throw "food"
  1491.                                 }                              
  1492.          } else {
  1493.             Write-Host "Couldn't find the right property: $Class.$Property on $( $Element.GetType().Name ) of type $( $Param1.Value.GetType().FullName )" -Fore Red
  1494.          }
  1495.                 }
  1496.                 else {
  1497.          Write-Host "Unknown Dependency Property Key: $Property on $($Element.GetType().Name)" -Fore Red
  1498.       }
  1499.    }
  1500.        
  1501.    if( $Passthru ) { $Element }
  1502. }
  1503. }
  1504.  
  1505. function Add-Struct {
  1506.    #.Synopsis
  1507.    #   Creates Struct types from a list of types and properties
  1508.    #.Description
  1509.    #   Add-Struct is a wrapper for Add-Type to create struct types.
  1510.    #.Example
  1511.    #   New-Struct Song {
  1512.    #   [string]$Artist
  1513.    #   [string]$Album
  1514.    #   [string]$Name
  1515.    #   [TimeSpan]$Length
  1516.    #   } -CreateConstructorFunction
  1517.    #
  1518.    #   Description
  1519.    #   -----------
  1520.    #   Creates a "Song" type with strongly typed Artist, Album, Name, and Length properties, with a simple constructor and a constructor function
  1521.    #.Example
  1522.    #   New-Struct @{
  1523.    #   >> Product  = { [string]$Name; [double]$Price; }
  1524.    #   >> Order    = { [Guid]$Id; [Product]$Product; [int]$Quantity }
  1525.    #   >> Customer = {
  1526.    #   >>   [string]$FirstName
  1527.    #   >>   [string]$LastName
  1528.    #   >>   [int]$Age
  1529.    #   >>   [Order[]]$OrderHistory
  1530.    #   >> }
  1531.    #   >> }
  1532.    #   >>
  1533.    #
  1534.    #   Description
  1535.    #   -----------
  1536.    #   To create a series of related struct types (where one type is a property of another type), you need to use the -Types hashtable parameter set.  That way, all of the types will compiled together at once, so the compiler will be able to find them all.
  1537.    #
  1538.    [CmdletBinding(DefaultParameterSetName="Multiple")]
  1539.    param(
  1540.        # The name of the TYPE you are creating. Must be unique per PowerShell session.
  1541.        [ValidateScript({
  1542.            if($_ -notmatch '^[a-z][a-z1-9_]*$') {
  1543.                throw "'$_' is invalid. A valid name identifier must start with a letter, and contain only alpha-numeric or the underscore (_)."
  1544.            }
  1545.            return $true            
  1546.        })]
  1547.        [Parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName = "Single")]
  1548.        [string]$Name,
  1549.  
  1550.        # A Scriptblock full of "[Type]$Name" definitions to show what properties you want on your Struct type
  1551.        [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName = "Single")]
  1552.        [ScriptBlock]$Property,
  1553.  
  1554.        # A Hashtable in the form @{Name={Properties}} with multiple

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me