PowerShellCookbook.psm1

function Add-ExtendedFileProperties
{
    ##############################################################################
    ##
    ## Add-ExtendedFileProperties
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Add the extended file properties normally shown in Exlorer's
    "File Properties" tab.
     
    .EXAMPLE
     
    PS > Get-ChildItem | Add-ExtendedFileProperties.ps1 | Format-Table Name,"Bit Rate"
     
    #>

    
    begin
    {
        Set-StrictMode -Version 3
    
        ## Create the Shell.Application COM object that provides this
        ## functionality
        $shellObject = New-Object -Com Shell.Application
       
        ## Remember the column property mappings
        $columnMappings = @{}
    }
    
    process
    {
        ## Store the property names and identifiers for all of the shell
        ## properties
        $itemProperties = @{}
    
        ## Get the file from the input pipeline. If it is just a filename
        ## (rather than a real file,) piping it to the Get-Item cmdlet will
        ## get the file it represents.
        $fileItem = $_ | Get-Item
    
        ## Don't process directories
        if($fileItem.PsIsContainer)
        {
            $fileItem
            return
        }
    
        ## Extract the file name and directory name
        $directoryName = $fileItem.DirectoryName
        $filename = $fileItem.Name
    
        ## Create the folder object and shell item from the COM object
        $folderObject = $shellObject.NameSpace($directoryName)
        $item = $folderObject.ParseName($filename)
    
        ## Populate the item properties
        $counter = 0
        $columnName = ""
        do
        {
            if(-not $columnMappings[$counter])
            {
                $columnMappings[$counter] = $folderObject.GetDetailsOf(
                    $folderObject.Items, $counter)
            }
            
            $columnName = $columnMappings[$counter]
            if($columnName)
            {
                $itemProperties[$columnName] =
                    $folderObject.GetDetailsOf($item, $counter)
            }
    
            $counter++
        } while($columnName)
        
        ## Process extended properties
        foreach($name in
            $item.ExtendedProperty('System.PropList.FullDetails').Split(';'))
        {
            $name = $name.Replace("*","")
            $itemProperties[$name] = $item.ExtendedProperty($name)
        }
    
        ## Now, go through each property and add its information as a
        ## property to the file we are about to return
        foreach($itemProperty in $itemProperties.Keys)
        {
            $value = $itemProperties[$itemProperty]
            if($value)
            {
                $fileItem | Add-Member NoteProperty $itemProperty `
                    $value -ErrorAction `
                    SilentlyContinue
            }
        }
    
        ## Finally, return the file with the extra shell information
        $fileItem
    }
}

function Add-FormatData
{
    ##############################################################################
    ##
    ## Add-FormatData
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Adds a table formatting definition for the specified type name.
     
    .EXAMPLE
     
    PS > $r = [PSCustomObject] @{
        Name = "Lee";
        Phone = "555-1212";
        SSN = "123-12-1212"
    }
    PS > $r.PSTypeNames.Add("AddressRecord")
    PS > Add-FormatData -TypeName AddressRecord -TableColumns Name, Phone
    PS > $r
     
    Name Phone
    ---- -----
    Lee 555-1212
     
    #>

    
    param(
        ## The type name (or PSTypeName) that the table definition should
        ## apply to.
        $TypeName,
    
        ## The columns to be displayed by default
        [string[]] $TableColumns
    )
    
    Set-StrictMode -Version 3
    
    ## Define the columns within a table control row
    $rowDefinition = New-Object Management.Automation.TableControlRow
    
    ## Create left-aligned columns for each provided column name
    foreach($column in $TableColumns)
    {
        $rowDefinition.Columns.Add(
            (New-Object Management.Automation.TableControlColumn "Left", 
                (New-Object Management.Automation.DisplayEntry $column,"Property")))
    }
    
    $tableControl = New-Object Management.Automation.TableControl
    $tableControl.Rows.Add($rowDefinition)
    
    ## And then assign the table control to a new format view,
    ## which we then add to an extended type definition. Define this view for the
    ## supplied custom type name.
    $formatViewDefinition = New-Object Management.Automation.FormatViewDefinition "TableView",$tableControl
    $extendedTypeDefinition = New-Object Management.Automation.ExtendedTypeDefinition $TypeName
    $extendedTypeDefinition.FormatViewDefinition.Add($formatViewDefinition)
    
    ## Add the definition to the session, and refresh the format data
    [Runspace]::DefaultRunspace.InitialSessionState.Formats.Add($extendedTypeDefinition)
    Update-FormatData
}

function Add-FormatTableIndexParameter
{
    ##############################################################################
    ##
    ## Add-FormatTableIndexParameter
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Adds a new -IncludeIndex switch parameter to the Format-Table command
    to help with array indexing.
     
    .NOTES
     
    This commands builds on New-CommandWrapper, also included in the Windows
    PowerShell Cookbook.
     
    .EXAMPLE
     
    PS > $items = dir
    PS > $items | Format-Table -IncludeIndex
    PS > $items[4]
     
    #>

    
    Set-StrictMode -Version 3
    
    New-CommandWrapper Format-Table `
        -AddParameter @{
            @{
                Name = 'IncludeIndex';
                Attributes = "[Switch]"
            } = {
    
            function Add-IndexParameter {
                begin
                {
                    $psIndex = 0
                }
                process
                {
                    ## If this is the Format-Table header
                    if($_.GetType().FullName -eq `
                        "Microsoft.PowerShell.Commands.Internal." +
                        "Format.FormatStartData")
                    {
                        ## Take the first column and create a copy of it
                        $formatStartType =
                            $_.shapeInfo.tableColumnInfoList[0].GetType()
                        $clone =
                            $formatStartType.GetConstructors()[0].Invoke($null)
    
                        ## Add a PSIndex property
                        $clone.PropertyName = "PSIndex"
                        $clone.Width = $clone.PropertyName.Length
    
                        ## And add its information to the header information
                        $_.shapeInfo.tableColumnInfoList.Insert(0, $clone)
                    }
    
                    ## If this is a Format-Table entry
                    if($_.GetType().FullName -eq `
                        "Microsoft.PowerShell.Commands.Internal." +
                        "Format.FormatEntryData")
                    {
                        ## Take the first property and create a copy of it
                        $firstField =
                            $_.formatEntryInfo.formatPropertyFieldList[0]
                        $formatFieldType = $firstField.GetType()
                        $clone =
                            $formatFieldType.GetConstructors()[0].Invoke($null)
    
                        ## Set the PSIndex property value
                        $clone.PropertyValue = $psIndex
                        $psIndex++
    
                        ## And add its information to the entry information
                        $_.formatEntryInfo.formatPropertyFieldList.Insert(
                            0, $clone)
                    }
    
                    $_
                }
            }
    
            $newPipeline = { __ORIGINAL_COMMAND__ | Add-IndexParameter }
        }
    }
}

function Add-ObjectCollector
{
    ##############################################################################
    ##
    ## Add-ObjectCollector
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Adds a new Out-Default command wrapper to store up to 500 elements from
    the previous command. This wrapper stores output in the $ll variable.
     
    .EXAMPLE
     
    PS > Get-Command $pshome\powershell.exe
     
    CommandType Name Definition
    ----------- ---- ----------
    Application powershell.exe C:\Windows\System32\Windo...
     
    PS > $ll.Definition
    C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
     
    .NOTES
     
    This command builds on New-CommandWrapper, also included in the Windows
    PowerShell Cookbook.
     
    #>

    
    Set-StrictMode -Version 3
    
    New-CommandWrapper Out-Default `
        -Begin {
            $cachedOutput = New-Object System.Collections.ArrayList
        } `
        -Process {
            ## If we get an input object, add it to our list of objects
            if($_ -ne $null) { $null = $cachedOutput.Add($_) }
            while($cachedOutput.Count -gt 500) { $cachedOutput.RemoveAt(0) }
        } `
        -End {
            ## Be sure we got objects that were not just errors (
            ## so that we don't wipe out the saved output when we get errors
            ## trying to work with it.)
            ## Also don't caputre formatting information, as those objects
            ## can't be worked with.
            $uniqueOutput = $cachedOutput | Foreach-Object {
                $_.GetType().FullName } | Select -Unique
            $containsInterestingTypes = ($uniqueOutput -notcontains `
                "System.Management.Automation.ErrorRecord") -and
                ($uniqueOutput -notlike `
                    "Microsoft.PowerShell.Commands.Internal.Format.*")
    
            ## If we actually had output, and it was interesting information,
            ## save the output into the $ll variable
            if(($cachedOutput.Count -gt 0) -and $containsInterestingTypes)
            {
                $GLOBAL:ll = $cachedOutput | % { $_ }
            }
        }
}

function Add-RelativePathCapture
{
    ##############################################################################
    ##
    ## Add-RelativePathCapture
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Adds a new CommandNotFound handler that captures relative path
    navigation without having to explicitly call 'Set-Location'
     
    .EXAMPLE
     
    PS C:\Users\Lee\Documents>..
    PS C:\Users\Lee>...
    PS C:\>
     
    #>

    
    Set-StrictMode -Version 3
    
    $executionContext.SessionState.InvokeCommand.CommandNotFoundAction = {
        param($CommandName, $CommandLookupEventArgs)
    
        ## If the command is only dots
        if($CommandName -match '^\.+$')
        {
            ## Associate a new command that should be invoked instead
            $CommandLookupEventArgs.CommandScriptBlock = {
    
                ## Count the number of dots, and run "Set-Location .." one
                ## less time.
                for($counter = 0; $counter -lt $CommandName.Length - 1; $counter++)
                {
                    Set-Location ..
                }
    
            ## We call GetNewClosure() so that the reference to $CommandName can
            ## be used in the new command.
            }.GetNewClosure()
    
            ## Stop going through the command resolution process. This isn't
            ## strictly required in the CommandNotFoundAction.
            $CommandLookupEventArgs.StopSearch = $true
        }
    }
}

function Compare-Property
{
    ##############################################################################
    ##
    ## Compare-Property
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Compare the property you provide against the input supplied to the script.
    This provides the functionality of simple Where-Object comparisons without
    the syntax required for that cmdlet.
     
    .EXAMPLE
     
    PS Get-Process | Compare-Property Handles gt 1000
     
    .EXAMPLE
     
    PS > Set-Alias ?? Compare-Property
    PS > dir | ?? PsIsContainer
     
    #>

    
    param(
        ## The property to compare
        $Property,
    
        ## The operator to use in the comparison
        $Operator = "eq",
    
        ## The value to compare with
        $MatchText = "$true"
    )
    
    Begin { $expression = "`$_.$property -$operator `"$matchText`"" }
    Process { if(Invoke-Expression $expression) { $_ } }
}

function Connect-WebService
{
    ##############################################################################
    ##
    ## Connect-WebService
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ## Connect to a given web service, and create a type that allows you to
    ## interact with that web service. In PowerShell version two, use the
    ## New-WebserviceProxy cmdlet.
    ##
    ## Example:
    ##
    ## $wsdl = "http://www.terraserver-usa.com/TerraService2.asmx?WSDL"
    ## $terraServer = Connect-WebService $wsdl
    ## $place = New-Object Place
    ## $place.City = "Redmond"
    ## $place.State = "WA"
    ## $place.Country = "USA"
    ## $facts = $terraserver.GetPlaceFacts($place)
    ## $facts.Center
    ##
    ##############################################################################
    
    param(
        ## The URL that contains the WSDL
        [string] $WsdlLocation = $(throw "Please specify a WSDL location"),
    
        ## The namespace to use to contain the web service proxy
        [string] $Namespace,
    
        ## Switch to identify web services that require authentication
        [Switch] $RequiresAuthentication
    )
    
    ## Create the web service cache, if it doesn't already exist
    if(-not (Test-Path Variable:\Lee.Holmes.WebServiceCache))
    {
        ${GLOBAL:Lee.Holmes.WebServiceCache} = @{}
    }
    
    ## Check if there was an instance from a previous connection to
    ## this web service. If so, return that instead.
    $oldInstance = ${GLOBAL:Lee.Holmes.WebServiceCache}[$wsdlLocation]
    if($oldInstance)
    {
        $oldInstance
        return
    }
    
    ## Load the required Web Services DLL
    $null = [Reflection.Assembly]::LoadWithPartialName("System.Web.Services")
    
    ## Download the WSDL for the service, and create a service description from
    ## it.
    $wc = New-Object System.Net.WebClient
    
    if($requiresAuthentication)
    {
        $wc.UseDefaultCredentials = $true
    }
    
    $wsdlStream = $wc.OpenRead($wsdlLocation)
    
    ## Ensure that we were able to fetch the WSDL
    if(-not (Test-Path Variable:\wsdlStream))
    {
        return
    }
    
    $serviceDescription =
        [Web.Services.Description.ServiceDescription]::Read($wsdlStream)
    $wsdlStream.Close()
    
    ## Ensure that we were able to read the WSDL into a service description
    if(-not (Test-Path Variable:\serviceDescription))
    {
        return
    }
    
    ## Import the web service into a CodeDom
    $serviceNamespace = New-Object System.CodeDom.CodeNamespace
    if($namespace)
    {
        $serviceNamespace.Name = $namespace
    }
    
    $codeCompileUnit = New-Object System.CodeDom.CodeCompileUnit
    $serviceDescriptionImporter =
        New-Object Web.Services.Description.ServiceDescriptionImporter
    $serviceDescriptionImporter.AddServiceDescription(
        $serviceDescription, $null, $null)
    [void] $codeCompileUnit.Namespaces.Add($serviceNamespace)
    [void] $serviceDescriptionImporter.Import(
        $serviceNamespace, $codeCompileUnit)
    
    ## Generate the code from that CodeDom into a string
    $generatedCode = New-Object Text.StringBuilder
    $stringWriter = New-Object IO.StringWriter $generatedCode
    $provider = New-Object Microsoft.CSharp.CSharpCodeProvider
    $provider.GenerateCodeFromCompileUnit($codeCompileUnit, $stringWriter, $null)
    
    ## Compile the source code.
    $references = @("System.dll", "System.Web.Services.dll", "System.Xml.dll")
    $compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters
    $compilerParameters.ReferencedAssemblies.AddRange($references)
    $compilerParameters.GenerateInMemory = $true
    
    $compilerResults =
        $provider.CompileAssemblyFromSource($compilerParameters, $generatedCode)
    
    ## Write any errors if generated.
    if($compilerResults.Errors.Count -gt 0)
    {
        $errorLines = ""
        foreach($error in $compilerResults.Errors)
        {
            $errorLines += "`n`t" + $error.Line + ":`t" + $error.ErrorText
        }
    
        Write-Error $errorLines
        return
    }
    ## There were no errors. Create the webservice object and return it.
    else
    {
        ## Get the assembly that we just compiled
        $assembly = $compilerResults.CompiledAssembly
    
        ## Find the type that had the WebServiceBindingAttribute.
        ## There may be other "helper types" in this file, but they will
        ## not have this attribute
        $type = $assembly.GetTypes() |
            Where-Object { $_.GetCustomAttributes(
                [System.Web.Services.WebServiceBindingAttribute], $false) }
    
        if(-not $type)
        {
            Write-Error "Could not generate web service proxy."
            return
        }
    
        ## Create an instance of the type, store it in the cache,
        ## and return it to the user.
        $instance = $assembly.CreateInstance($type)
    
        ## Many services that support authentication also require it on the
        ## resulting objects
        if($requiresAuthentication)
        {
            if(@($instance.PsObject.Properties |
                where { $_.Name -eq "UseDefaultCredentials" }).Count -eq 1)
            {
                $instance.UseDefaultCredentials = $true
            }
        }
    
        ${GLOBAL:Lee.Holmes.WebServiceCache}[$wsdlLocation] = $instance
    
        $instance
    }
}

function Convert-TextObject
{
    ##############################################################################
    ##
    ## Convert-TextObject
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Convert a simple string into a custom PowerShell object.
     
    .EXAMPLE
     
    PS > "Hello World" | Convert-TextObject
    Generates an Object with "P1=Hello" and "P2=World"
     
    .EXAMPLE
     
    PS > "Hello World" | Convert-TextObject -Delimiter "ll"
    Generates an Object with "P1=He" and "P2=o World"
     
    .EXAMPLE
     
    PS > "Hello World" | Convert-TextObject -Pattern "He(ll.*o)r(ld)"
    Generates an Object with "P1=llo Wo" and "P2=ld"
     
    .EXAMPLE
     
    PS > "Hello World" | Convert-TextObject -PropertyName FirstWord,SecondWord
    Generates an Object with "FirstWord=Hello" and "SecondWord=World
     
    .EXAMPLE
     
    PS > "123 456" | Convert-TextObject -PropertyType $([string],[int])
    Generates an Object with "Property1=123" and "Property2=456"
    The second property is an integer, as opposed to a string
     
    .EXAMPLE
     
    PS > $ipAddress = (ipconfig | Convert-TextObject -Delim ": ")[2].P2
    PS > $ipAddress
    192.168.1.104
     
    #>

    
    [CmdletBinding(DefaultParameterSetName = "ByDelimiter")]
    param(
        ## If specified, gives the .NET Regular Expression with which to
        ## split the string. The script generates properties for the
        ## resulting object out of the elements resulting from this split.
        ## If not specified, defaults to splitting on the maximum amount
        ## of whitespace: "\s+", as long as Pattern is not
        ## specified either.
        [Parameter(ParameterSetName = "ByDelimiter", Position = 0)]
        [string] $Delimiter = "\s+",
    
        ## If specified, gives the .NET Regular Expression with which to
        ## parse the string. The script generates properties for the
        ## resulting object out of the groups captured by this regular
        ## expression.
        [Parameter(Mandatory = $true,
            ParameterSetName = "ByPattern",
            Position = 0)]
        [string] $Pattern,
    
        ## If specified, the script will pair the names from this object
        ## definition with the elements from the parsed string. If not
        ## specified (or the generated object contains more properties
        ## than you specify,) the script uses property names in the
        ## pattern of P1,P2,...,PN
        [Parameter(Position = 1)]
        [Alias("PN")]
        [string[]] $PropertyName = @(),
    
        ## If specified, the script will pair the types from this list with
        ## the properties from the parsed string. If not specified (or the
        ## generated object contains more properties than you specify,) the
        ## script sets the properties to be of type [string]
        [Parameter(Position = 2)]
        [Alias("PT")]
        [type[]] $PropertyType = @(),
    
        ## The input object to process
        [Parameter(ValueFromPipeline = $true)]
        [string] $InputObject
    )
    
    begin {
        Set-StrictMode -Version 3
    }
    
    process {
        $returnObject = New-Object PSObject
    
        $matches = $null
        $matchCount = 0
    
        if($PSBoundParameters["Pattern"])
        {
            ## Verify that the input contains the pattern
            ## Populates the matches variable by default
            if(-not ($InputObject -match $pattern))
            {
                return
            }
    
            $matchCount = $matches.Count
        $startIndex = 1
        }
        else
        {
            ## Verify that the input contains the delimiter
            if(-not ($InputObject -match $delimiter))
            {
                return
            }
    
            ## If so, split the input on that delimiter
            $matches = $InputObject -split $delimiter
            $matchCount = $matches.Length
            $startIndex = 0
        }
    
        ## Go through all of the matches, and add them as notes to the output
        ## object.
        for($counter = $startIndex; $counter -lt $matchCount; $counter++)
        {
            $currentPropertyName = "P$($counter - $startIndex + 1)"
            $currentPropertyType = [string]
    
            ## Get the property name
            if($counter -lt $propertyName.Length)
            {
                if($propertyName[$counter])
                {
                    $currentPropertyName = $propertyName[$counter - 1]
                }
            }
    
            ## Get the property value
            if($counter -lt $propertyType.Length)
            {
                if($propertyType[$counter])
                {
                    $currentPropertyType = $propertyType[$counter - 1]
                }
            }
    
            Add-Member -InputObject $returnObject NoteProperty `
                -Name $currentPropertyName `
                -Value ($matches[$counter].Trim() -as $currentPropertyType)
        }
    
        $returnObject
    }
}

function ConvertFrom-FahrenheitWithFunction
{
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    
    param([double] $Fahrenheit)
    
    Set-StrictMode -Version 3
    
    ## Convert Fahrenheit to Celsius
    function ConvertFahrenheitToCelsius([double] $fahrenheit)
    {
        $celsius = $fahrenheit - 32
        $celsius = $celsius / 1.8
        $celsius
    }
    
    $celsius = ConvertFahrenheitToCelsius $fahrenheit
    
    ## Output the answer
    "$fahrenheit degrees Fahrenheit is $celsius degrees Celsius."
}

function ConvertFrom-FahrenheitWithoutFunction
{
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    
    param([double] $Fahrenheit)
    
    Set-StrictMode -Version 3
    
    ## Convert it to Celsius
    $celsius = $fahrenheit - 32
    $celsius = $celsius / 1.8
    
    ## Output the answer
    "$fahrenheit degrees Fahrenheit is $celsius degrees Celsius."
}

function Copy-History
{
    ##############################################################################
    ##
    ## Copy-History
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Copy selected commands from the history buffer into the clipboard as a script.
     
    .EXAMPLE
     
    PS > Copy-History
    Copies the entire contents of the history buffer into the clipboard.
     
    .EXAMPLE
     
    PS > Copy-History -5
    Copies the last five commands into the clipboard.
     
    .EXAMPLE
     
    PS > Copy-History 2,5,8,4
    Copies commands 2,5,8, and 4.
     
    .EXAMPLE
     
    PS > Copy-History (1..10+5+6)
    Copies commands 1 through 10, then 5, then 6, using PowerShell's array
    slicing syntax.
     
    #>

    
    [CmdletBinding()]
    param(
        ## The range of history IDs to copy
        [Alias("Id")]
        [int[]] $Range
    )
    
    Set-StrictMode -Version 3
    
    $history = @()
    
    ## If they haven't specified a range, assume it's everything
    if((-not $range) -or ($range.Count -eq 0))
    {
        $history = @(Get-History -Count ([Int16]::MaxValue))
    }
    ## If it's a negative number, copy only that many
    elseif(($range.Count -eq 1) -and ($range[0] -lt 0))
    {
        $count = [Math]::Abs($range[0])
        $history = (Get-History -Count $count)
    }
    ## Otherwise, go through each history ID in the given range
    ## and add it to our history list.
    else
    {
        foreach($commandId in $range)
        {
            if($commandId -eq -1) { $history += Get-History -Count 1 }
            else { $history += Get-History -Id $commandId }
        }
    }
    
    ## Finally, export the history to the clipboard.
    $history | Foreach-Object { $_.CommandLine } | clip.exe
}

function Enable-BreakOnError
{
    #############################################################################
    ##
    ## Enable-BreakOnError
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Creates a breakpoint that only fires when PowerShell encounters an error
     
    .EXAMPLE
     
    PS > Enable-BreakOnError
     
    ID Script Line Command Variable Action
    -- ------ ---- ------- -------- ------
     0 Out-Default ...
     
    PS > 1/0
    Entering debug mode. Use h or ? for help.
     
    Hit Command breakpoint on 'Out-Default'
     
     
    PS > $error
    Attempted to divide by zero.
     
    #>

    
    Set-StrictMode -Version 3
    
    ## Store the current number of errors seen in the session so far
    $GLOBAL:EnableBreakOnErrorLastErrorCount = $error.Count
    
    Set-PSBreakpoint -Command Out-Default -Action {
    
        ## If we're generating output, and the error count has increased,
        ## break into the debugger.
        if($error.Count -ne $EnableBreakOnErrorLastErrorCount)
        {
            $GLOBAL:EnableBreakOnErrorLastErrorCount = $error.Count
            break
        }
    }
}

function Enable-HistoryPersistence
{
    ##############################################################################
    ##
    ## Enable-HistoryPersistence
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Reloads any previously saved command history, and registers for the
    PowerShell.Exiting engine event to save new history when the shell
    exits.
     
    #>

    
    Set-StrictMode -Version 3
    
    ## Load our previous history
    $GLOBAL:maximumHistoryCount = 32767
    $historyFile = (Join-Path (Split-Path $profile) "commandHistory.clixml")
    if(Test-Path $historyFile)
    {
        Import-CliXml $historyFile | Add-History
    }
    
    ## Register for the engine shutdown event
    $null = Register-EngineEvent -SourceIdentifier `
        ([System.Management.Automation.PsEngineEvent]::Exiting) -Action {
    
        ## Save our history
        $historyFile = (Join-Path (Split-Path $profile) "commandHistory.clixml")
        $maximumHistoryCount = 1kb
    
        ## Get the previous history items
        $oldEntries = @()
        if(Test-Path $historyFile)
        {
            $oldEntries = Import-CliXml $historyFile -ErrorAction SilentlyContinue
        }
    
        ## And merge them with our changes
        $currentEntries = Get-History -Count $maximumHistoryCount
        $additions = Compare-Object $oldEntries $currentEntries `
            -Property CommandLine | Where-Object { $_.SideIndicator -eq "=>" } |
            Foreach-Object { $_.CommandLine }
    
        $newEntries = $currentEntries | ? { $additions -contains $_.CommandLine }
    
        ## Keep only unique command lines. First sort by CommandLine in
        ## descending order (so that we keep the newest entries,) and then
        ## re-sort by StartExecutionTime.
        $history = @($oldEntries + $newEntries) |
            Sort -Unique -Descending CommandLine | Sort StartExecutionTime
    
        ## Finally, keep the last 100
        Remove-Item $historyFile
        $history | Select -Last 100 | Export-CliXml $historyFile
    }
}

function Enable-RemoteCredSSP
{
    ##############################################################################
    ##
    ## Enable-RemoteCredSSP
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Enables CredSSP support on a remote computer. Requires that the machine
    have PowerShell Remoting enabled, and that its operating system is Windows
    Vista or later.
     
    .EXAMPLE
     
    PS > Enable-RemoteCredSSP <Computer>
     
    #>

    
    param(
        ## The computer on which to enable CredSSP
        $Computername,
    
        ## The credential to use when connecting
        $Credential = (Get-Credential)
    )
    
    Set-StrictMode -Version 3
    
    ## Call Get-Credential again, so that the user can type something like
    ## Enable-RemoteCredSSP -Computer Computer -Cred DOMAIN\user
    $credential = Get-Credential $credential
    $username = $credential.Username
    $password = $credential.GetNetworkCredential().Password
    
    ## Define the script we will use to create the scheduled task
    $powerShellCommand =
        "powershell -noprofile -command Enable-WsManCredSSP -Role Server -Force"
    $script = @"
    schtasks /CREATE /TN 'Enable CredSSP' /SC WEEKLY /RL HIGHEST ``
        /RU $username /RP $password ``
        /TR "$powerShellCommand" /F
     
    schtasks /RUN /TN 'Enable CredSSP'
"@

    
    ## Create the task on the remote system to configure CredSSP
    $command = [ScriptBlock]::Create($script)
    Invoke-Command $computername $command -Cred $credential
    
    ## Wait for the remoting changes to come into effect
    for($count = 1; $count -le 10; $count++)
    {
        $output =
            Invoke-Command $computername { 1 } -Auth CredSSP -Cred $credential
        if($output -eq 1) { break; }
    
        "Attempt $count : Not ready yet."
        Sleep 5
    }
    
    ## Clean up
    $command = [ScriptBlock]::Create($script)
    Invoke-Command $computername {
        schtasks /DELETE /TN 'Enable CredSSP' /F } -Cred $credential
    
    ## Verify the output
    Invoke-Command $computername {
        Get-WmiObject Win32_ComputerSystem } -Auth CredSSP -Cred $credential
}

function Enable-RemotePsRemoting
{
    ##############################################################################
    ##
    ## Enable-RemotePsRemoting
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Enables PowerShell Remoting on a remote computer. Requires that the machine
    responds to WMI requests, and that its operating system is Windows Vista or
    later.
     
    .EXAMPLE
     
    PS > Enable-RemotePsRemoting <Computer>
     
    #>

    
    param(
        ## The computer on which to enable remoting
        $Computername,
    
        ## The credential to use when connecting
        $Credential = (Get-Credential)
    )
    
    Set-StrictMode -Version 3
    
    $VerbosePreference = "Continue"
    
    $credential = Get-Credential $credential
    $username = $credential.Username
    $password = $credential.GetNetworkCredential().Password
    
    $script = @"
     
    `$log = Join-Path `$env:TEMP Enable-RemotePsRemoting.output.txt
    Remove-Item -Force `$log -ErrorAction SilentlyContinue
    Start-Transcript -Path `$log
     
    ## Create a task that will run with full network privileges.
    ## In this task, we call Enable-PsRemoting
    schtasks /CREATE /TN 'Enable Remoting' /SC WEEKLY /RL HIGHEST ``
        /RU $username /RP $password ``
        /TR "powershell -noprofile -command Enable-PsRemoting -Force" /F |
        Out-String
    schtasks /RUN /TN 'Enable Remoting' | Out-String
     
    `$securePass = ConvertTo-SecureString $password -AsPlainText -Force
    `$credential =
        New-Object Management.Automation.PsCredential $username,`$securepass
     
    ## Wait for the remoting changes to come into effect
    for(`$count = 1; `$count -le 10; `$count++)
    {
        `$output = Invoke-Command localhost { 1 } -Cred `$credential ``
            -ErrorAction SilentlyContinue
        if(`$output -eq 1) { break; }
     
        "Attempt `$count : Not ready yet."
        Sleep 5
    }
     
    ## Delete the temporary task
    schtasks /DELETE /TN 'Enable Remoting' /F | Out-String
    Stop-Transcript
     
"@

    
    $commandBytes = [System.Text.Encoding]::Unicode.GetBytes($script)
    $encoded = [Convert]::ToBase64String($commandBytes)
    
    Write-Verbose "Configuring $computername"
    $command = "powershell -NoProfile -EncodedCommand $encoded"
    $null = Invoke-WmiMethod -Computer $computername -Credential $credential `
        Win32_Process Create -Args $command
    
    Write-Verbose "Testing connection"
    Invoke-Command $computername {
        Get-WmiObject Win32_ComputerSystem } -Credential $credential
}

function Enter-Module
{
    ##############################################################################
    ##
    ## Enter-Module
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Lets you examine internal module state and functions by executing user
    input in the scope of the supplied module.
     
    .EXAMPLE
     
    PS > Import-Module PersistentState
    PS > Get-Module PersistentState
     
    ModuleType Name ExportedCommands
    ---------- ---- ----------------
    Script PersistentState {Set-Memory, Get-Memory}
     
     
    PS > "Hello World" | Set-Memory
    PS > $m = Get-Module PersistentState
    PS > Enter-Module $m
    PersistentState: dir variable:\mem*
     
    Name Value
    ---- -----
    memory {Hello World}
     
    PersistentState: exit
    PS >
     
    #>

    
    param(
        ## The module to examine
        [System.Management.Automation.PSModuleInfo] $Module
    )
    
    Set-StrictMode -Version 3
    
    $userInput = Read-Host $($module.Name)
    while($userInput -ne "exit")
    {
        $scriptblock = [ScriptBlock]::Create($userInput)
        & $module $scriptblock
    
        $userInput = Read-Host $($module.Name)
    }
}

function Format-Hex
{
    ##############################################################################
    ##
    ## Format-Hex
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Outputs a file or pipelined input as a hexadecimal display. To determine the
    offset of a character in the input, add the number at the far-left of the row
    with the the number at the top of the column for that character.
     
    .EXAMPLE
     
    PS > "Hello World" | Format-Hex
     
                0 1 2 3 4 5 6 7 8 9 A B C D E F
     
    00000000 48 00 65 00 6C 00 6C 00 6F 00 20 00 57 00 6F 00 H.e.l.l.o. .W.o.
    00000010 72 00 6C 00 64 00 r.l.d.
     
    .EXAMPLE
     
    PS > Format-Hex c:\temp\example.bmp
     
    #>

    
    [CmdletBinding(DefaultParameterSetName = "ByPath")]
    param(
        ## The file to read the content from
        [Parameter(ParameterSetName = "ByPath", Position = 0)]
        [string] $Path,
    
        ## The input (bytes or strings) to format as hexadecimal
        [Parameter(
            ParameterSetName = "ByInput", Position = 0,
            ValueFromPipeline = $true)]
        [Object] $InputObject
    )
    
    begin
    {
        Set-StrictMode -Version 3
    
        ## Create the array to hold the content. If the user specified the
        ## -Path parameter, read the bytes from the path.
        [byte[]] $inputBytes = $null
        if($Path) { $inputBytes = Get-Content $Path -Encoding Byte -Raw }
    
        ## Store our header, and formatting information
        $counter = 0
        $header = " 0 1 2 3 4 5 6 7 8 9 A B C D E F"
        $nextLine = "{0} " -f  [Convert]::ToString(
            $counter, 16).ToUpper().PadLeft(8, '0')
        $asciiEnd = ""
    
        ## Output the header
        "`r`n$header`r`n"
    }
    
    process
    {
        ## If they specified the -InputObject parameter, retrieve the bytes
        ## from that input
        if($PSCmdlet.ParameterSetName -eq "ByInput")
        {
            ## If it's an actual byte, add it to the inputBytes array.
            if($InputObject -is [Byte])
            {
                $inputBytes = $InputObject
            }
            else
            {
                ## Otherwise, convert it to a string and extract the bytes
                ## from that.
                $inputString = [string] $InputObject
                $inputBytes = [Text.Encoding]::Unicode.GetBytes($inputString)
            }
        }
    
        ## Now go through the input bytes
        foreach($byte in $inputBytes)
        {
            ## Display each byte, in 2-digit hexidecimal, and add that to the
            ## left-hand side.
            $nextLine += "{0:X2} " -f $byte
    
            ## If the character is printable, add its ascii representation to
            ## the right-hand side. Otherwise, add a dot to the right hand side.
            if(($byte -ge 0x20) -and ($byte -le 0xFE))
            {
                $asciiEnd += [char] $byte
            }
            else
            {
                $asciiEnd += "."
            }
    
            $counter++;
    
            ## If we've hit the end of a line, combine the right half with the
            ## left half, and start a new line.
            if(($counter % 16) -eq 0)
            {
    
                "$nextLine $asciiEnd"
                $nextLine = "{0} " -f [Convert]::ToString(
                    $counter, 16).ToUpper().PadLeft(8, '0')
                $asciiEnd = "";
            }
        }
    }
    
    end
    {
        ## At the end of the file, we might not have had the chance to output
        ## the end of the line yet. Only do this if we didn't exit on the 16-byte
        ## boundary, though.
        if(($counter % 16) -ne 0)
        {
            while(($counter % 16) -ne 0)
            {
                $nextLine += " "
                $asciiEnd += " "
                $counter++;
            }
            "$nextLine $asciiEnd"
        }
    
        ""
    }
}

function Format-String
{
    ##############################################################################
    ##
    ## Format-String
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Replaces text in a string based on named replacement tags
     
    .EXAMPLE
     
    PS > Format-String "Hello {NAME}" @{ NAME = 'PowerShell' }
    Hello PowerShell
     
    .EXAMPLE
     
    PS > Format-String "Your score is {SCORE:P}" @{ SCORE = 0.85 }
    Your score is 85.00 %
     
    #>

    
    param(
        ## The string to format. Any portions in the form of {NAME}
        ## will be automatically replaced by the corresponding value
        ## from the supplied hashtable.
        $String,
    
        ## The named replacements to use in the string
        [hashtable] $Replacements
    )
    
    Set-StrictMode -Version 3
    
    $currentIndex = 0
    $replacementList = @()
    
    if($String -match "{{|}}")
    {
        throw "Escaping of replacement terms are not supported."
    }
    
    ## Go through each key in the hashtable
    foreach($key in $replacements.Keys)
    {
        ## Convert the key into a number, so that it can be used by
        ## String.Format
        $inputPattern = '{(.*)' + $key + '(.*)}'
        $replacementPattern = '{${1}' + $currentIndex + '${2}}'
        $string = $string -replace $inputPattern,$replacementPattern
        $replacementList += $replacements[$key]
    
        $currentIndex++
    }
    
    ## Now use String.Format to replace the numbers in the
    ## format string.
    $string -f $replacementList
}

function Get-AclMisconfiguration
{
    ##############################################################################
    ##
    ## Get-AclMisconfiguration
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstration of functionality exposed by the Get-Acl cmdlet. This script
    goes through all access rules in all files in the current directory, and
    ensures that the Administrator group has full control of that file.
     
    #>

    
    Set-StrictMode -Version 3
    
    ## Get all files in the current directory
    foreach($file in Get-ChildItem)
    {
        ## Retrieve the ACL from the current file
        $acl = Get-Acl $file
        if(-not $acl)
        {
            continue
        }
    
        $foundAdministratorAcl = $false
    
        ## Go through each access rule in that ACL
        foreach($accessRule in $acl.Access)
        {
            ## If we find the Administrator, Full Control access rule,
            ## then set the $foundAdministratorAcl variable
            if(($accessRule.IdentityReference -like "*Administrator*") -and
                ($accessRule.FileSystemRights -eq "FullControl"))
            {
                $foundAdministratorAcl = $true
            }
        }
    
        ## If we didn't find the administrator ACL, output a message
        if(-not $foundAdministratorAcl)
        {
            "Found possible ACL Misconfiguration: $file"
        }
    }
}

function Get-AliasSuggestion
{
    ##############################################################################
    ##
    ## Get-AliasSuggestion
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Get an alias suggestion from the full text of the last command. Intended to
    be added to your prompt function to help learn aliases for commands.
     
    .EXAMPLE
     
    PS > Get-AliasSuggestion Remove-ItemProperty
    Suggestion: An alias for Remove-ItemProperty is rp
     
    #>

    
    param(
        ## The full text of the last command
        $LastCommand
    )
    
    Set-StrictMode -Version 3
    
    $helpMatches = @()
    
    ## Find all of the commands in their last input
    $tokens = [Management.Automation.PSParser]::Tokenize(
        $lastCommand, [ref] $null)
    $commands = $tokens | Where-Object { $_.Type -eq "Command" }
    
    ## Go through each command
    foreach($command in $commands)
    {
        ## Get the alias suggestions
        foreach($alias in Get-Alias -Definition $command.Content)
        {
            $helpMatches += "Suggestion: An alias for " +
                "$($alias.Definition) is $($alias.Name)"
        }
    }
    
    $helpMatches
}

function Get-Answer
{
    ##############################################################################
    ##
    ## Get-Answer
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Uses Bing Answers to answer your question
     
    .EXAMPLE
     
    PS > Get-Answer "sqrt(2)"
    sqrt(2) = 1.41421356
     
    .EXAMPLE
     
    PS > Get-Answer msft stock
    Microsoft Corp (US:MSFT) NASDAQ
    29.66 -0.35 (-1.17%)
    After Hours: 30.02 +0.36 (1.21%)
    Open: 30.09 Day's Range: 29.59 - 30.20
    Volume: 55.60 M 52 Week Range: 17.27 - 31.50
    P/E Ratio: 16.30 Market Cap: 260.13 B
     
    .EXAMPLE
     
    PS > Get-Answer "What is the time in Seattle, WA?"
    Current time in Seattle, WA
    01:12:41 PM
    08/18/2012 ? Pacific Daylight Time
     
    #>

    
    Set-StrictMode -Version 3
    
    $question = $args -join " "
    
    function Main
    {
        ## Load the System.Web.HttpUtility DLL, to let us URLEncode
        Add-Type -Assembly System.Web
    
        ## Get the web page into a single string with newlines between
        ## the lines.
        $encoded = [System.Web.HttpUtility]::UrlEncode($question)
        $url = "http://www.bing.com/search?q=$encoded"
        $text = [String] (Invoke-WebRequest $url)
    
        ## Find the start of the answers section
        $startIndex = $text.IndexOf('<div class="ans"')
    
        ## The end is either defined by an "attribution" div
        ## or the start of a "results" div
        $endIndex = $text.IndexOf('<div class="sn_att2"', $startIndex)
        if($endIndex -lt 0) { $endIndex = $text.IndexOf('<div id="results"', $startIndex) }
    
        ## If we found a result, then filter the result
        if(($startIndex -ge 0) -and ($endIndex -ge 0))
        {
            ## Pull out the text between the start and end portions
            $partialText = $text.Substring($startIndex, $endIndex - $startIndex)
    
            ## Very fragile screen scraping here. Replace a bunch of
            ## tags that get placed on new lines with the newline
            ## character, and a few others with spaces.
            $partialText = $partialText -replace '<div[^>]*>',"`n"
            $partialText = $partialText -replace '<tr[^>]*>',"`n"
            $partialText = $partialText -replace '<li[^>]*>',"`n"
            $partialText = $partialText -replace '<br[^>]*>',"`n"
            $partialText = $partialText -replace '<p [^>]*>',"`n"
            $partialText = $partialText -replace '<span[^>]*>'," "
            $partialText = $partialText -replace '<td[^>]*>'," "
    
            $partialText = CleanHtml $partialText
    
            ## Now split the results on newlines, trim each line, and then
            ## join them back.
            $partialText = $partialText -split "`n" |
                Foreach-Object { $_.Trim() } | Where-Object { $_ }
            $partialText = $partialText -join "`n"
    
            [System.Web.HttpUtility]::HtmlDecode($partialText.Trim())
        }
        else
        {
            "No answer found."
        }
    }
    
    ## Clean HTML from a text chunk
    function CleanHtml ($htmlInput)
    {
        $tempString = [Regex]::Replace($htmlInput, "(?s)<[^>]*>", "")
        $tempString.Replace("&nbsp&nbsp", "")
    }
    
    Main
}

function Get-Arguments
{
    ##############################################################################
    ##
    ## Get-Arguments
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Uses command-line arguments
     
    #>

    
    param(
        ## The first named argument
        $FirstNamedArgument,
    
        ## The second named argument
        [int] $SecondNamedArgument = 0
    )
    
    Set-StrictMode -Version 3
    
    ## Display the arguments by name
    "First named argument is: $firstNamedArgument"
    "Second named argument is: $secondNamedArgument"
    
    function GetArgumentsFunction
    {
        ## We could use a param statement here, as well
        ## param($firstNamedArgument, [int] $secondNamedArgument = 0)
    
        ## Display the arguments by position
        "First positional function argument is: " + $args[0]
        "Second positional function argument is: " + $args[1]
    }
    
    GetArgumentsFunction One Two
    
    $scriptBlock =
    {
        param($firstNamedArgument, [int] $secondNamedArgument = 0)
    
        ## We could use $args here, as well
        "First named scriptblock argument is: $firstNamedArgument"
        "Second named scriptblock argument is: $secondNamedArgument"
    }
    
    & $scriptBlock -First One -Second 4.5
}

function Get-Characteristics
{
    ##############################################################################
    ##
    ## Get-Characteristics
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Get the file characteristics of a file in the PE Executable File Format.
     
    .EXAMPLE
     
    PS > Get-Characteristics $env:WINDIR\notepad.exe
    IMAGE_FILE_LOCAL_SYMS_STRIPPED
    IMAGE_FILE_RELOCS_STRIPPED
    IMAGE_FILE_EXECUTABLE_IMAGE
    IMAGE_FILE_32BIT_MACHINE
    IMAGE_FILE_LINE_NUMS_STRIPPED
     
    #>

    
    param(
        ## The path to the file to check
        [Parameter(Mandatory = $true)]
        [string] $Path
    )
    
    Set-StrictMode -Version 3
    
    ## Define the characteristics used in the PE file file header.
    ## Taken from:
    ## http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
    $characteristics = @{}
    $characteristics["IMAGE_FILE_RELOCS_STRIPPED"] = 0x0001
    $characteristics["IMAGE_FILE_EXECUTABLE_IMAGE"] = 0x0002
    $characteristics["IMAGE_FILE_LINE_NUMS_STRIPPED"] = 0x0004
    $characteristics["IMAGE_FILE_LOCAL_SYMS_STRIPPED"] = 0x0008
    $characteristics["IMAGE_FILE_AGGRESSIVE_WS_TRIM"] = 0x0010
    $characteristics["IMAGE_FILE_LARGE_ADDRESS_AWARE"] = 0x0020
    $characteristics["RESERVED"] = 0x0040
    $characteristics["IMAGE_FILE_BYTES_REVERSED_LO"] = 0x0080
    $characteristics["IMAGE_FILE_32BIT_MACHINE"] = 0x0100
    $characteristics["IMAGE_FILE_DEBUG_STRIPPED"] = 0x0200
    $characteristics["IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP"] = 0x0400
    $characteristics["IMAGE_FILE_NET_RUN_FROM_SWAP"] = 0x0800
    $characteristics["IMAGE_FILE_SYSTEM"] = 0x1000
    $characteristics["IMAGE_FILE_DLL"] = 0x2000
    $characteristics["IMAGE_FILE_UP_SYSTEM_ONLY"] = 0x4000
    $characteristics["IMAGE_FILE_BYTES_REVERSED_HI"] = 0x8000
    
    ## Get the content of the file, as an array of bytes
    $fileBytes = Get-Content $path -ReadCount 0 -Encoding byte
    
    ## The offset of the signature in the file is stored at location 0x3c.
    $signatureOffset = $fileBytes[0x3c]
    
    ## Ensure it is a PE file
    $signature = [char[]] $fileBytes[$signatureOffset..($signatureOffset + 3)]
    if(($signature -join '') -ne "PE`0`0")
    {
        throw "This file does not conform to the PE specification."
    }
    
    ## The location of the COFF header is 4 bytes into the signature
    $coffHeader = $signatureOffset + 4
    
    ## The characteristics data are 18 bytes into the COFF header. The
    ## BitConverter class manages the conversion of the 4 bytes into an integer.
    $characteristicsData = [BitConverter]::ToInt32($fileBytes, $coffHeader + 18)
    
    ## Go through each of the characteristics. If the data from the file has that
    ## flag set, then output that characteristic.
    foreach($key in $characteristics.Keys)
    {
        $flag = $characteristics[$key]
        if(($characteristicsData -band $flag) -eq $flag)
        {
            $key
        }
    }
}

function Get-Clipboard
{
    #############################################################################
    ##
    ## Get-Clipboard
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Retrieve the text contents of the Windows Clipboard.
     
    .EXAMPLE
     
    PS > Get-Clipboard
    Hello World
     
    #>

    
    Set-StrictMode -Version 3
    
    Add-Type -Assembly PresentationCore
    [Windows.Clipboard]::GetText()
}

function Get-DetailedSystemInformation
{
    ##############################################################################
    ##
    ## Get-DetailedSystemInformation
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Get detailed information about a system.
     
    .EXAMPLE
     
    PS > Get-DetailedSystemInformation LEE-DESK > output.txt
    Gets detailed information about LEE-DESK and stores the output into output.txt
     
    #>

    
    param(
        ## The computer to analyze
        $Computer = "."
    )
    
    Set-StrictMode -Version 3
    
    "#"*80
    "System Information Summary"
    "Generated $(Get-Date)"
    "#"*80
    ""
    ""
    
    "#"*80
    "Computer System Information"
    "#"*80
    Get-CimInstance Win32_ComputerSystem -Computer $computer | Format-List *
    
    "#"*80
    "Operating System Information"
    "#"*80
    Get-CimInstance Win32_OperatingSystem -Computer $computer | Format-List *
    
    "#"*80
    "BIOS Information"
    "#"*80
    Get-CimInstance Win32_Bios -Computer $computer | Format-List *
    
    "#"*80
    "Memory Information"
    "#"*80
    Get-CimInstance Win32_PhysicalMemory -Computer $computer | Format-List *
    
    "#"*80
    "Physical Disk Information"
    "#"*80
    Get-CimInstance Win32_DiskDrive -Computer $computer | Format-List *
    
    "#"*80
    "Logical Disk Information"
    "#"*80
    Get-CimInstance Win32_LogicalDisk -Computer $computer | Format-List *
}

function Get-DiskUsage
{
    ##############################################################################
    ##
    ## Get-DiskUsage
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Retrieve information about disk usage in the current directory and all
    subdirectories. If you specify the -IncludeSubdirectories flag, this
    script accounts for the size of subdirectories in the size of a directory.
     
    .EXAMPLE
     
    PS > Get-DiskUsage
    Gets the disk usage for the current directory.
     
    .EXAMPLE
     
    PS > Get-DiskUsage -IncludeSubdirectories
    Gets the disk usage for the current directory and those below it,
    adding the size of child directories to the directory that contains them.
     
    #>

    
    param(
        ## Switch to include subdirectories in the size of each directory
        [switch] $IncludeSubdirectories
    )
    
    Set-StrictMode -Version 3
    
    ## If they specify the -IncludeSubdirectories flag, then we want to account
    ## for all subdirectories in the size of each directory
    if($includeSubdirectories)
    {
        Get-ChildItem -Directory |
            Select-Object Name,
                @{ Name="Size";
                Expression={ ($_ | Get-ChildItem -Recurse |
                    Measure-Object -Sum Length).Sum + 0 } }
    }
    ## Otherwise, we just find all directories below the current directory,
    ## and determine their size
    else
    {
        Get-ChildItem -Recurse -Directory |
            Select-Object FullName,
                @{ Name="Size";
                Expression={ ($_ | Get-ChildItem |
                    Measure-Object -Sum Length).Sum + 0 } }
    }
}

function Get-FacebookNotification
{
    $cred = Get-Credential
    $login = Invoke-WebRequest http://www.facebook.com/login.php -SessionVariable fb
    $login.Forms[0].Fields.email = $cred.GetNetworkCredential().UserName
    $login.Forms[0].Fields.pass = $cred.GetNetworkCredential().Password
    $mainPage = Invoke-WebRequest $login.Forms[0].Action -WebSession $fb -Body $login -Method Post
    $mainPage.ParsedHtml.getElementById("notificationsCountValue").InnerText
}

function Get-FileEncoding
{
    ##############################################################################
    ##
    ## Get-FileEncoding
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Gets the encoding of a file
     
    .EXAMPLE
     
    Get-FileEncoding.ps1 .\UnicodeScript.ps1
     
    BodyName : unicodeFFFE
    EncodingName : Unicode (Big-Endian)
    HeaderName : unicodeFFFE
    WebName : unicodeFFFE
    WindowsCodePage : 1200
    IsBrowserDisplay : False
    IsBrowserSave : False
    IsMailNewsDisplay : False
    IsMailNewsSave : False
    IsSingleByte : False
    EncoderFallback : System.Text.EncoderReplacementFallback
    DecoderFallback : System.Text.DecoderReplacementFallback
    IsReadOnly : True
    CodePage : 1201
     
    #>

    
    param(
        ## The path of the file to get the encoding of.
        $Path
    )
    
    Set-StrictMode -Version 3
    
    ## First, check if the file is binary. That is, if the first
    ## 5 lines contain any non-printable characters.
    $nonPrintable = [char[]] (0..8 + 10..31 + 127 + 129 + 141 + 143 + 144 + 157)
    $lines = Get-Content $Path -ErrorAction Ignore -TotalCount 5
    $result = @($lines | Where-Object { $_.IndexOfAny($nonPrintable) -ge 0 })
    if($result.Count -gt 0)
    {
        "Binary"
        return
    }
    
    ## Next, check if it matches a well-known encoding.
    
    ## The hashtable used to store our mapping of encoding bytes to their
    ## name. For example, "255-254 = Unicode"
    $encodings = @{}
    
    ## Find all of the encodings understood by the .NET Framework. For each,
    ## determine the bytes at the start of the file (the preamble) that the .NET
    ## Framework uses to identify that encoding.
    foreach($encoding in [System.Text.Encoding]::GetEncodings())
    {
        $preamble = $encoding.GetEncoding().GetPreamble()
        if($preamble)
        {
            $encodingBytes = $preamble -join '-'
            $encodings[$encodingBytes] = $encoding.GetEncoding()
        }
    }
    
    ## Find out the lengths of all of the preambles.
    $encodingLengths = $encodings.Keys | Where-Object { $_ } |
        Foreach-Object { ($_ -split "-").Count }
    
    ## Assume the encoding is UTF7 by default
    $result = [System.Text.Encoding]::UTF7
    
    ## Go through each of the possible preamble lengths, read that many
    ## bytes from the file, and then see if it matches one of the encodings
    ## we know about.
    foreach($encodingLength in $encodingLengths | Sort -Descending)
    {
        $bytes = Get-Content -encoding byte -readcount $encodingLength $path | Select -First 1
        $encoding = $encodings[$bytes -join '-']
    
        ## If we found an encoding that had the same preamble bytes,
        ## save that output and break.
        if($encoding)
        {
            $result = $encoding
            break
        }
    }
    
    ## Finally, output the encoding.
    $result
}

function Get-InstalledSoftware
{
    ##############################################################################
    ##
    ## Get-InstalledSoftware
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Lists installed software on the current computer.
     
    .EXAMPLE
     
    PS > Get-InstalledSoftware *Frame* | Select DisplayName
     
    DisplayName
    -----------
    Microsoft .NET Framework 3.5 SP1
    Microsoft .NET Framework 3.5 SP1
    Hotfix for Microsoft .NET Framework 3.5 SP1 (KB953595)
    Hotfix for Microsoft .NET Framework 3.5 SP1 (KB958484)
    Update for Microsoft .NET Framework 3.5 SP1 (KB963707)
     
    #>

    
    param(
        ## The name of the software to search for
        $DisplayName = "*"
    )
    
    Set-StrictMode -Off
    
    ## Get all the listed software in the Uninstall key
    $keys =
        Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
    
    ## Get all of the properties from those items
    $items = $keys | Foreach-Object { Get-ItemProperty $_.PsPath }
    
    ## For each of those items, display the DisplayName and Publisher
    foreach($item in $items)
    {
        if(($item.DisplayName) -and ($item.DisplayName -like $displayName))
        {
            $item
        }
    }
}

function Get-InvocationInfo
{
    ##############################################################################
    ##
    ## Get-InvocationInfo
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Display the information provided by the $myInvocation variable
     
    #>

    
    param(
        ## Switch to no longer recursively call ourselves
        [switch] $PreventExpansion
    )
    
    Set-StrictMode -Version 3
    
    ## Define a helper function, so that we can see how $myInvocation changes
    ## when it is called, and when it is dot-sourced
    function HelperFunction
    {
        " MyInvocation from function:"
        "-"*50
        $myInvocation
    
        " Command from function:"
        "-"*50
        $myInvocation.MyCommand
    }
    
    ## Define a script block, so that we can see how $myInvocation changes
    ## when it is called, and when it is dot-sourced
    $myScriptBlock = {
        " MyInvocation from script block:"
        "-"*50
        $myInvocation
    
        " Command from script block:"
        "-"*50
        $myInvocation.MyCommand
    }
    
    ## Define a helper alias
    Set-Alias gii .\Get-InvocationInfo
    
    ## Illustrate how $myInvocation.Line returns the entire line that the
    ## user typed.
    "You invoked this script by typing: " + $myInvocation.Line
    
    ## Show the information that $myInvocation returns from a script
    "MyInvocation from script:"
    "-"*50
    $myInvocation
    
    "Command from script:"
    "-"*50
    $myInvocation.MyCommand
    
    ## If we were called with the -PreventExpansion switch, don't go
    ## any further
    if($preventExpansion)
    {
        return
    }
    
    ## Show the information that $myInvocation returns from a function
    "Calling HelperFunction"
    "-"*50
    HelperFunction
    
    ## Show the information that $myInvocation returns from a dot-sourced
    ## function
    "Dot-Sourcing HelperFunction"
    "-"*50
    . HelperFunction
    
    ## Show the information that $myInvocation returns from an aliased script
    "Calling aliased script"
    "-"*50
    gii -PreventExpansion
    
    ## Show the information that $myInvocation returns from a script block
    "Calling script block"
    "-"*50
    & $myScriptBlock
    
    ## Show the information that $myInvocation returns from a dot-sourced
    ## script block
    "Dot-Sourcing script block"
    "-"*50
    . $myScriptBlock
    
    ## Show the information that $myInvocation returns from an aliased script
    "Calling aliased script"
    "-"*50
    gii -PreventExpansion
}

function Get-MachineStartupShutdownScript
{
    ##############################################################################
    ##
    ## Get-MachineStartupShutdownScript
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Get the startup or shutdown scripts assigned to a machine
     
    .EXAMPLE
     
    PS > Get-MachineStartupShutdownScript -ScriptType Startup
    Gets startup scripts for the machine
     
    #>

    
    param(
        ## The type of script to search for: Startup, or Shutdown.
        [Parameter(Mandatory = $true)]
        [ValidateSet("Startup","Shutdown")]
        $ScriptType
    )
    
    Set-StrictMode -Version 3
    
    ## Store the location of the group policy scripts for the machine
    $registryKey = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\System\Scripts"
    
    ## There may be no scripts defined
    if(-not (Test-Path $registryKey))
    {
        return
    }
    
    ## Go through each of the policies in the specified key
    foreach($policy in Get-ChildItem $registryKey\$scriptType)
    {
        ## For each of the scripts in that policy, get its script name
        ## and parameters
        foreach($script in Get-ChildItem $policy.PsPath)
        {
            Get-ItemProperty $script.PsPath | Select Script,Parameters
        }
    }
}

function Get-OfficialTime
{
    ##############################################################################
    ##
    ## Get-OfficialTime
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Gets the official US time (PST) from time.gov
     
    #>

    
    Set-StrictMode -Version 3
    
    ## Create the URL that contains the Twitter search results
    Add-Type -Assembly System.Web
    $url = 'http://www.time.gov/timezone.cgi?Pacific/d/-8'
    
    ## Download the web page
    $results = Invoke-WebRequest $url | Foreach-Object Content
    
    ## Extract the text of the time, which is contained in
    ## a segment that looks like "<font size="7" color="white"><b>...<br>"
    $match = $results -match '<font [^>]*><b>(.*)<br>'
    if($matches)
    {
        $time = $matches[1]
    }
    
    ## Output the time
    $time
}

function Get-OperatingSystemSku
{
    ##############################################################################
    ##
    ## Get-OperatingSystemSku
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Gets the sku information for the current operating system
     
    .EXAMPLE
     
    PS > Get-OperatingSystemSku
    Professional with Media Center
     
    #>

    
    param($Sku = 
        (Get-CimInstance Win32_OperatingSystem).OperatingSystemSku)
    
    Set-StrictMode -Version 3
    
    switch ($Sku)
    {
        0   { "An unknown product"; break; }
        1   { "Ultimate"; break; }
        2   { "Home Basic"; break; }
        3   { "Home Premium"; break; }
        4   { "Enterprise"; break; }
        5   { "Home Basic N"; break; }
        6   { "Business"; break; }
        7   { "Server Standard"; break; }
        8   { "Server Datacenter (full installation)"; break; }
        9   { "Windows Small Business Server"; break; }
        10  { "Server Enterprise (full installation)"; break; }
        11  { "Starter"; break; }
        12  { "Server Datacenter (core installation)"; break; }
        13  { "Server Standard (core installation)"; break; }
        14  { "Server Enterprise (core installation)"; break; }
        15  { "Server Enterprise for Itanium-based Systems"; break; }
        16  { "Business N"; break; }
        17  { "Web Server (full installation)"; break; }
        18  { "HPC Edition"; break; }
        19  { "Windows Storage Server 2008 R2 Essentials"; break; }
        20  { "Storage Server Express"; break; }
        21  { "Storage Server Standard"; break; }
        22  { "Storage Server Workgroup"; break; }
        23  { "Storage Server Enterprise"; break; }
        24  { "Windows Server 2008 for Windows Essential Server Solutions"; break; }
        25  { "Small Business Server Premium"; break; }
        26  { "Home Premium N"; break; }
        27  { "Enterprise N"; break; }
        28  { "Ultimate N"; break; }
        29  { "Web Server (core installation)"; break; }
        30  { "Windows Essential Business Server Management Server"; break; }
        31  { "Windows Essential Business Server Security Server"; break; }
        32  { "Windows Essential Business Server Messaging Server"; break; }
        33  { "Server Foundation"; break; }
        34  { "Windows Home Server 2011"; break; }
        35  { "Windows Server 2008 without Hyper-V for Windows Essential Server Solutions"; break; }
        36  { "Server Standard without Hyper-V"; break; }
        37  { "Server Datacenter without Hyper-V (full installation)"; break; }
        38  { "Server Enterprise without Hyper-V (full installation)"; break; }
        39  { "Server Datacenter without Hyper-V (core installation)"; break; }
        40  { "Server Standard without Hyper-V (core installation)"; break; }
        41  { "Server Enterprise without Hyper-V (core installation)"; break; }
        42  { "Microsoft Hyper-V Server"; break; }
        43  { "Storage Server Express (core installation)"; break; }
        44  { "Storage Server Standard (core installation)"; break; }
        45  { "Storage Server Workgroup (core installation)"; break; }
        46  { "Storage Server Enterprise (core installation)"; break; }
        46  { "Storage Server Enterprise (core installation)"; break; }
        47  { "Starter N"; break; }
        48  { "Professional"; break; }
        49  { "Professional N"; break; }
        50  { "Windows Small Business Server 2011 Essentials"; break; }
        51  { "Server For SB Solutions"; break; }
        52  { "Server Solutions Premium"; break; }
        53  { "Server Solutions Premium (core installation)"; break; }
        54  { "Server For SB Solutions EM"; break; }
        55  { "Server For SB Solutions EM"; break; }
        56  { "Windows MultiPoint Server"; break; }
        59  { "Windows Essential Server Solution Management"; break; }
        60  { "Windows Essential Server Solution Additional"; break; }
        61  { "Windows Essential Server Solution Management SVC"; break; }
        62  { "Windows Essential Server Solution Additional SVC"; break; }
        63  { "Small Business Server Premium (core installation)"; break; }
        64  { "Server Hyper Core V"; break; }
        72  { "Server Enterprise (evaluation installation)"; break; }
        76  { "Windows MultiPoint Server Standard (full installation)"; break; }
        77  { "Windows MultiPoint Server Premium (full installation)"; break; }
        79  { "Server Standard (evaluation installation)"; break; }
        80  { "Server Datacenter (evaluation installation)"; break; }
        84  { "Enterprise N (evaluation installation)"; break; }
        95  { "Storage Server Workgroup (evaluation installation)"; break; }
        96  { "Storage Server Standard (evaluation installation)"; break; }
        98  { "Windows 8 N"; break; }
        99  { "Windows 8 China"; break; }
        100 { "Windows 8 Single Language"; break; }
        101 { "Windows 8"; break; }
        103 { "Professional with Media Center"; break; }
    
        default {"UNKNOWN: " + $SKU }
    }
}

function Get-OwnerReport
{
    ##############################################################################
    ##
    ## Get-OwnerReport
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Gets a list of files in the current directory, but with their owner added
    to the resulting objects.
     
    .EXAMPLE
     
    PS > Get-OwnerReport | Format-Table Name,LastWriteTime,Owner
    Retrieves all files in the current directory, and displays the
    Name, LastWriteTime, and Owner
     
    #>

    
    Set-StrictMode -Version 3
    
    $files = Get-ChildItem
    foreach($file in $files)
    {
        $owner = (Get-Acl $file).Owner
        $file | Add-Member NoteProperty Owner $owner
        $file
    }
}

function Get-PageUrls
{
    ##############################################################################
    ##
    ## Get-PageUrls
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Parse all of the URLs out of a given file.
     
    .EXAMPLE
     
    PS > Get-PageUrls microsoft.html http://www.microsoft.com
    Gets all of the URLs from HTML stored in microsoft.html, and converts relative
    URLs to the domain of http://www.microsoft.com
     
    .EXAMPLE
     
    PS > Get-PageUrls microsoft.html http://www.microsoft.com 'aspx$'
    Gets all of the URLs from HTML stored in microsoft.html, converts relative
    URLs to the domain of http://www.microsoft.com, and returns only URLs that end
    in 'aspx'.
     
    #>

    
    param(
        ## The filename to parse
        [Parameter(Mandatory = $true)]
        [string] $Path,
    
        ## The URL from which you downloaded the page.
        ## For example, http://www.microsoft.com
        [Parameter(Mandatory = $true)]
        [string] $BaseUrl,
    
        [switch] $Images,
        
        ## The Regular Expression pattern with which to filter
        ## the returned URLs
        [string] $Pattern = ".*"
    )
    
    Set-StrictMode -Version 3
    
    ## Load the System.Web DLL so that we can decode URLs
    Add-Type -Assembly System.Web
    
    ## Defines the regular expression that will parse an URL
    ## out of an anchor tag.
    $regex = "<\s*a\s*[^>]*?href\s*=\s*[`"']*([^`"'>]+)[^>]*?>"
    if($Images)
    {
        $regex = "<\s*img\s*[^>]*?src\s*=\s*[`"']*([^`"'>]+)[^>]*?>"
    }
    
    ## Parse the file for links
    function Main
    {
        ## Do some minimal source URL fixups, by switching backslashes to
        ## forward slashes
        $baseUrl = $baseUrl.Replace("\", "/")
    
        if($baseUrl.IndexOf("://") -lt 0)
        {
            throw "Please specify a base URL in the form of " +
                "http://server/path_to_file/file.html"
        }
    
        ## Determine the server from which the file originated. This will
        ## help us resolve links such as "/somefile.zip"
        $baseUrl = $baseUrl.Substring(0, $baseUrl.LastIndexOf("/") + 1)
        $baseSlash = $baseUrl.IndexOf("/", $baseUrl.IndexOf("://") + 3)
    
        if($baseSlash -ge 0)
        {
            $domain = $baseUrl.Substring(0, $baseSlash)
        }
        else
        {
            $domain = $baseUrl
        }
    
    
        ## Put all of the file content into a big string, and
        ## get the regular expression matches
        $content = (Get-Content $path) -join ' '
        $contentMatches = @(GetMatches $content $regex)
    
        foreach($contentMatch in $contentMatches)
        {
            if(-not ($contentMatch -match $pattern)) { continue }
            if($contentMatch -match "javascript:") { continue }
    
            $contentMatch = $contentMatch.Replace("\", "/")
    
            ## Hrefs may look like:
            ## ./file
            ## file
            ## ../../../file
            ## /file
            ## url
            ## We'll keep all of the relative paths, as they will resolve.
            ## We only need to resolve the ones pointing to the root.
            if($contentMatch.IndexOf("://") -gt 0)
            {
                $url = $contentMatch
            }
            elseif($contentMatch[0] -eq "/")
            {
                $url = "$domain$contentMatch"
            }
            else
            {
                $url = "$baseUrl$contentMatch"
                $url = $url.Replace("/./", "/")
            }
    
            ## Return the URL, after first removing any HTML entities
            [System.Web.HttpUtility]::HtmlDecode($url)
        }
    }
    
    function GetMatches([string] $content, [string] $regex)
    {
        $returnMatches = new-object System.Collections.ArrayList
    
        ## Match the regular expression against the content, and
        ## add all trimmed matches to our return list
        $resultingMatches = [Regex]::Matches($content, $regex, "IgnoreCase")
        foreach($match in $resultingMatches)
        {
            $cleanedMatch = $match.Groups[1].Value.Trim()
            [void] $returnMatches.Add($cleanedMatch)
        }
    
        $returnMatches
    }
    
    . Main
}

function Get-ParameterAlias
{
    ##############################################################################
    ##
    ## Get-ParameterAlias
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Looks in the session history, and returns any aliases that apply to
    parameters of commands that were used.
     
    .EXAMPLE
     
    PS > dir -ErrorAction SilentlyContinue
    PS > Get-ParameterAlias
    An alias for the 'ErrorAction' parameter of 'dir' is ea
     
    #>

    
    Set-StrictMode -Version 3
    
    ## Get the last item from their session history
    $history = Get-History -Count 1
    if(-not $history)
    {
        return
    }
    
    ## And extract the actual command line they typed
    $lastCommand = $history.CommandLine
    
    ## Use the Tokenizer API to determine which portions represent
    ## commands and parameters to those commands
    $tokens = [System.Management.Automation.PsParser]::Tokenize(
        $lastCommand, [ref] $null)
    $currentCommand = $null
    
    ## Now go through each resulting token
    foreach($token in $tokens)
    {
        ## If we've found a new command, store that.
        if($token.Type -eq "Command")
        {
            $currentCommand = $token.Content
        }
    
        ## If we've found a command parameter, start looking for aliases
        if(($token.Type -eq "CommandParameter") -and ($currentCommand))
        {
            ## Remove the leading "-" from the parameter
            $currentParameter = $token.Content.TrimStart("-")
    
            ## Determine all of the parameters for the current command.
            (Get-Command $currentCommand).Parameters.GetEnumerator() |
    
                ## For parameters that start with the current parameter name,
                Where-Object { $_.Key -like "$currentParameter*" } |
    
                ## return all of the aliases that apply. We use "starts with"
                ## because the user might have typed a shortened form of
                ## the parameter name.
                Foreach-Object {
                    $_.Value.Aliases | Foreach-Object {
                        "Suggestion: An alias for the '$currentParameter' " +
                        "parameter of '$currentCommand' is '$_'"
                    }
                }
        }
    }
}

function Get-PrivateProfileString
{
    #############################################################################
    ##
    ## Get-PrivateProfileString
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Retrieves an element from a standard .INI file
     
    .EXAMPLE
     
    PS > Get-PrivateProfileString c:\windows\system32\tcpmon.ini `
        "<Generic Network Card>" Name
    Generic Network Card
     
    #>

    
    param(
        ## The INI file to retrieve
        $Path,
    
        ## The section to retrieve from
        $Category,
    
        ## The item to retrieve
        $Key
    )
    
    Set-StrictMode -Version 3
    
    ## The signature of the Windows API that retrieves INI
    ## settings
    $signature = @'
    [DllImport("kernel32.dll")]
    public static extern uint GetPrivateProfileString(
        string lpAppName,
        string lpKeyName,
        string lpDefault,
        StringBuilder lpReturnedString,
        uint nSize,
        string lpFileName);
'@

    
    ## Create a new type that lets us access the Windows API function
    $type = Add-Type -MemberDefinition $signature `
        -Name Win32Utils -Namespace GetPrivateProfileString `
        -Using System.Text -PassThru
    
    ## The GetPrivateProfileString function needs a StringBuilder to hold
    ## its output. Create one, and then invoke the method
    $builder = New-Object System.Text.StringBuilder 1024
    $null = $type::GetPrivateProfileString($category,
        $key, "", $builder, $builder.Capacity, $path)
    
    ## Return the output
    $builder.ToString()
}

function Get-RemoteRegistryChildItem
{
    ##############################################################################
    ##
    ## Get-RemoteRegistryChildItem
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Get the list of subkeys below a given key on a remote computer.
     
    .EXAMPLE
     
    Get-RemoteRegistryChildItem LEE-DESK HKLM:\Software
     
    #>

    
    param(
        ## The computer that you wish to connect to
        [Parameter(Mandatory = $true)]
        $ComputerName,
    
        ## The path to the registry items to retrieve
        [Parameter(Mandatory = $true)]
        $Path
    )
    
    Set-StrictMode -Version 3
    
    ## Validate and extract out the registry key
    if($path -match "^HKLM:\\(.*)")
    {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(
            "LocalMachine", $computername)
    }
    elseif($path -match "^HKCU:\\(.*)")
    {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(
            "CurrentUser", $computername)
    }
    else
    {
        Write-Error ("Please specify a fully-qualified registry path " +
            "(i.e.: HKLM:\Software) of the registry key to open.")
        return
    }
    
    ## Open the key
    $key = $baseKey.OpenSubKey($matches[1])
    
    ## Retrieve all of its children
    foreach($subkeyName in $key.GetSubKeyNames())
    {
        ## Open the subkey
        $subkey = $key.OpenSubKey($subkeyName)
    
        ## Add information so that PowerShell displays this key like regular
        ## registry key
        $returnObject = [PsObject] $subKey
        $returnObject | Add-Member NoteProperty PsChildName $subkeyName
        $returnObject | Add-Member NoteProperty Property $subkey.GetValueNames()
    
        ## Output the key
        $returnObject
    
        ## Close the child key
        $subkey.Close()
    }
    
    ## Close the key and base keys
    $key.Close()
    $baseKey.Close()
}

function Get-RemoteRegistryKeyProperty
{
    ##############################################################################
    ##
    ## Get-RemoteRegistryKeyProperty
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Get the value of a remote registry key property
     
    .EXAMPLE
     
    PS > $registryPath =
         "HKLM:\software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
    PS > Get-RemoteRegistryKeyProperty LEE-DESK $registryPath ExecutionPolicy
     
    #>

    
    param(
        ## The computer that you wish to connect to
        [Parameter(Mandatory = $true)]
        $ComputerName,
    
        ## The path to the registry item to retrieve
        [Parameter(Mandatory = $true)]
        $Path,
    
        ## The specific property to retrieve
        $Property = "*"
    )
    
    Set-StrictMode -Version 3
    
    ## Validate and extract out the registry key
    if($path -match "^HKLM:\\(.*)")
    {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(
            "LocalMachine", $computername)
    }
    elseif($path -match "^HKCU:\\(.*)")
    {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(
            "CurrentUser", $computername)
    }
    else
    {
        Write-Error ("Please specify a fully-qualified registry path " +
            "(i.e.: HKLM:\Software) of the registry key to open.")
        return
    }
    
    ## Open the key
    $key = $baseKey.OpenSubKey($matches[1])
    $returnObject = New-Object PsObject
    
    ## Go through each of the properties in the key
    foreach($keyProperty in $key.GetValueNames())
    {
        ## If the property matches the search term, add it as a
        ## property to the output
        if($keyProperty -like $property)
        {
            $returnObject |
                Add-Member NoteProperty $keyProperty $key.GetValue($keyProperty)
        }
    }
    
    ## Return the resulting object
    $returnObject
    
    ## Close the key and base keys
    $key.Close()
    $baseKey.Close()
}

function Get-ScriptCoverage
{
    #############################################################################
    ##
    ## Get-ScriptCoverage
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Uses conditional breakpoints to obtain information about what regions of
    a script are executed when run.
     
    .EXAMPLE
     
    PS > Get-Content c:\temp\looper.ps1
     
    param($userInput)
     
    for($count = 0; $count -lt 10; $count++)
    {
        "Count is: $count"
    }
     
    if($userInput -eq "One")
    {
        "Got 'One'"
    }
     
    if($userInput -eq "Two")
    {
        "Got 'Two'"
    }
     
    PS > $action = { c:\temp\looper.ps1 -UserInput 'One' }
    PS > $coverage = Get-ScriptCoverage c:\temp\looper.ps1 -Action $action
    PS > $coverage | Select Content,StartLine,StartColumn | Format-Table -Auto
     
    Content StartLine StartColumn
    ------- --------- -----------
    userInput 1 7
    Got 'Two' 15 5
     
    This example exercises a 'looper.ps1' script, and supplies it with some
    user input. The output demonstrates that we didn't exercise the
    "Got 'Two'" statement.
     
    #>

    
    param(
        ## The path of the script to monitor
        $Path,
    
        ## The command to exercise the script
        [ScriptBlock] $Action = { & $path }
    )
    
    Set-StrictMode -Version 3
    
    ## Determine all of the tokens in the script
    $scriptContent = Get-Content $path
    $ignoreTokens = "Comment","NewLine","StatementSeparator","Keyword",
        "GroupStart","GroupEnd"
    $tokens = [System.Management.Automation.PsParser]::Tokenize(
        $scriptContent, [ref] $null) |
        Where-Object { $ignoreTokens -notcontains $_.Type }
    $tokens = $tokens | Sort-Object StartLine,StartColumn
    
    ## Create a variable to hold the tokens that PowerShell actually hits
    $visited = New-Object System.Collections.ArrayList
    
    ## Go through all of the tokens
    $breakpoints = foreach($token in $tokens)
    {
        ## Create a new action. This action logs the token that we
        ## hit. We call GetNewClosure() so that the $token variable
        ## gets the _current_ value of the $token variable, as opposed
        ## to the value it has when the breakpoints gets hit.
        $breakAction = { $null = $visited.Add($token) }.GetNewClosure()
    
        ## Set a breakpoint on the line and column of the current token.
        ## We use the action from above, which simply logs that we've hit
        ## that token.
        Set-PsBreakpoint $path -Line `
            $token.StartLine -Column $token.StartColumn -Action $breakAction
    }
    
    ## Invoke the action that exercises the script
    $null = . $action
    
    ## Remove the temporary breakpoints we set
    $breakpoints | Remove-PsBreakpoint
    
    ## Sort the tokens that we hit, and compare them with all of the tokens
    ## in the script. Output the result of that comparison.
    $visited = $visited | Sort-Object -Unique StartLine,StartColumn
    Compare-Object $tokens $visited -Property StartLine,StartColumn -PassThru
    
    ## Clean up our temporary variable
    Remove-Item variable:\visited
}

function Get-ScriptPerformanceProfile
{
    #############################################################################
    ##
    ## Get-ScriptPerformanceProfile
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Computes the performance characteristics of a script, based on the transcript
    of it running at trace level 1.
     
    .DESCRIPTION
     
    To profile a script:
     
       1) Turn on script tracing in the window that will run the script:
          Set-PsDebug -trace 1
       2) Turn on the transcript for the window that will run the script:
          Start-Transcript
          (Note the filename that PowerShell provides as the logging destination.)
       3) Type in the script name, but don't actually start it.
       4) Open another PowerShell window, and navigate to the directory holding
          this script. Type in '.\Get-ScriptPerformanceProfile <transcript>',
          replacing <transcript> with the path given in step 2. Don't
          press <Enter> yet.
       5) Switch to the profiled script window, and start the script.
          Switch to the window containing this script, and press <Enter>
       6) Wait until your profiled script exits, or has run long enough to be
          representative of its work. To be statistically accurate, your script
          should run for at least ten seconds.
       7) Switch to the window running this script, and press a key.
       8) Switch to the window holding your profiled script, and type:
          Stop-Transcript
       9) Delete the transcript.
     
    .NOTES
     
    You can profile regions of code (ie: functions) rather than just lines
    by placing the following call at the start of the region:
          Write-Debug "ENTER <region_name>"
    and the following call and the end of the region:
          Write-Debug "EXIT"
    This is implemented to account exclusively for the time spent in that
    region, and does not include time spent in regions contained within the
    region. For example, if FunctionA calls FunctionB, and you've surrounded
    each by region markers, the statistics for FunctionA will not include the
    statistics for FunctionB.
     
    #>

    
    param(
        ## The path of the transcript log file
        [Parameter(Mandatory = $true)]
        $Path
    )
    
    Set-StrictMode -Version 3
    
    function Main
    {
        ## Run the actual profiling of the script. $uniqueLines gets
        ## the mapping of line number to actual script content.
        ## $samples gets a hashtable mapping line number to the number of times
        ## we observed the script running that line.
        $uniqueLines = @{}
        $samples = GetSamples $uniqueLines
    
        "Breakdown by line:"
        "----------------------------"
    
        ## Create a new hash table that flips the $samples hashtable --
        ## one that maps the number of times sampled to the line sampled.
        ## Also, figure out how many samples we got altogether.
        $counts = @{}
        $totalSamples = 0;
        foreach($item in $samples.Keys)
        {
            $counts[$samples[$item]] = $item
            $totalSamples += $samples[$item]
        }
    
        ## Go through the flipped hashtable, in descending order of number of
        ## samples. As we do so, output the number of samples as a percentage of
        ## the total samples. This gives us the percentage of the time our
        ## script spent executing that line.
        foreach($count in ($counts.Keys | Sort-Object -Descending))
        {
            $line = $counts[$count]
            $percentage = "{0:#0}" -f ($count * 100 / $totalSamples)
            "{0,3}%: Line {1,4} -{2}" -f $percentage,$line,
                $uniqueLines[$line]
        }
    
        ## Go through the transcript log to figure out which lines are part of
        ## any marked regions. This returns a hashtable that maps region names
        ## to the lines they contain.
        ""
        "Breakdown by marked regions:"
        "----------------------------"
        $functionMembers = GenerateFunctionMembers
    
        ## For each region name, cycle through the lines in the region. As we
        ## cycle through the lines, sum up the time spent on those lines and
        ## output the total.
        foreach($key in $functionMembers.Keys)
        {
            $totalTime = 0
            foreach($line in $functionMembers[$key])
            {
                $totalTime += ($samples[$line] * 100 / $totalSamples)
            }
    
            $percentage = "{0:#0}" -f $totalTime
            "{0,3}%: {1}" -f $percentage,$key
        }
    }
    
    ## Run the actual profiling of the script. $uniqueLines gets
    ## the mapping of line number to actual script content.
    ## Return a hashtable mapping line number to the number of times
    ## we observed the script running that line.
    function GetSamples($uniqueLines)
    {
        ## Open the log file. We use the .Net file I/O, so that we keep
        ## monitoring just the end of the file. Otherwise, we would make our
        ## timing inaccurate as we scan the entire length of the file every time.
        $logStream = [System.IO.File]::Open($Path, "Open", "Read", "ReadWrite")
        $logReader = New-Object System.IO.StreamReader $logStream
    
        $random = New-Object Random
        $samples = @{}
    
        $lastCounted = $null
    
        ## Gather statistics until the user presses a key.
        while(-not $host.UI.RawUI.KeyAvailable)
        {
            ## We sleep a slightly random amount of time. If we sleep a constant
            ## amount of time, we run the very real risk of improperly sampling
            ## scripts that exhibit periodic behaviour.
            $sleepTime = [int] ($random.NextDouble() * 100.0)
            Start-Sleep -Milliseconds $sleepTime
    
            ## Get any content produced by the transcript since our last poll.
            ## From that poll, extract the last DEBUG statement (which is the last
            ## line executed.)
            $rest = $logReader.ReadToEnd()
            $lastEntryIndex = $rest.LastIndexOf("DEBUG: ")
    
            ## If we didn't get a new line, then the script is still working on
            ## the last line that we captured.
            if($lastEntryIndex -lt 0)
            {
                if($lastCounted) { $samples[$lastCounted] ++ }
                continue;
            }
    
            ## Extract the debug line.
            $lastEntryFinish = $rest.IndexOf("\n", $lastEntryIndex)
            if($lastEntryFinish -eq -1) { $lastEntryFinish = $rest.length }
    
            $scriptLine = $rest.Substring(
                $lastEntryIndex, ($lastEntryFinish - $lastEntryIndex)).Trim()
            if($scriptLine -match 'DEBUG:[ \t]*([0-9]*)\+(.*)')
            {
                ## Pull out the line number from the line
                $last = $matches[1]
    
                $lastCounted = $last
                $samples[$last] ++
    
                ## Pull out the actual script line that matches the line number
                $uniqueLines[$last] = $matches[2]
            }
    
            ## Discard anything that's buffered during this poll, and start
            ## waiting again
            $logReader.DiscardBufferedData()
        }
    
        ## Clean up
        $logStream.Close()
        $logReader.Close()
    
        $samples
    }
    
    ## Go through the transcript log to figure out which lines are part of any
    ## marked regions. This returns a hashtable that maps region names to
    ## the lines they contain.
    function GenerateFunctionMembers
    {
        ## Create a stack that represents the callstack. That way, if a marked
        ## region contains another marked region, we attribute the statistics
        ## appropriately.
        $callstack = New-Object System.Collections.Stack
        $currentFunction = "Unmarked"
        $callstack.Push($currentFunction)
    
        $functionMembers = @{}
    
        ## Go through each line in the transcript file, from the beginning
        foreach($line in (Get-Content $Path))
        {
            ## Check if we're entering a monitor block
            ## If so, store that we're in that function, and push it onto
            ## the callstack.
            if($line -match 'write-debug "ENTER (.*)"')
            {
                $currentFunction = $matches[1]
                $callstack.Push($currentFunction)
            }
            ## Check if we're exiting a monitor block
            ## If so, clear the "current function" from the callstack,
            ## and store the new "current function" onto the callstack.
            elseif($line -match 'write-debug "EXIT"')
            {
                [void] $callstack.Pop()
                $currentFunction = $callstack.Peek()
            }
            ## Otherwise, this is just a line with some code.
            ## Add the line number as a member of the "current function"
            else
            {
                if($line -match 'DEBUG:[ \t]*([0-9]*)\+')
                {
                    ## Create the arraylist if it's not initialized
                    if(-not $functionMembers[$currentFunction])
                    {
                        $functionMembers[$currentFunction] =
                            New-Object System.Collections.ArrayList
                    }
    
                    ## Add the current line to the ArrayList
                    $hitLines = $functionMembers[$currentFunction]
                    if(-not $hitLines.Contains($matches[1]))
                    {
                        [void] $hitLines.Add($matches[1])
                    }
                }
            }
        }
    
        $functionMembers
    }
    
    . Main
}

function Get-Tomorrow
{
    ##############################################################################
    ## Get-Tomorrow
    ##
    ## Get the date that represents tomorrow
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    Set-StrictMode -Version 3
    
    function GetDate
    {
        Get-Date
    }
    
    $tomorrow = (GetDate).AddDays(1)
    $tomorrow
}

function Get-UserLogonLogoffScript
{
    ##############################################################################
    ##
    ## Get-UserLogonLogoffScript
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Get the logon or logoff scripts assigned to a specific user
     
    .EXAMPLE
     
    Get-UserLogonLogoffScript LEE-DESK\LEE Logon
    Gets all logon scripts for the user 'LEE-DESK\Lee'
     
    #>

    
    param(
        ## The username to examine
        [Parameter(Mandatory = $true)]
        $Username,
    
        [Parameter(Mandatory = $true)]
        [ValidateSet("Logon","Logoff")]
        $ScriptType
    )
    
    Set-StrictMode -Version 3
    
    ## Find the SID for the username
    $account = New-Object System.Security.Principal.NTAccount $username
    $sid =
        $account.Translate([System.Security.Principal.SecurityIdentifier]).Value
    
    ## Map that to their group policy scripts
    $registryKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\" +
        "Group Policy\State\$sid\Scripts"
    
    if(-not (Test-Path $registryKey))
    {
        return
    }
    
    ## Go through each of the policies in the specified key
    foreach($policy in Get-ChildItem $registryKey\$scriptType)
    {
        ## For each of the scripts in that policy, get its script name
        ## and parameters
        foreach($script in Get-ChildItem $policy.PsPath)
        {
            Get-ItemProperty $script.PsPath | Select Script,Parameters
        }
    }
}

function Get-WarningsAndErrors
{
    ##############################################################################
    ##
    ## Get-WarningsAndErrors
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstrates the functionality of the Write-Warning, Write-Error, and throw
    statements
     
    #>

    
    Set-StrictMode -Version 3
    
    Write-Warning "Warning: About to generate an error"
    Write-Error "Error: You are running this script"
    throw "Could not complete operation."
}

function Get-WmiClassKeyProperty
{
    ##############################################################################
    ##
    ## Get-WmiClassKeyProperty
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Get all of the properties that you may use in a WMI filter for a given class.
     
    .EXAMPLE
     
    PS > Get-WmiClassKeyProperty Win32_Process
    Handle
     
    #>

    
    param(
        ## The WMI class to examine
        [WmiClass] $WmiClass
    )
    
    Set-StrictMode -Version 3
    
    ## WMI classes have properties
    foreach($currentProperty in $wmiClass.Properties)
    {
        ## WMI properties have qualifiers to explain more about them
        foreach($qualifier in $currentProperty.Qualifiers)
        {
            ## If it has a 'Key' qualifier, then you may use it in a filter
            if($qualifier.Name -eq "Key")
            {
                $currentProperty.Name
            }
        }
    }
}

function Grant-RegistryAccessFullControl
{
    ##############################################################################
    ##
    ## Grant-RegistryAccessFullControl
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Grants full control access to a user for the specified registry key.
     
    .EXAMPLE
     
    PS > $registryPath = "HKLM:\Software\MyProgram"
    PS > Grant-RegistryAccessFullControl "LEE-DESK\LEE" $registryPath
     
    #>

    
    param(
        ## The user to grant full control
        [Parameter(Mandatory = $true)]
        $User,
    
        ## The registry path that should have its permissions modified
        [Parameter(Mandatory = $true)]
        $RegistryPath
    )
    
    Set-StrictMode -Version 3
    
    Push-Location
    Set-Location -LiteralPath $registryPath
    
    ## Retrieve the ACL from the registry key
    $acl = Get-Acl .
    
    ## Prepare the access rule, and set the access rule
    $arguments = $user,"FullControl","Allow"
    $accessRule = New-Object Security.AccessControl.RegistryAccessRule $arguments
    $acl.SetAccessRule($accessRule)
    
    ## Apply the modified ACL to the regsitry key
    $acl | Set-Acl  .
    
    Pop-Location
}

function Import-ADUser
{
    #############################################################################
    ##
    ## Import-AdUser
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    #############################################################################
    
    <#
     
    .SYNOPSIS
     
    Create users in Active Directory from the content of a CSV.
     
    .DESCRIPTION
     
    In the user CSV, One column must be named "CN" for the user name.
    All other columns represent properties in Active Directory for that user.
     
    For example:
    CN,userPrincipalName,displayName,manager
    MyerKen,Ken.Myer@fabrikam.com,Ken Myer,
    DoeJane,Jane.Doe@fabrikam.com,Jane Doe,"CN=MyerKen,OU=West,OU=Sales,DC=..."
    SmithRobin,Robin.Smith@fabrikam.com,Robin Smith,"CN=MyerKen,OU=West,OU=..."
     
    .EXAMPLE
     
    PS > $container = "LDAP://localhost:389/ou=West,ou=Sales,dc=Fabrikam,dc=COM"
    PS > Import-ADUser.ps1 $container .\users.csv
     
    #>

    
    param(
        ## The container in which to import users
        ## For example:
        ## "LDAP://localhost:389/ou=West,ou=Sales,dc=Fabrikam,dc=COM)")
        [Parameter(Mandatory = $true)]
        $Container,
    
        ## The path to the CSV that contains the user records
        [Parameter(Mandatory = $true)]
        $Path
    )
    
    Set-StrictMode -Off
    
    ## Bind to the container
    $userContainer = [adsi] $container
    
    ## Ensure that the container was valid
    if(-not $userContainer.Name)
    {
        Write-Error "Could not connect to $container"
        return
    }
    
    ## Load the CSV
    $users = @(Import-Csv $Path)
    if($users.Count -eq 0)
    {
        return
    }
    
    ## Go through each user from the CSV
    foreach($user in $users)
    {
        ## Pull out the name, and create that user
        $username = $user.CN
        $newUser = $userContainer.Create("User", "CN=$username")
    
        ## Go through each of the properties from the CSV, and set its value
        ## on the user
        foreach($property in $user.PsObject.Properties)
        {
            ## Skip the property if it was the CN property that sets the
            ## user name
            if($property.Name -eq "CN")
            {
                continue
            }
    
            ## Ensure they specified a value for the property
            if(-not $property.Value)
            {
                continue
            }
    
            ## Set the value of the property
            $newUser.Put($property.Name, $property.Value)
        }
    
        ## Finalize the information in Active Directory
        $newUser.SetInfo()
    }
}

function Invoke-AddTypeTypeDefinition
{
    #############################################################################
    ##
    ## Invoke-AddTypeTypeDefinition
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstrates the use of the -TypeDefinition parameter of the Add-Type
    cmdlet.
     
    #>

    
    Set-StrictMode -Version 3
    
    ## Define the new C# class
    $newType = @'
    using System;
     
    namespace PowerShellCookbook
    {
        public class AddTypeTypeDefinitionDemo
        {
            public string SayHello(string name)
            {
                string result = String.Format("Hello {0}", name);
                return result;
            }
        }
    }
     
'@

    
    ## Add it to the Powershell session
    Add-Type -TypeDefinition $newType
    
    ## Show that we can access it like any other .NET type
    $greeter = New-Object PowerShellCookbook.AddTypeTypeDefinitionDemo
    $greeter.SayHello("World")
}

function Invoke-AdvancedFunction
{
    param(
        [Parameter(Mandatory = $true)]
        [ScriptBlock] $Scriptblock
        )
    
    ## Invoke the scriptblock supplied by the user.
    & $scriptblock
}

function Invoke-BinaryProcess
{
    ##############################################################################
    ##
    ## Invoke-BinaryProcess
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Invokes a process that emits or consumes binary data.
     
    .EXAMPLE
     
    PS > Invoke-BinaryProcess binaryProcess.exe -RedirectOutput -ArgumentList "-Emit" |
           Invoke-BinaryProcess binaryProcess.exe -RedirectInput -ArgumentList "-Consume"
     
    #>

    
    param(
        ## The name of the process to invoke
        [string] $ProcessName,
    
        ## Specifies that input to the process should be treated as
        ## binary
        [Alias("Input")]
        [switch] $RedirectInput,
    
        ## Specifies that the output of the process should be treated
        ## as binary
        [Alias("Output")]
        [switch] $RedirectOutput,
    
        ## Specifies the arguments for the process
        [string] $ArgumentList
    )
    
    Set-StrictMode -Version 3
    
    ## Prepare to invoke the process
    $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo
    $processStartInfo.FileName = (Get-Command $processname).Definition
    $processStartInfo.WorkingDirectory = (Get-Location).Path
    if($argumentList) { $processStartInfo.Arguments = $argumentList }
    $processStartInfo.UseShellExecute = $false
    
    ## Always redirect the input and output of the process.
    ## Sometimes we will capture it as binary, other times we will
    ## just treat it as strings.
    $processStartInfo.RedirectStandardOutput = $true
    $processStartInfo.RedirectStandardInput = $true
    
    $process = [System.Diagnostics.Process]::Start($processStartInfo)
    
    ## If we've been asked to redirect the input, treat it as bytes.
    ## Otherwise, write any input to the process as strings.
    if($redirectInput)
    {
        $inputBytes = @($input)
        $process.StandardInput.BaseStream.Write($inputBytes, 0, $inputBytes.Count)
        $process.StandardInput.Close()
    }
    else
    {
        $input | % { $process.StandardInput.WriteLine($_) }
        $process.StandardInput.Close()
    }
    
    ## If we've been asked to redirect the output, treat it as bytes.
    ## Otherwise, read any input from the process as strings.
    if($redirectOutput)
    {
        $byteRead = -1
        do
        {
            $byteRead = $process.StandardOutput.BaseStream.ReadByte()
            if($byteRead -ge 0) { $byteRead }
        } while($byteRead -ge 0)
    }
    else
    {
        $process.StandardOutput.ReadToEnd()
    }
}

function Invoke-CmdScript
{
    ##############################################################################
    ##
    ## Invoke-CmdScript
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Invoke the specified batch file (and parameters), but also propigate any
    environment variable changes back to the PowerShell environment that
    called it.
     
    .EXAMPLE
     
    PS > type foo-that-sets-the-FOO-env-variable.cmd
    @set FOO=%*
    echo FOO set to %FOO%.
     
    PS > $env:FOO
    PS > Invoke-CmdScript "foo-that-sets-the-FOO-env-variable.cmd" Test
     
    C:\Temp>echo FOO set to Test.
    FOO set to Test.
     
    PS > $env:FOO
    Test
     
    #>

    
    param(
        ## The path to the script to run
        [Parameter(Mandatory = $true)]
        [string] $Path,
    
        ## The arguments to the script
        [string] $ArgumentList
    )
    
    Set-StrictMode -Version 3
    
    $tempFile = [IO.Path]::GetTempFileName()
    
    ## Store the output of cmd.exe. We also ask cmd.exe to output
    ## the environment table after the batch file completes
    cmd /c " `"$Path`" $argumentList && set > `"$tempFile`" "
    
    ## Go through the environment variables in the temp file.
    ## For each of them, set the variable in our local environment.
    Get-Content $tempFile | Foreach-Object {
        if($_ -match "^(.*?)=(.*)$")
        {
            Set-Content "env:\$($matches[1])" $matches[2]
        }
    }
    
    Remove-Item $tempFile
}

function Invoke-ComplexDebuggerScript
{
    #############################################################################
    ##
    ## Invoke-ComplexDebuggerScript
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstrates the functionality of PowerShell's debugging support.
     
    #>

    
    Set-StrictMode -Version 3
    
    function HelperFunction
    {
        $dirCount = 0
    }
    
    Write-Host "Calculating lots of complex information"
    
    $runningTotal = 0
    $runningTotal += [Math]::Pow(5 * 5 + 10, 2)
    $runningTotal
    
    $dirCount = @(Get-ChildItem $env:WINDIR).Count
    $dirCount
    
    HelperFunction
    
    $dirCount
    
    $runningTotal -= 10
    $runningTotal /= 2
    $runningTotal
    
    $runningTotal *= 3
    $runningTotal /= 2
    $runningTotal
}

function Invoke-ComplexScript
{
    #############################################################################
    ##
    ## Invoke-ComplexScript
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstrates the functionality of PowerShell's debugging support.
     
    #>

    
    Set-StrictMode -Version 3
    
    Write-Host "Calculating lots of complex information"
    
    $runningTotal = 0
    $runningTotal += [Math]::Pow(5 * 5 + 10, 2)
    
    Write-Debug "Current value: $runningTotal"
    
    Set-PsDebug -Trace 1
    $dirCount = @(Get-ChildItem $env:WINDIR).Count
    
    Set-PsDebug -Trace 2
    $runningTotal -= 10
    $runningTotal /= 2
    
    Set-PsDebug -Step
    $runningTotal *= 3
    $runningTotal /= 2
    
    $host.EnterNestedPrompt()
    
    Set-PsDebug -off
}

function Invoke-DemonstrationScript
{
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    
    "The script ran!"
}

function Invoke-ElevatedCommand
{
    ##############################################################################
    ##
    ## Invoke-ElevatedCommand
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Runs the provided script block under an elevated instance of PowerShell as
    through it were a member of a regular pipeline.
     
    .EXAMPLE
     
    PS > Get-Process | Invoke-ElevatedCommand.ps1 {
        $input | Where-Object { $_.Handles -gt 500 } } | Sort Handles
     
    #>

    
    param(
        ## The script block to invoke elevated
        [Parameter(Mandatory = $true)]
        [ScriptBlock] $Scriptblock,
    
        ## Any input to give the elevated process
        [Parameter(ValueFromPipeline = $true)]
        $InputObject,
    
        ## Switch to enable the user profile
        [switch] $EnableProfile
    )
    
    begin
    {
        Set-StrictMode -Version 3
        $inputItems = New-Object System.Collections.ArrayList
    }
    
    process
    {
        $null = $inputItems.Add($inputObject)
    }
    
    end
    {
        ## Create some temporary files for streaming input and output
        $outputFile = [IO.Path]::GetTempFileName()
        $inputFile = [IO.Path]::GetTempFileName()
    
        ## Stream the input into the input file
        $inputItems.ToArray() | Export-CliXml -Depth 1 $inputFile
    
        ## Start creating the command line for the elevated PowerShell session
        $commandLine = ""
        if(-not $EnableProfile) { $commandLine += "-NoProfile " }
    
        ## Convert the command into an encoded command for PowerShell
        $commandString = "Set-Location '$($pwd.Path)'; " +
            "`$output = Import-CliXml '$inputFile' | " +
            "& {" + $scriptblock.ToString() + "} 2>&1; " +
            "`$output | Export-CliXml -Depth 1 '$outputFile'"
    
        $commandBytes = [System.Text.Encoding]::Unicode.GetBytes($commandString)
        $encodedCommand = [Convert]::ToBase64String($commandBytes)
        $commandLine += "-EncodedCommand $encodedCommand"
    
        ## Start the new PowerShell process
        $process = Start-Process -FilePath (Get-Command powershell).Definition `
            -ArgumentList $commandLine -Verb RunAs `
            -WindowStyle Hidden `
            -Passthru
        $process.WaitForExit()
    
        ## Return the output to the user
        if((Get-Item $outputFile).Length -gt 0)
        {
            Import-CliXml $outputFile
        }
    
        ## Clean up
        [Console]::WriteLine($outputFile)
        Remove-Item $outputFile
        Remove-Item $inputFile
    }
}

function Invoke-Inline
{
    #############################################################################
    ##
    ## Invoke-Inline
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    #############################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstrates the Add-Type cmdlet to invoke in-line C#
     
    #>

    
    Set-StrictMode -Version 3
    
    $inlineType = Add-Type -Name InvokeInline_Inline -PassThru `
        -MemberDefinition @'
        public static int RightShift(int original, int places)
        {
            return original >> places;
        }
'@

    
    $inlineType::RightShift(1024, 3)
}

function Invoke-LocalizedScript
{
    Set-StrictMode -Version 3
    
    ## Create some default messages for English cultures, and
    ## when culture-specific messages are not available.
    $messages = DATA {
        @{
            Greeting = "Hello, {0}"
            Goodbye = "So long."
        }
    }
    
    ## Import localized messages for the current culture.
    Import-LocalizedData messages -ErrorAction SilentlyContinue
    
    ## Output the localized messages
    $messages.Greeting -f "World"
    $messages.Goodbye
}

function Invoke-LongRunningOperation
{
    ##############################################################################
    ##
    ## Invoke-LongRunningOperation
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstrates the functionality of the Write-Progress cmdlet
     
    #>

    
    Set-StrictMode -Version 3
    
    $activity = "A long running operation"
    $status = "Initializing"
    
    ## Initialize the long-running operation
    for($counter = 0; $counter -lt 100; $counter++)
    {
        $currentOperation = "Initializing item $counter"
        Write-Progress $activity $status -PercentComplete $counter `
            -CurrentOperation $currentOperation
        Start-Sleep -m 20
    }
    
    $status = "Running"
    
    ## Initialize the long-running operation
    for($counter = 0; $counter -lt 100; $counter++)
    {
        $currentOperation = "Running task $counter"
        Write-Progress $activity $status -PercentComplete $counter `
            -CurrentOperation $currentOperation
        Start-Sleep -m 20
    }
}

function Invoke-Member
{
    ##############################################################################
    ##
    ## Invoke-Member
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Enables easy access to methods and properties of pipeline objects.
     
    .EXAMPLE
     
    PS > "Hello","World" | .\Invoke-Member Length
    5
    5
     
    .EXAMPLE
     
    PS > "Hello","World" | .\Invoke-Member -m ToUpper
    HELLO
    WORLD
     
    .EXAMPLE
     
    PS > "Hello","World" | .\Invoke-Member Replace l w
    Hewwo
    Worwd
     
    #>

    
    [CmdletBinding(DefaultParameterSetName= "Member")]
    param(
    
        ## A switch parameter to identify the requested member as a method.
        ## Only required for methods that take no arguments.
        [Parameter(ParameterSetName = "Method")]
        [Alias("M","Me")]
        [switch] $Method,
    
        ## The name of the member to retrieve
        [Parameter(ParameterSetName = "Method", Position = 0)]
        [Parameter(ParameterSetName = "Member", Position = 0)]
        [string] $Member,
    
        ## Arguments for the method, if any
        [Parameter(
            ParameterSetName = "Method", Position = 1,
            Mandatory = $false, ValueFromRemainingArguments = $true)]
        [object[]] $ArgumentList = @(),
    
        ## The object from which to retrieve the member
        [Parameter(ValueFromPipeline = $true)]
        $InputObject
        )
    
    process
    {
        ## If the user specified a method, invoke it
        ## with any required arguments.
        if($psCmdlet.ParameterSetName -eq "Method")
        {
            $inputObject.$member.Invoke(@($argumentList))
        }
        ## Otherwise, retrieve the property
        else
        {
            $inputObject.$member
        }
    }
}

function Invoke-RemoteExpression
{
    ##############################################################################
    ##
    ## Invoke-RemoteExpression
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Invoke a PowerShell expression on a remote machine. Requires PsExec from
    http://live.sysinternals.com/tools/psexec.exe. If the remote machine
    supports PowerShell version two, use PowerShell remoting instead.
     
    .EXAMPLE
     
    PS > Invoke-RemoteExpression LEE-DESK { Get-Process }
    Retrieves the output of a simple command from a remote machine
     
    .EXAMPLE
     
    PS > (Invoke-RemoteExpression LEE-DESK { Get-Date }).AddDays(1)
    Invokes a command on a remote machine. Since the command returns one of
    PowerShell's primitive types (a DateTime object,) you can manipulate
    its output as an object afterward.
     
    .EXAMPLE
     
    PS > Invoke-RemoteExpression LEE-DESK { Get-Process } | Sort Handles
    Invokes a command on a remote machine. The command does not return one of
    PowerShell's primitive types, but you can still use PowerShell's filtering
    cmdlets to work with its structured output.
     
    #>

    
    param(
        ## The computer on which to invoke the command.
        $ComputerName = "$ENV:ComputerName",
    
        ## The scriptblock to invoke on the remote machine.
        [Parameter(Mandatory = $true)]
        [ScriptBlock] $ScriptBlock,
    
        ## The username / password to use in the connection
        $Credential,
    
        ## Determines if PowerShell should load the user's PowerShell profile
        ## when invoking the command.
        [switch] $NoProfile
    )
    
    Set-StrictMode -Version 3
    
    ## Prepare the computername for PSExec
    if($ComputerName -notmatch '^\\')
    {
        $ComputerName = "\\$ComputerName"
    }
    
    ## Prepare the command line for PsExec. We use the XML output encoding so
    ## that PowerShell can convert the output back into structured objects.
    ## PowerShell expects that you pass it some input when being run by PsExec
    ## this way, so the 'echo .' statement satisfies that appetite.
    $commandLine = "echo . | powershell -Output XML "
    
    if($noProfile)
    {
        $commandLine += "-NoProfile "
    }
    
    ## Convert the command into an encoded command for PowerShell
    $commandBytes = [System.Text.Encoding]::Unicode.GetBytes($scriptblock)
    $encodedCommand = [Convert]::ToBase64String($commandBytes)
    $commandLine += "-EncodedCommand $encodedCommand"
    
    ## Collect the output and error output
    $errorOutput = [IO.Path]::GetTempFileName()
    
    if($Credential)
    {
        ## This lets users pass either a username, or full credential to our
        ## credential parameter
        $credential = Get-Credential $credential
        $networkCredential = $credential.GetNetworkCredential()
        $username = $networkCredential.Username
        $password = $networkCredential.Password
    
        $output = psexec $computername /user $username /password $password `
            /accepteula cmd /c $commandLine 2>$errorOutput
    }
    else
    {
        $output = psexec /acceptEula $computername cmd /c $commandLine 2>$errorOutput
    }
    
    ## Check for any errors
    $errorContent = Get-Content $errorOutput
    Remove-Item $errorOutput
    
    if($lastExitCode -ne 0)
    {
        $OFS = "`n"
        $errorMessage = "Could not execute remote expression. "
        $errorMessage += "Ensure that your account has administrative " +
            "privileges on the target machine.`n"
        $errorMessage += ($errorContent -match "psexec.exe :")
    
        Write-Error $errorMessage
    }
    
    ## Return the output to the user
    $output
}

function Invoke-ScriptBlock
{
    ##############################################################################
    ##
    ## Invoke-ScriptBlock
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Apply the given mapping command to each element of the input. (Note that
    PowerShell includes this command natively, and calls it Foreach-Object)
     
    .EXAMPLE
     
    PS > 1,2,3 | Invoke-ScriptBlock { $_ * 2 }
     
    #>

    
    param(
        ## The scriptblock to apply to each incoming element
        [ScriptBlock] $MapCommand
    )
    
    begin
    {
        Set-StrictMode -Version 3
    }
    process
    {
        & $mapCommand
    }
}

function Invoke-ScriptBlockClosure
{
    ##############################################################################
    ##
    ## Invoke-ScriptBlockClosure
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstrates the GetNewClosure() method on a script block that pulls variables
    in from the user's session (if they are defined.)
     
    .EXAMPLE
     
    PS > $name = "Hello There"
    PS > Invoke-ScriptBlockClosure { $name }
    Hello There
    Hello World
    Hello There
     
    #>

    
    param(
        ## The scriptblock to invoke
        [ScriptBlock] $ScriptBlock
    )
    
    Set-StrictMode -Version 3
    
    ## Create a new script block that pulls variables
    ## from the user's scope (if defined.)
    $closedScriptBlock = $scriptBlock.GetNewClosure()
    
    ## Invoke the script block normally. The contents of
    ## the $name variable will be from the user's session.
    & $scriptBlock
    
    ## Define a new variable
    $name = "Hello World"
    
    ## Invoke the script block normally. The contents of
    ## the $name variable will be "Hello World", now from
    ## our scope.
    & $scriptBlock
    
    ## Invoke the "closed" script block. The contents of
    ## the $name variable will still be whatever was in the user's session
    ## (if it was defined.)
    & $closedScriptBlock
}

function Invoke-ScriptThatRequiresMta
{
    ###########################################################################
    ##
    ## Invoke-ScriptThatRequiresMta
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ###########################################################################
    
    <#
     
    .SYNOPSIS
     
    Demonstrates a technique to relaunch a script that requires MTA mode.
    This is useful only for simple parameter definitions that can be
    specified positionally.
     
    #>

    
    param(
        $Parameter1,
        $Parameter2
    )
    
    Set-StrictMode -Version 3
    
    "Current threading mode: " + $host.Runspace.ApartmentState
    "Parameter1 is: $parameter1"
    "Parameter2 is: $parameter2"
    
    if($host.Runspace.ApartmentState -eq "STA")
    {
        "Relaunching"
        $file = $myInvocation.MyCommand.Path
        powershell -NoProfile -Mta -File $file $parameter1 $parameter2
        return
    }
    
    "After relaunch - current threading mode: " + $host.Runspace.ApartmentState
}

function Invoke-SqlCommand
{
    ##############################################################################
    ##
    ## Invoke-SqlCommand
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Return the results of a SQL query or operation
     
    .EXAMPLE
     
    Invoke-SqlCommand.ps1 -Sql "SELECT TOP 10 * FROM Orders"
    Invokes a command using Windows authentication
     
    .EXAMPLE
     
    PS > $cred = Get-Credential
    PS > Invoke-SqlCommand.ps1 -Sql "SELECT TOP 10 * FROM Orders" -Cred $cred
    Invokes a command using SQL Authentication
     
    .EXAMPLE
     
    PS > $server = "MYSERVER"
    PS > $database = "Master"
    PS > $sql = "UPDATE Orders SET EmployeeID = 6 WHERE OrderID = 10248"
    PS > Invoke-SqlCommand $server $database $sql
    Invokes a command that performs an update
     
    .EXAMPLE
     
    PS > $sql = "EXEC SalesByCategory 'Beverages'"
    PS > Invoke-SqlCommand -Sql $sql
    Invokes a stored procedure
     
    .EXAMPLE
     
    PS > Invoke-SqlCommand (Resolve-Path access_test.mdb) -Sql "SELECT * FROM Users"
    Access an Access database
     
    .EXAMPLE
     
    PS > Invoke-SqlCommand (Resolve-Path xls_test.xls) -Sql 'SELECT * FROM [Sheet1$]'
    Access an Excel file
     
    #>

    
    param(
        ## The data source to use in the connection
        [string] $DataSource = ".\SQLEXPRESS",
    
        ## The database within the data source
        [string] $Database = "Northwind",
    
        ## The SQL statement(s) to invoke against the database
        [Parameter(Mandatory = $true)]
        [string[]] $SqlCommand,
    
        ## The timeout, in seconds, to wait for the query to complete
        [int] $Timeout = 60,
    
        ## The credential to use in the connection, if any.
        $Credential
    )
    
    
    Set-StrictMode -Version 3
    
    ## Prepare the authentication information. By default, we pick
    ## Windows authentication
    $authentication = "Integrated Security=SSPI;"
    
    ## If the user supplies a credential, then they want SQL
    ## authentication
    if($credential)
    {
        $credential = Get-Credential $credential
        $plainCred = $credential.GetNetworkCredential()
        $authentication =
            ("uid={0};pwd={1};" -f $plainCred.Username,$plainCred.Password)
    }
    
    ## Prepare the connection string out of the information they
    ## provide
    $connectionString = "Provider=sqloledb; " +
                        "Data Source=$dataSource; " +
                        "Initial Catalog=$database; " +
                        "$authentication; "
    
    ## If they specify an Access database or Excel file as the connection
    ## source, modify the connection string to connect to that data source
    if($dataSource -match '\.xls$|\.mdb$')
    {
        $connectionString = "Provider=Microsoft.Jet.OLEDB.4.0; " +
            "Data Source=$dataSource; "
    
        if($dataSource -match '\.xls$')
        {
            $connectionString += 'Extended Properties="Excel 8.0;"; '
    
            ## Generate an error if they didn't specify the sheet name properly
            if($sqlCommand -notmatch '\[.+\$\]')
            {
                $error = 'Sheet names should be surrounded by square brackets, ' +
                    'and have a dollar sign at the end: [Sheet1$]'
                Write-Error $error
                return
            }
        }
    }
    
    ## Connect to the data source and open it
    $connection = New-Object System.Data.OleDb.OleDbConnection $connectionString
    $connection.Open()
    
    foreach($commandString in $sqlCommand)
    {
        $command = New-Object Data.OleDb.OleDbCommand $commandString,$connection
        $command.CommandTimeout = $timeout
    
        ## Fetch the results, and close the connection
        $adapter = New-Object System.Data.OleDb.OleDbDataAdapter $command
        $dataset = New-Object System.Data.DataSet
        [void] $adapter.Fill($dataSet)
    
        ## Return all of the rows from their query
        $dataSet.Tables | Select-Object -Expand Rows
    }
    
    $connection.Close()
}

function Invoke-WindowsApi
{
    ##############################################################################
    ##
    ## Invoke-WindowsApi
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Invoke a native Windows API call that takes and returns simple data types.
     
    .EXAMPLE
     
    ## Prepare the parameter types and parameters for the CreateHardLink function
    PS > $filename = "c:\temp\hardlinked.txt"
    PS > $existingFilename = "c:\temp\link_target.txt"
    PS > Set-Content $existingFilename "Hard Link target"
    PS > $parameterTypes = [string], [string], [IntPtr]
    PS > $parameters = [string] $filename, [string] $existingFilename,
        [IntPtr]::Zero
     
    ## Call the CreateHardLink method in the Kernel32 DLL
    PS > $result = Invoke-WindowsApi "kernel32" ([bool]) "CreateHardLink" `
        $parameterTypes $parameters
    PS > Get-Content C:\temp\hardlinked.txt
    Hard Link target
     
    #>

    
    param(
        ## The name of the DLL that contains the Windows API, such as "kernel32"
        [string] $DllName,
    
        ## The return type expected from Windows API
        [Type] $ReturnType,
    
        ## The name of the Windows API
        [string] $MethodName,
    
        ## The types of parameters expected by the Windows API
        [Type[]] $ParameterTypes,
    
        ## Parameter values to pass to the Windows API
        [Object[]] $Parameters
    )
    
    Set-StrictMode -Version 3
    
    ## Begin to build the dynamic assembly
    $domain = [AppDomain]::CurrentDomain
    $name = New-Object Reflection.AssemblyName 'PInvokeAssembly'
    $assembly = $domain.DefineDynamicAssembly($name, 'Run')
    $module = $assembly.DefineDynamicModule('PInvokeModule')
    $type = $module.DefineType('PInvokeType', "Public,BeforeFieldInit")
    
    ## Go through all of the parameters passed to us. As we do this,
    ## we clone the user's inputs into another array that we will use for
    ## the P/Invoke call.
    $inputParameters = @()
    $refParameters = @()
    
    for($counter = 1; $counter -le $parameterTypes.Length; $counter++)
    {
        ## If an item is a PSReference, then the user
        ## wants an [out] parameter.
        if($parameterTypes[$counter - 1] -eq [Ref])
        {
            ## Remember which parameters are used for [Out] parameters
            $refParameters += $counter
    
            ## On the cloned array, we replace the PSReference type with the
            ## .Net reference type that represents the value of the PSReference,
            ## and the value with the value held by the PSReference.
            $parameterTypes[$counter - 1] =
                $parameters[$counter - 1].Value.GetType().MakeByRefType()
            $inputParameters += $parameters[$counter - 1].Value
        }
        else
        {
            ## Otherwise, just add their actual parameter to the
            ## input array.
            $inputParameters += $parameters[$counter - 1]
        }
    }
    
    ## Define the actual P/Invoke method, adding the [Out]
    ## attribute for any parameters that were originally [Ref]
    ## parameters.
    $method = $type.DefineMethod(
        $methodName, 'Public,HideBySig,Static,PinvokeImpl',
        $returnType, $parameterTypes)
    foreach($refParameter in $refParameters)
    {
        [void] $method.DefineParameter($refParameter, "Out", $null)
    }
    
    ## Apply the P/Invoke constructor
    $ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([string])
    $attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, $dllName
    $method.SetCustomAttribute($attr)
    
    ## Create the temporary type, and invoke the method.
    $realType = $type.CreateType()
    
    $realType.InvokeMember(
        $methodName, 'Public,Static,InvokeMethod', $null, $null,$inputParameters)
    
    ## Finally, go through all of the reference parameters, and update the
    ## values of the PSReference objects that the user passed in.
    foreach($refParameter in $refParameters)
    {
        $parameters[$refParameter - 1].Value = $inputParameters[$refParameter - 1]
    }
}

function Measure-CommandPerformance
{
    ##############################################################################
    ##
    ## Measure-CommandPerformance
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Measures the average time of a command, accounting for natural variability by
    automatically ignoring the top and bottom ten percent.
     
    .EXAMPLE
     
    PS > Measure-CommandPerformance.ps1 { Start-Sleep -m 300 }
     
    Count : 30
    Average : 312.10155
    (...)
     
    #>

    
    param(
        ## The command to measure
        [Scriptblock] $Scriptblock,
    
        ## The number of times to measure the command's performance
        [int] $Iterations = 30
    )
    
    Set-StrictMode -Version 3
    
    ## Figure out how many extra iterations we need to account for the outliers
    $buffer = [int] ($iterations * 0.1)
    $totalIterations = $iterations + (2 * $buffer)
    
    ## Get the results
    $results = 1..$totalIterations |
        Foreach-Object { Measure-Command $scriptblock }
    
    ## Sort the results, and skip the outliers
    $middleResults = $results | Sort TotalMilliseconds |
        Select -Skip $buffer -First $iterations
    
    ## Show the average
    $middleResults | Measure-Object -Average TotalMilliseconds
}

function Move-LockedFile
{
    ##############################################################################
    ##
    ## Move-LockedFile
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Registers a locked file to be moved at the next system restart.
     
    .EXAMPLE
     
    PS > Move-LockedFile c:\temp\locked.txt c:\temp\locked.txt.bak
     
    #>

    
    param(
        ## The current location of the file to move
        $Path,
    
        ## The target location of the file
        $Destination
    )
    
    Set-StrictMode -Version 3
    
    ## Convert the the path and destination to fully qualified paths
    $path = (Resolve-Path $path).Path
    $destination = $executionContext.SessionState.`
        Path.GetUnresolvedProviderPathFromPSPath($destination)
    
    ## Define a new .NET type that calls into the Windows API to
    ## move a locked file.
    $MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004
    $memberDefinition = @'
    [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern bool MoveFileEx(
        string lpExistingFileName, string lpNewFileName, int dwFlags);
'@

    $type = Add-Type -Name MoveFileUtils `
        -MemberDefinition $memberDefinition -PassThru
    
    ## Move the file
    $type::MoveFileEx($path, $destination, $MOVEFILE_DELAY_UNTIL_REBOOT)
}

function New-CommandWrapper
{
    ##############################################################################
    ##
    ## New-CommandWrapper
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Adds parameters and functionality to existing cmdlets and functions.
     
    .EXAMPLE
     
    New-CommandWrapper Get-Process `
          -AddParameter @{
              SortBy = {
                  $newPipeline = {
                      __ORIGINAL_COMMAND__ | Sort-Object -Property $SortBy
                  }
              }
          }
     
    This example adds a 'SortBy' parameter to Get-Process. It accomplishes
    this by adding a Sort-Object command to the pipeline.
     
    .EXAMPLE
     
    $parameterAttributes = @'
              [Parameter(Mandatory = $true)]
              [ValidateRange(50,75)]
              [Int]
'@
     
    New-CommandWrapper Clear-Host `
          -AddParameter @{
              @{
                  Name = 'MyMandatoryInt';
                  Attributes = $parameterAttributes
              } = {
                  Write-Host $MyMandatoryInt
                  Read-Host "Press ENTER"
             }
          }
     
    This example adds a new mandatory 'MyMandatoryInt' parameter to
    Clear-Host. This parameter is also validated to fall within the range
    of 50 to 75. It doesn't alter the pipeline, but does display some
    information on the screen before processing the original pipeline.
     
    #>

    
    param(
        ## The name of the command to extend
        [Parameter(Mandatory = $true)]
        $Name,
    
        ## Script to invoke before the command begins
        [ScriptBlock] $Begin,
    
        ## Script to invoke for each input element
        [ScriptBlock] $Process,
    
        ## Script to invoke at the end of the command
        [ScriptBlock] $End,
    
        ## Parameters to add, and their functionality.
        ##
        ## The Key of the hashtable can be either a simple parameter name,
        ## or a more advanced parameter description.
        ##
        ## If you want to add additional parameter validation (such as a
        ## parameter type,) then the key can itself be a hashtable with the keys
        ## 'Name' and 'Attributes'. 'Attributes' is the text you would use when
        ## defining this parameter as part of a function.
        ##
        ## The Value of each hashtable entry is a scriptblock to invoke
        ## when this parameter is selected. To customize the pipeline,
        ## assign a new scriptblock to the $newPipeline variable. Use the
        ## special text, __ORIGINAL_COMMAND__, to represent the original
        ## command. The $targetParameters variable represents a hashtable
        ## containing the parameters that will be passed to the original
        ## command.
        [HashTable] $AddParameter
    )
    
    Set-StrictMode -Version 3
    
    ## Store the target command we are wrapping, and its command type
    $target = $Name
    $commandType = "Cmdlet"
    
    ## If a function already exists with this name (perhaps it's already been
    ## wrapped,) rename the other function and chain to its new name.
    if(Test-Path function:\$Name)
    {
        $target = "$Name" + "-" + [Guid]::NewGuid().ToString().Replace("-","")
        Rename-Item function:\GLOBAL:$Name GLOBAL:$target
        $commandType = "Function"
    }
    
    ## The template we use for generating a command proxy
    $proxy = @'
     
    __CMDLET_BINDING_ATTRIBUTE__
    param(
    __PARAMETERS__
    )
    begin
    {
        try {
            __CUSTOM_BEGIN__
     
            ## Access the REAL Foreach-Object command, so that command
            ## wrappers do not interfere with this script
            $foreachObject = $executionContext.InvokeCommand.GetCmdlet(
                "Microsoft.PowerShell.Core\Foreach-Object")
     
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(
                '__COMMAND_NAME__',
                [System.Management.Automation.CommandTypes]::__COMMAND_TYPE__)
     
            ## TargetParameters represents the hashtable of parameters that
            ## we will pass along to the wrapped command
            $targetParameters = @{}
            $PSBoundParameters.GetEnumerator() |
                & $foreachObject {
                    if($command.Parameters.ContainsKey($_.Key))
                    {
                        $targetParameters.Add($_.Key, $_.Value)
                    }
                }
     
            ## finalPipeline represents the pipeline we wil ultimately run
            $newPipeline = { & $wrappedCmd @targetParameters }
            $finalPipeline = $newPipeline.ToString()
     
            __CUSTOM_PARAMETER_PROCESSING__
     
            $steppablePipeline = [ScriptBlock]::Create(
                $finalPipeline).GetSteppablePipeline()
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }
     
    process
    {
        try {
            __CUSTOM_PROCESS__
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }
     
    end
    {
        try {
            __CUSTOM_END__
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
     
    dynamicparam
    {
        ## Access the REAL Get-Command, Foreach-Object, and Where-Object
        ## commands, so that command wrappers do not interfere with this script
        $getCommand = $executionContext.InvokeCommand.GetCmdlet(
            "Microsoft.PowerShell.Core\Get-Command")
        $foreachObject = $executionContext.InvokeCommand.GetCmdlet(
            "Microsoft.PowerShell.Core\Foreach-Object")
        $whereObject = $executionContext.InvokeCommand.GetCmdlet(
            "Microsoft.PowerShell.Core\Where-Object")
     
        ## Find the parameters of the original command, and remove everything
        ## else from the bound parameter list so we hide parameters the wrapped
        ## command does not recognize.
        $command = & $getCommand __COMMAND_NAME__ -Type __COMMAND_TYPE__
        $targetParameters = @{}
        $PSBoundParameters.GetEnumerator() |
            & $foreachObject {
                if($command.Parameters.ContainsKey($_.Key))
                {
                    $targetParameters.Add($_.Key, $_.Value)
                }
            }
     
        ## Get the argumment list as it would be passed to the target command
        $argList = @($targetParameters.GetEnumerator() |
            Foreach-Object { "-$($_.Key)"; $_.Value })
     
        ## Get the dynamic parameters of the wrapped command, based on the
        ## arguments to this command
        $command = $null
        try
        {
            $command = & $getCommand __COMMAND_NAME__ -Type __COMMAND_TYPE__ `
                -ArgumentList $argList
        }
        catch
        {
     
        }
     
        $dynamicParams = @($command.Parameters.GetEnumerator() |
            & $whereObject { $_.Value.IsDynamic })
     
        ## For each of the dynamic parameters, add them to the dynamic
        ## parameters that we return.
        if ($dynamicParams.Length -gt 0)
        {
            $paramDictionary = `
                New-Object Management.Automation.RuntimeDefinedParameterDictionary
            foreach ($param in $dynamicParams)
            {
                $param = $param.Value
                $arguments = $param.Name, $param.ParameterType, $param.Attributes
                $newParameter = `
                    New-Object Management.Automation.RuntimeDefinedParameter `
                    $arguments
                $paramDictionary.Add($param.Name, $newParameter)
            }
            return $paramDictionary
        }
    }
     
    <#
     
    .ForwardHelpTargetName __COMMAND_NAME__
    .ForwardHelpCategory __COMMAND_TYPE__
     
    #>
     
'@

    
    ## Get the information about the original command
    $originalCommand = Get-Command $target
    $metaData = New-Object System.Management.Automation.CommandMetaData `
        $originalCommand
    $proxyCommandType = [System.Management.Automation.ProxyCommand]
    
    ## Generate the cmdlet binding attribute, and replace information
    ## about the target
    $proxy = $proxy.Replace("__CMDLET_BINDING_ATTRIBUTE__",
        $proxyCommandType::GetCmdletBindingAttribute($metaData))
    $proxy = $proxy.Replace("__COMMAND_NAME__", $target)
    $proxy = $proxy.Replace("__COMMAND_TYPE__", $commandType)
    
    ## Stores new text we'll be putting in the param() block
    $newParamBlockCode = ""
    
    ## Stores new text we'll be putting in the begin block
    ## (mostly due to parameter processing)
    $beginAdditions = ""
    
    ## If the user wants to add a parameter
    $currentParameter = $originalCommand.Parameters.Count
    if($AddParameter)
    {
        foreach($parameter in $AddParameter.Keys)
        {
            ## Get the code associated with this parameter
            $parameterCode = $AddParameter[$parameter]
    
            ## If it's an advanced parameter declaration, the hashtable
            ## holds the validation and / or type restrictions
            if($parameter -is [Hashtable])
            {
                ## Add their attributes and other information to
                ## the variable holding the parameter block additions
                if($currentParameter -gt 0)
                {
                    $newParamBlockCode += ","
                }
    
                $newParamBlockCode += "`n`n " +
                    $parameter.Attributes + "`n" +
                    ' $' + $parameter.Name
    
                $parameter = $parameter.Name
            }
            else
            {
                ## If this is a simple parameter name, add it to the list of
                ## parameters. The proxy generation APIs will take care of
                ## adding it to the param() block.
                $newParameter =
                    New-Object System.Management.Automation.ParameterMetadata `
                        $parameter
                $metaData.Parameters.Add($parameter, $newParameter)
            }
    
            $parameterCode = $parameterCode.ToString()
    
            ## Create the template code that invokes their parameter code if
            ## the parameter is selected.
            $templateCode = @"
     
            if(`$PSBoundParameters['$parameter'])
            {
                $parameterCode
     
                ## Replace the __ORIGINAL_COMMAND__ tag with the code
                ## that represents the original command
                `$alteredPipeline = `$newPipeline.ToString()
                `$finalPipeline = `$alteredPipeline.Replace(
                    '__ORIGINAL_COMMAND__', `$finalPipeline)
            }
"@

    
            ## Add the template code to the list of changes we're making
            ## to the begin() section.
            $beginAdditions += $templateCode
            $currentParameter++
        }
    }
    
    ## Generate the param() block
    $parameters = $proxyCommandType::GetParamBlock($metaData)
    if($newParamBlockCode) { $parameters += $newParamBlockCode }
    $proxy = $proxy.Replace('__PARAMETERS__', $parameters)
    
    ## Update the begin, process, and end sections
    $proxy = $proxy.Replace('__CUSTOM_BEGIN__', $Begin)
    $proxy = $proxy.Replace('__CUSTOM_PARAMETER_PROCESSING__', $beginAdditions)
    $proxy = $proxy.Replace('__CUSTOM_PROCESS__', $Process)
    $proxy = $proxy.Replace('__CUSTOM_END__', $End)
    
    ## Save the function wrapper
    Write-Verbose $proxy
    Set-Content function:\GLOBAL:$NAME $proxy
    
    ## If we were wrapping a cmdlet, hide it so that it doesn't conflict with
    ## Get-Help and Get-Command
    if($commandType -eq "Cmdlet")
    {
        $originalCommand.Visibility = "Private"
    }
}

function New-DynamicVariable
{
    ##############################################################################
    ##
    ## New-DynamicVariable
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Creates a variable that supports scripted actions for its getter and setter
     
    .EXAMPLE
     
    PS > .\New-DynamicVariable GLOBAL:WindowTitle `
         -Getter { $host.UI.RawUI.WindowTitle } `
         -Setter { $host.UI.RawUI.WindowTitle = $args[0] }
     
    PS > $windowTitle
    Administrator: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
    PS > $windowTitle = "Test"
    PS > $windowTitle
    Test
     
    #>

    
    param(
        ## The name for the dynamic variable
        [Parameter(Mandatory = $true)]
        $Name,
    
        ## The scriptblock to invoke when getting the value of the variable
        [Parameter(Mandatory = $true)]
        [ScriptBlock] $Getter,
    
        ## The scriptblock to invoke when setting the value of the variable
        [ScriptBlock] $Setter
    )
    
    Set-StrictMode -Version 3
    
    Add-Type @"
    using System;
    using System.Collections.ObjectModel;
    using System.Management.Automation;
     
    namespace Lee.Holmes
    {
        public class DynamicVariable : PSVariable
        {
            public DynamicVariable(
                string name,
                ScriptBlock scriptGetter,
                ScriptBlock scriptSetter)
                    : base(name, null, ScopedItemOptions.AllScope)
            {
                getter = scriptGetter;
                setter = scriptSetter;
            }
            private ScriptBlock getter;
            private ScriptBlock setter;
     
            public override object Value
            {
                get
                {
                    if(getter != null)
                    {
                        Collection<PSObject> results = getter.Invoke();
                        if(results.Count == 1)
                        {
                            return results[0];
                        }
                        else
                        {
                            PSObject[] returnResults =
                                new PSObject[results.Count];
                            results.CopyTo(returnResults, 0);
                            return returnResults;
                        }
                    }
                    else { return null; }
                }
                set
                {
                    if(setter != null) { setter.Invoke(value); }
                }
            }
        }
    }
"@

    
    ## If we've already defined the variable, remove it.
    if(Test-Path variable:\$name)
    {
        Remove-Item variable:\$name -Force
    }
    
    ## Set the new variable, along with its getter and setter.
    $executioncontext.SessionState.PSVariable.Set(
        (New-Object Lee.Holmes.DynamicVariable $name,$getter,$setter))
}

function New-FilesystemHardLink
{
    ##############################################################################
    ##
    ## New-FileSystemHardLink
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Create a new hard link, which allows you to create a new name by which you
    can access an existing file. Windows only deletes the actual file once
    you delete all hard links that point to it.
     
    .EXAMPLE
     
    PS > "Hello" > test.txt
    PS > dir test* | select name
     
    Name
    ----
    test.txt
     
    PS > .\New-FilesystemHardLink.ps1 test.txt test2.txt
    PS > type test2.txt
    Hello
    PS > dir test* | select name
     
    Name
    ----
    test.txt
    test2.txt
     
    #>

    
    param(
        ## The existing file that you want the new name to point to
        [string] $Path,
    
        ## The new filename you want to create
        [string] $Destination
    )
    
    Set-StrictMode -Version 3
    
    ## Ensure that the provided names are absolute paths
    $filename = $executionContext.SessionState.`
        Path.GetUnresolvedProviderPathFromPSPath($destination)
    $existingFilename = Resolve-Path $path
    
    ## Prepare the parameter types and parameters for the CreateHardLink function
    $parameterTypes = [string], [string], [IntPtr]
    $parameters = [string] $filename, [string] $existingFilename, [IntPtr]::Zero
    
    ## Call the CreateHardLink method in the Kernel32 DLL
    $currentDirectory = Split-Path $myInvocation.MyCommand.Path
    $invokeWindowsApiCommand = Join-Path $currentDirectory Invoke-WindowsApi.ps1
    $result = & $invokeWindowsApiCommand "kernel32" `
        ([bool]) "CreateHardLink" $parameterTypes $parameters
    
    ## Provide an error message if the call fails
    if(-not $result)
    {
        $message = "Could not create hard link of $filename to " +
            "existing file $existingFilename"
        Write-Error $message
    }
}

function New-GenericObject
{
    ##############################################################################
    ##
    ## New-GenericObject
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Creates an object of a generic type. If using PowerShell version three,
    use this syntax:
     
    PS > $r = New-Object "System.Collections.Generic.Dictionary[String, Int32]"
     
    .EXAMPLE
     
    PS > New-GenericObject System.Collections.ObjectModel.Collection System.Int32
    Creates a simple generic collection
     
    .EXAMPLE
     
    PS > New-GenericObject System.Collections.Generic.Dictionary `
          System.String,System.Int32
    Creates a generic dictionary with two types
     
    .EXAMPLE
     
    PS > $secondType = New-GenericObject System.Collections.Generic.List Int32
    PS > New-GenericObject System.Collections.Generic.Dictionary `
          System.String,$secondType.GetType()
    Creates a generic list as the second type to a generic dictionary
     
    .EXAMPLE
     
    PS > New-GenericObject System.Collections.Generic.LinkedListNode `
          System.String "Hi"
    Creates a generic type with a non-default constructor
     
    #>

    
    param(
        ## The generic type to create
        [Parameter(Mandatory = $true)]
        [string] $TypeName,
    
        ## The types that should be applied to the generic object
        [Parameter(Mandatory = $true)]
        [string[]] $TypeParameters,
    
        ## Arguments to be passed to the constructor
        [object[]] $ConstructorParameters
    )
    
    Set-StrictMode -Version 2
    
    ## Create the generic type name
    $genericTypeName = $typeName + '`' + $typeParameters.Count
    $genericType = [Type] $genericTypeName
    
    if(-not $genericType)
    {
        throw "Could not find generic type $genericTypeName"
    }
    
    ## Bind the type arguments to it
    [type[]] $typedParameters = $typeParameters
    $closedType = $genericType.MakeGenericType($typedParameters)
    if(-not $closedType)
    {
        throw "Could not make closed type $genericType"
    }
    
    ## Create the closed version of the generic type
    ,[Activator]::CreateInstance($closedType, $constructorParameters)
}

function New-SelfSignedCertificate
{
    ##############################################################################
    ##
    ## New-SelfSignedCertificate
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Generate a new self-signed certificate. The certificate generated by these
    commands allow you to sign scripts on your own computer for protection
    from tampering. Files signed with this signature are not valid on other
    computers.
     
    .EXAMPLE
     
    PS > New-SelfSignedCertificate.ps1
    Creates a new self-signed certificate
     
    #>

    
    Set-StrictMode -Version 3
    
    ## Ensure we can find makecert.exe
    if(-not (Get-Command makecert.exe -ErrorAction SilentlyContinue))
    {
        $errorMessage = "Could not find makecert.exe. " +
            "This tool is available as part of Visual Studio, or the Windows SDK."
    
        Write-Error $errorMessage
        return
    }
    
    $keyPath = Join-Path ([IO.Path]::GetTempPath()) "root.pvk"
    
    ## Generate the local certification authority
    makecert -n "CN=PowerShell Local Certificate Root" -a sha1 `
        -eku 1.3.6.1.5.5.7.3.3 -r -sv $keyPath root.cer `
        -ss Root -sr localMachine
    
    ## Use the local certification authority to generate a self-signed
    ## certificate
    makecert -pe -n "CN=PowerShell User" -ss MY -a sha1 `
        -eku 1.3.6.1.5.5.7.3.3 -iv $keyPath -ic root.cer
    
    ## Remove the private key from the filesystem.
    Remove-Item $keyPath
    
    ## Retrieve the certificate
    Get-ChildItem cert:\currentuser\my -codesign |
        Where-Object { $_.Subject -match "PowerShell User" }
}

function New-ZipFile
{
    ##############################################################################
    ##
    ## New-ZipFile
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Create a Zip file from any files piped in.
     
    .EXAMPLE
     
    PS > dir *.ps1 | New-ZipFile scripts.zip
    Copies all PS1 files in the current directory to scripts.zip
     
    .EXAMPLE
     
    PS > "readme.txt" | New-ZipFile docs.zip
    Copies readme.txt to docs.zip
     
    #>

    
    param(
        ## The name of the zip archive to create
        $Path = $(throw "Specify a zip file name"),
    
        ## Switch to delete the zip archive if it already exists.
        [Switch] $Force
    )
    
    Set-StrictMode -Version 3
    
    ## Create the Zip File
    $zipName = $executionContext.SessionState.`
        Path.GetUnresolvedProviderPathFromPSPath($Path)
    
    ## Check if the file exists already. If it does, check
    ## for -Force - generate an error if not specified.
    if(Test-Path $zipName)
    {
        if($Force)
        {
            Remove-Item $zipName -Force
        }
        else
        {
            throw "Item with specified name $zipName already exists."
        }
    }
    
    ## Add the DLL that helps with file compression
    Add-Type -Assembly System.IO.Compression.FileSystem
    
    try
    {
        ## Open the Zip archive
        $archive = [System.IO.Compression.ZipFile]::Open($zipName, "Create")
    
        ## Go through each file in the input, adding it to the Zip file
        ## specified
        foreach($file in $input)
        {
            ## Skip the current file if it is the zip file itself
            if($file.FullName -eq $zipName)
            {
                continue
            }
    
            ## Skip directories
            if($file.PSIsContainer)
            {
                continue
            }
    
            $item = $file | Get-Item
            $null = [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile(
                $archive, $item.FullName, $item.Name)
        }
    }
    finally
    {
        ## Close the file
        $archive.Dispose()
        $archive = $null
    }
}

function Read-HostWithPrompt
{
    #############################################################################
    ##
    ## Read-HostWithPrompt
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Read user input, with choices restricted to the list of options you
    provide.
     
    .EXAMPLE
     
    PS > $caption = "Please specify a task"
    PS > $message = "Specify a task to run"
    PS > $option = "&Clean Temporary Files","&Defragment Hard Drive"
    PS > $helptext = "Clean the temporary files from the computer",
    >> "Run the defragment task"
    >>
    PS > $default = 1
    PS > Read-HostWithPrompt $caption $message $option $helptext $default
     
    Please specify a task
    Specify a task to run
    [C] Clean Temporary Files [D] Defragment Hard Drive [?] Help
    (default is "D"):?
    C - Clean the temporary files from the computer
    D - Run the defragment task
    [C] Clean Temporary Files [D] Defragment Hard Drive [?] Help
    (default is "D"):C
    0
     
    #>

    
    param(
        ## The caption for the prompt
        $Caption = $null,
    
        ## The message to display in the prompt
        $Message = $null,
    
        ## Options to provide in the prompt
        [Parameter(Mandatory = $true)]
        $Option,
    
        ## Any help text to provide
        $HelpText = $null,
    
        ## The default choice
        $Default = 0
    )
    
    Set-StrictMode -Version 3
    
    ## Create the list of choices
    $choices = New-Object `
        Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
    
    ## Go through each of the options, and add them to the choice collection
    for($counter = 0; $counter -lt $option.Length; $counter++)
    {
        $choice = New-Object Management.Automation.Host.ChoiceDescription `
            $option[$counter]
    
        if($helpText -and $helpText[$counter])
        {
            $choice.HelpMessage = $helpText[$counter]
        }
    
        $choices.Add($choice)
    }
    
    ## Prompt for the choice, returning the item the user selected
    $host.UI.PromptForChoice($caption, $message, $choices, $default)
}

function Read-InputBox
{
    ##############################################################################
    ##
    ## Read-InputBox
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Read user input from a visual InputBox
     
    .EXAMPLE
     
    PS > $caption = "Please enter your name"
    PS > $name = Read-InputBox $caption
     
    #>

    
    param(
        ## The title of the dialog when displayed
        [string] $Title = "Input Dialog"
    )
    
    Set-StrictMode -Version 3
    
    ## Load the Windows Forms assembly
    Add-Type -Assembly System.Windows.Forms
    
    ## Create the main form
    $form = New-Object Windows.Forms.Form
    $form.Size = New-Object Drawing.Size @(400,100)
    $form.FormBorderStyle = "FixedToolWindow"
    
    ## Create the listbox to hold the items from the pipeline
    $textbox = New-Object Windows.Forms.TextBox
    $textbox.Top = 5
    $textbox.Left = 5
    $textBox.Width = 380
    $textbox.Anchor = "Left","Right"
    $form.Text = $Title
    
    ## Create the button panel to hold the OK and Cancel buttons
    $buttonPanel = New-Object Windows.Forms.Panel
    $buttonPanel.Size = New-Object Drawing.Size @(400,40)
    $buttonPanel.Dock = "Bottom"
    
    ## Create the Cancel button, which will anchor to the bottom right
    $cancelButton = New-Object Windows.Forms.Button
    $cancelButton.Text = "Cancel"
    $cancelButton.DialogResult = "Cancel"
    $cancelButton.Top = $buttonPanel.Height - $cancelButton.Height - 10
    $cancelButton.Left = $buttonPanel.Width - $cancelButton.Width - 10
    $cancelButton.Anchor = "Right"
    
    ## Create the OK button, which will anchor to the left of Cancel
    $okButton = New-Object Windows.Forms.Button
    $okButton.Text = "Ok"
    $okButton.DialogResult = "Ok"
    $okButton.Top = $cancelButton.Top
    $okButton.Left = $cancelButton.Left - $okButton.Width - 5
    $okButton.Anchor = "Right"
    
    ## Add the buttons to the button panel
    $buttonPanel.Controls.Add($okButton)
    $buttonPanel.Controls.Add($cancelButton)
    
    ## Add the button panel and list box to the form, and also set
    ## the actions for the buttons
    $form.Controls.Add($buttonPanel)
    $form.Controls.Add($textbox)
    $form.AcceptButton = $okButton
    $form.CancelButton = $cancelButton
    $form.Add_Shown( { $form.Activate(); $textbox.Focus() } )
    
    ## Show the form, and wait for the response
    $result = $form.ShowDialog()
    
    ## If they pressed OK (or Enter,) go through all the
    ## checked items and send the corresponding object down the pipeline
    if($result -eq "OK")
    {
        $textbox.Text
    }
}

function Register-TemporaryEvent
{
    ##############################################################################
    ##
    ## Register-TemporaryEvent
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Registers an event action for an object, and automatically unregisters
    itself afterward. In PowerShell version three, use the -MaxTriggerCount
    parameter of the Register-*Event cmdlets.
     
    .EXAMPLE
     
    PS > $timer = New-Object Timers.Timer
    PS > Register-TemporaryEvent $timer Disposed { [Console]::Beep(100,100) }
    PS > $timer.Dispose()
    PS > Get-EventSubscriber
    PS > Get-Job
     
    #>

    
    param(
        ## The object that generates the event
        $Object,
    
        ## The event to subscribe to
        $Event,
    
        ## The action to invoke when the event arrives
        [ScriptBlock] $Action
    )
    
    Set-StrictMode -Version 2
    
    $actionText = $action.ToString()
    $actionText += @'
     
    $eventSubscriber | Unregister-Event
    $eventSubscriber.Action | Remove-Job
'@

    
    $eventAction = [ScriptBlock]::Create($actionText)
    $null = Register-ObjectEvent $object $event -Action $eventAction
}

function Resolve-Error
{
    #############################################################################
    ##
    ## Resolve-Error
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Displays detailed information about an error and its context.
     
    #>

    
    param(
        ## The error to resolve
        $ErrorRecord = ($error[0])
    )
    
    Set-StrictMode -Off
    
    ""
    "If this is an error in a script you wrote, use the Set-PsBreakpoint cmdlet"
    "to diagnose it."
    ""
    
    'Error details ($error[0] | Format-List * -Force)'
    "-"*80
    $errorRecord | Format-List * -Force
    
    'Information about the command that caused this error ' +
        '($error[0].InvocationInfo | Format-List *)'
    "-"*80
    $errorRecord.InvocationInfo | Format-List *
    
    'Information about the error''s target ' +
        '($error[0].TargetObject | Format-List *)'
    "-"*80
    $errorRecord.TargetObject | Format-List *
    
    'Exception details ($error[0].Exception | Format-List * -Force)'
    "-"*80
    
    $exception = $errorRecord.Exception
    
    for ($i = 0; $exception; $i++, ($exception = $exception.InnerException))
    {
        "$i" * 80
        $exception | Format-List * -Force
    }
}

function Search-Bing
{
    ##############################################################################
    ##
    ## Search-Bing
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Search Bing for a given term
     
    .EXAMPLE
     
    PS > Search-Bing PowerShell
    Searches Bing for the term "PowerShell"
     
    #>

    
    param(
        ## The term to search for
        $Pattern = "PowerShell"
    )
    
    Set-StrictMode -Version 3
    
    ## Create the URL that contains the Twitter search results
    Add-Type -Assembly System.Web
    $queryUrl = 'http://www.bing.com/search?q={0}'
    $queryUrl = $queryUrl -f ([System.Web.HttpUtility]::UrlEncode($pattern))
    
    ## Download the web page
    $results = [string] (Invoke-WebRequest $queryUrl)
    
    ## Extract the text of the results, which are contained in
    ## segments that look like "<div class="sb_tlst">...</div>"
    $matches = $results |
        Select-String -Pattern '(?s)<div[^>]*sb_tlst[^>]*>.*?</div>' -AllMatches
    
    foreach($match in $matches.Matches)
    {
        ## Extract the URL, keeping only the text inside the quotes
        ## of the HREF
        $url = $match.Value -replace '.*href="(.*?)".*','$1'
        $url = [System.Web.HttpUtility]::UrlDecode($url)
    
        ## Extract the page name, replace anything in angle
        ## brackets with an empty string.
        $item = $match.Value -replace '<[^>]*>', ''
    
        ## Output the item
        [PSCustomObject] @{ Item = $item; Url = $url }
    }
}

function Search-CertificateStore
{
    ##############################################################################
    ##
    ## Search-CertificateStore
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Search the certificate provider for certificates that match the specified
    Enhanced Key Usage (EKU.)
     
    .EXAMPLE
     
    PS > Search-CertificateStore "Encrypting File System"
    Searches the certificate store for Encrypting File System certificates
     
    #>

    
    param(
        ## The friendly name of an Enhanced Key Usage
        ## (such as 'Code Signing')
        [Parameter(Mandatory = $true)]
        $EkuName
    )
    
    Set-StrictMode -Off
    
    ## Go through every certificate in the current user's "My" store
    foreach($cert in Get-ChildItem cert:\CurrentUser\My)
    {
        ## For each of those, go through its extensions
        foreach($extension in $cert.Extensions)
        {
            ## For each extension, go through its Enhanced Key Usages
            foreach($certEku in $extension.EnhancedKeyUsages)
            {
                ## If the friendly name matches, output that certificate
                if($certEku.FriendlyName -eq $ekuName)
                {
                    $cert
                }
            }
        }
    }
}

function Search-Help
{
    ##############################################################################
    ##
    ## Search-Help
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Search the PowerShell help documentation for a given keyword or regular
    expression. For simple keyword searches in PowerShell version two or three,
    simply use "Get-Help <keyword>"
     
    .EXAMPLE
     
    PS > Search-Help hashtable
    Searches help for the term 'hashtable'
     
    .EXAMPLE
     
    PS > Search-Help "(datetime|ticks)"
    Searches help for the term datetime or ticks, using the regular expression
    syntax.
     
    #>

    
    param(
        ## The pattern to search for
        [Parameter(Mandatory = $true)]
        $Pattern
    )
    
    $helpNames = $(Get-Help * | Where-Object { $_.Category -ne "Alias" })
    
    ## Go through all of the help topics
    foreach($helpTopic in $helpNames)
    {
        ## Get their text content, and
        $content = Get-Help -Full $helpTopic.Name | Out-String
        if($content -match "(.{0,30}$pattern.{0,30})")
        {
            $helpTopic | Add-Member NoteProperty Match $matches[0].Trim()
            $helpTopic | Select-Object Name,Match
        }
    }
}

function Search-Registry
{
    ##############################################################################
    ##
    ## Search-Registry
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Search the registry for keys or properties that match a specific value.
     
    .EXAMPLE
     
    PS > Set-Location HKCU:\Software\Microsoft\
    PS > Search-Registry Run
     
    #>

    
    param(
        ## The text to search for
        [Parameter(Mandatory = $true)]
        [string] $Pattern
    )
    
    Set-StrictMode -Off
    
    ## Helper function to create a new object that represents
    ## a registry match from this script
    function New-RegistryMatch
    {
        param( $matchType, $keyName, $propertyName, $line )
    
        $registryMatch = New-Object PsObject -Property @{
            MatchType = $matchType;
            KeyName = $keyName;
            PropertyName = $propertyName;
            Line = $line
        }
    
        $registryMatch
    }
    
    ## Go through each item in the registry
    foreach($item in Get-ChildItem -Recurse -ErrorAction SilentlyContinue)
    {
        ## Check if the key name matches
        if($item.Name -match $pattern)
        {
            New-RegistryMatch "Key" $item.Name $null $item.Name
        }
    
        ## Check if a key property matches
        foreach($property in (Get-ItemProperty $item.PsPath).PsObject.Properties)
        {
            ## Skip the property if it was one PowerShell added
            if(($property.Name -eq "PSPath") -or
                ($property.Name -eq "PSChildName"))
            {
                continue
            }
    
            ## Search the text of the property
            $propertyText = "$($property.Name)=$($property.Value)"
            if($propertyText -match $pattern)
            {
                New-RegistryMatch "Property" $item.Name `
                    property.Name $propertyText
            }
        }
    }
}

function Search-StackOverflow
{
    ##############################################################################
    ##
    ## Search-StackOverflow
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Searches Stack Overflow for PowerShell questions that relate to your
    search term, and provides the link to the accepted answer.
     
     
    .EXAMPLE
     
    PS > Search-StackOverflow upload ftp
    Searches StackOverflow for questions about how to upload FTP files
     
    .EXAMPLE
     
    PS > $answers = Search-StackOverflow.ps1 upload ftp
    PS > $answers | Out-GridView -PassThru | Foreach-Object { start $_ }
     
    Launches Out-GridView with the answers from a search. Select the URLs
    that you want to launch, and then press OK. PowerShell then launches
    your default web brower for those URLs.
     
    #>

    
    Set-StrictMode -Off
    Add-Type -Assembly System.Web
    
    $query = ($args | Foreach-Object { '"' + $_ + '"' }) -join " "
    $query = [System.Web.HttpUtility]::UrlEncode($query)
    
    ## Use the StackOverflow API to retrieve the answer for a question
    $url = "https://api.stackexchange.com/2.0/search?order=desc&sort=relevance" +
        "&pagesize=5&tagged=powershell&intitle=$query&site=stackoverflow"
    $question = Invoke-RestMethod $url
    
    ## Now go through and show the questions and answers
    $question.Items | Where accepted_answer_id | Foreach-Object {
            "Question: " + $_.Title
            "http://www.stackoverflow.com/questions/$($_.accepted_answer_id)"
            ""
    }
}

function Search-StartMenu
{
    ##############################################################################
    ##
    ## Search-StartMenu
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/blog)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Search the Start Menu for items that match the provided text. This script
    searches both the name (as displayed on the Start Menu itself,) and the
    destination of the link.
     
    .EXAMPLE
     
    PS > Search-StartMenu "Character Map" | Invoke-Item
    Searches for the "Character Map" appication, and then runs it
     
    PS > Search-StartMenu PowerShell | Select-FilteredObject | Invoke-Item
    Searches for anything with "PowerShell" in the application name, lets you
    pick which one to launch, and then launches it.
     
    #>

    
    param(
        ## The pattern to match
        [Parameter(Mandatory = $true)]
        $Pattern
    )
    
    Set-StrictMode -Version 3
    
    ## Get the locations of the start menu paths
    $myStartMenu = [Environment]::GetFolderPath("StartMenu")
    $shell = New-Object -Com WScript.Shell
    $allStartMenu = $shell.SpecialFolders.Item("AllUsersStartMenu")
    
    ## Escape their search term, so that any regular expression
    ## characters don't affect the search
    $escapedMatch = [Regex]::Escape($pattern)
    
    ## Search in "my start menu" for text in the link name or link destination
    dir $myStartMenu *.lnk -rec | Where-Object {
        ($_.Name -match "$escapedMatch") -or
        ($_ | Select-String "\\[^\\]*$escapedMatch\." -Quiet)
    }
    
    ## Search in "all start menu" for text in the link name or link destination
    dir $allStartMenu *.lnk -rec | Where-Object {
        ($_.Name -match "$escapedMatch") -or
        ($_ | Select-String "\\[^\\]*$escapedMatch\." -Quiet)
    }
}

function Search-WmiNamespace
{
    ##############################################################################
    ##
    ## Search-WmiNamespace
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Search the WMI classes installed on the system for the provided match text.
     
    .EXAMPLE
     
    PS > Search-WmiNamespace Registry
    Searches WMI for any classes or descriptions that mention "Registry"
     
    .EXAMPLE
     
    Search-WmiNamespace Process ClassName,PropertyName
    Searchs WMI for any classes or properties that mention "Process"
     
    .EXAMPLE
     
    Search-WmiNamespace CPU -Detailed
    Searches WMI for any class names, descriptions, or properties that mention
    "CPU"
     
    #>

    
    param(
        ## The pattern to search for
        [Parameter(Mandatory = $true)]
        [string] $Pattern,
    
        ## Switch parameter to look for class names, descriptions, or properties
        [switch] $Detailed,
    
        ## Switch parameter to look for class names, descriptions, properties, and
        ## property description.
        [switch] $Full,
    
        ## Custom match options.
        ## Supports any or all of the following match options:
        ## ClassName, ClassDescription, PropertyName, PropertyDescription
        [string[]] $MatchOptions = ("ClassName","ClassDescription")
    )
    
    Set-StrictMode -Off
    
    ## Helper function to create a new object that represents
    ## a Wmi match from this script
    function New-WmiMatch
    {
        param( $matchType, $className, $propertyName, $line )
    
        $wmiMatch = New-Object PsObject -Property @{
            MatchType = $matchType;
            ClassName = $className;
            PropertyName = $propertyName;
            Line = $line
        }
    
        $wmiMatch
    }
    
    ## If they've specified the -detailed or -full options, update
    ## the match options to provide them an appropriate amount of detail
    if($detailed)
    {
        $matchOptions = "ClassName","ClassDescription","PropertyName"
    }
    
    if($full)
    {
        $matchOptions =
            "ClassName","ClassDescription","PropertyName","PropertyDescription"
    }
    
    ## Verify that they specified only valid match options
    foreach($matchOption in $matchOptions)
    {
        $fullMatchOptions =
            "ClassName","ClassDescription","PropertyName","PropertyDescription"
    
        if($fullMatchOptions -notcontains $matchOption)
        {
            $error = "Cannot convert value {0} to a match option. " +
                "Specify one of the following values and try again. " +
                "The possible values are ""{1}""."
            $ofs = ", "
            throw ($error -f $matchOption, ([string] $fullMatchOptions))
        }
    }
    
    ## Go through all of the available classes on the computer
    foreach($class in Get-WmiObject -List -Rec)
    {
        ## Provide explicit get options, so that we get back descriptios
        ## as well
        $managementOptions = New-Object System.Management.ObjectGetOptions
        $managementOptions.UseAmendedQualifiers = $true
        $managementClass =
            New-Object Management.ManagementClass $class.Name,$managementOptions
    
        ## If they want us to match on class names, check if their text
        ## matches the class name
        if($matchOptions -contains "ClassName")
        {
            if($managementClass.Name -match $pattern)
            {
                New-WmiMatch "ClassName" `
                    $managementClass.Name $null $managementClass.__PATH
            }
        }
    
        ## If they want us to match on class descriptions, check if their text
        ## matches the class description
        if($matchOptions -contains "ClassDescription")
        {
            $description =
                $managementClass.Qualifiers |
                    foreach { if($_.Name -eq "Description") { $_.Value } }
            if($description -match $pattern)
            {
                New-WmiMatch "ClassDescription" `
                    $managementClass.Name $null $description
            }
        }
    
        ## Go through the properties of the class
        foreach($property in $managementClass.Properties)
        {
            ## If they want us to match on property names, check if their text
            ## matches the property name
            if($matchOptions -contains "PropertyName")
            {
                if($property.Name -match $pattern)
                {
                    New-WmiMatch "PropertyName" `
                        $managementClass.Name $property.Name $property.Name
                }
            }
    
            ## If they want us to match on property descriptions, check if
            ## their text matches the property name
            if($matchOptions -contains "PropertyDescription")
            {
                $propertyDescription =
                    $property.Qualifiers |
                        foreach { if($_.Name -eq "Description") { $_.Value } }
                if($propertyDescription -match $pattern)
                {
                    New-WmiMatch "PropertyDescription" `
                        $managementClass.Name $property.Name $propertyDescription
                }
            }
        }
    }
}

function Select-FilteredObject
{
    ##############################################################################
    ##
    ## Select-FilteredObject
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Provides an inteactive window to help you select complex sets of objects.
    To do this, it takes all the input from the pipeline, and presents it in a
    notepad window. Keep any lines that represent objects you want to retain,
    delete the rest, then save the file and exit notepad.
     
    The script then passes the original objects that you kept along the
    pipeline.
     
    .EXAMPLE
     
    PS > Get-Process | Select-FilteredObject | Stop-Process -WhatIf
    Gets all of the processes running on the system, and displays them to you.
    After you've selected the ones you want to stop, it pipes those into the
    Stop-Process cmdlet.
     
    #>

    
    ## PowerShell runs your "begin" script block before it passes you any of the
    ## items in the pipeline.
    begin
    {
        Set-StrictMode -Version 3
    
        ## Create a temporary file
        $filename = [System.IO.Path]::GetTempFileName()
    
        ## Define a header in a "here-string" that explains how to interact with
        ## the file
        $header = @"
    ############################################################
    ## Keep any lines that represent obects you want to retain,
    ## and delete the rest.
    ##
    ## Once you finish selecting objects, save this file and
    ## exit.
    ############################################################
     
"@

    
        ## Place the instructions into the file
        $header > $filename
    
        ## Initialize the variables that will hold our list of objects, and
        ## a counter to help us keep track of the objects coming down the
        ## pipeline
        $objectList = @()
        $counter = 0
    }
    
    ## PowerShell runs your "process" script block for each item it passes down
    ## the pipeline. In this block, the "$_" variable represents the current
    ## pipeline object
    process
    {
        ## Add a line to the file, using PowerShell's format (-f) operator.
        ## When provided the ouput of Get-Process, for example, these lines look
        ## like:
        ## 30: System.Diagnostics.Process (powershell)
        "{0}: {1}" -f $counter,$_.ToString() >> $filename
    
        ## Add the object to the list of objects, and increment our counter.
        $objectList += $_
        $counter++
    }
    
    ## PowerShell runs your "end" script block once it completes passing all
    ## objects down the pipeline.
    end
    {
        ## Start notepad, then call the process's WaitForExit() method to
        ## pause the script until the user exits notepad.
        $process = Start-Process Notepad -Args $filename -PassThru
        $process.WaitForExit()
    
        ## Go over each line of the file
        foreach($line in (Get-Content $filename))
        {
            ## Check if the line is of the special format: numbers, followed by
            ## a colon, followed by extra text.
            if($line -match "^(\d+?):.*")
            {
                ## If it did match the format, then $matches[1] represents the
                ## number -- a counter into the list of objects we saved during
                ## the "process" section.
                ## So, we output that object from our list of saved objects.
                $objectList[$matches[1]]
            }
        }
    
        ## Finally, clean up the temporary file.
        Remove-Item $filename
    }
}

function Select-GraphicalFilteredObject
{
    ##############################################################################
    ##
    ## Select-GraphicalFilteredObject
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Display a Windows Form to help the user select a list of items piped in.
    Any selected items get passed along the pipeline.
     
    .EXAMPLE
     
    PS > dir | Select-GraphicalFilteredObject
     
      Directory: C:\
     
    Mode LastWriteTime Length Name
    ---- ------------- ------ ----
    d---- 10/7/2006 4:30 PM Documents and Settings
    d---- 3/18/2007 7:56 PM Windows
     
    #>

    
    Set-StrictMode -Version 2
    
    $objectArray = @($input)
    
    ## Ensure that they've piped information into the script
    if($objectArray.Count -eq 0)
    {
        Write-Error "This script requires pipeline input."
        return
    }
    
    ## Load the Windows Forms assembly
    Add-Type -Assembly System.Windows.Forms
    
    ## Create the main form
    $form = New-Object Windows.Forms.Form
    $form.Size = New-Object Drawing.Size @(600,600)
    
    ## Create the listbox to hold the items from the pipeline
    $listbox = New-Object Windows.Forms.CheckedListBox
    $listbox.CheckOnClick = $true
    $listbox.Dock = "Fill"
    $form.Text = "Select the list of objects you wish to pass down the pipeline"
    $listBox.Items.AddRange($objectArray)
    
    ## Create the button panel to hold the OK and Cancel buttons
    $buttonPanel = New-Object Windows.Forms.Panel
    $buttonPanel.Size = New-Object Drawing.Size @(600,30)
    $buttonPanel.Dock = "Bottom"
    
    ## Create the Cancel button, which will anchor to the bottom right
    $cancelButton = New-Object Windows.Forms.Button
    $cancelButton.Text = "Cancel"
    $cancelButton.DialogResult = "Cancel"
    $cancelButton.Top = $buttonPanel.Height - $cancelButton.Height - 5
    $cancelButton.Left = $buttonPanel.Width - $cancelButton.Width - 10
    $cancelButton.Anchor = "Right"
    
    ## Create the OK button, which will anchor to the left of Cancel
    $okButton = New-Object Windows.Forms.Button
    $okButton.Text = "Ok"
    $okButton.DialogResult = "Ok"
    $okButton.Top = $cancelButton.Top
    $okButton.Left = $cancelButton.Left - $okButton.Width - 5
    $okButton.Anchor = "Right"
    
    ## Add the buttons to the button panel
    $buttonPanel.Controls.Add($okButton)
    $buttonPanel.Controls.Add($cancelButton)
    
    ## Add the button panel and list box to the form, and also set
    ## the actions for the buttons
    $form.Controls.Add($listBox)
    $form.Controls.Add($buttonPanel)
    $form.AcceptButton = $okButton
    $form.CancelButton = $cancelButton
    $form.Add_Shown( { $form.Activate() } )
    
    ## Show the form, and wait for the response
    $result = $form.ShowDialog()
    
    ## If they pressed OK (or Enter,) go through all the
    ## checked items and send the corresponding object down the pipeline
    if($result -eq "OK")
    {
        foreach($index in $listBox.CheckedIndices)
        {
            $objectArray[$index]
        }
    }
}

function Select-TextOutput
{
    ##############################################################################
    ##
    ## Select-TextOutput
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Searches the textual output of a command for a pattern.
     
    .EXAMPLE
     
    PS > Get-Service | Select-TextOutput audio
    Finds all references to "Audio" in the output of Get-Service
     
    #>

    
    param(
        ## The pattern to search for
        $Pattern
    )
    
    Set-StrictMode -Version 3
    $input | Out-String -Stream | Select-String $pattern
}

function Send-File
{
    ##############################################################################
    ##
    ## Send-File
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Sends a file to a remote session.
     
    .EXAMPLE
     
    PS > $session = New-PsSession leeholmes1c23
    PS > Send-File c:\temp\test.exe c:\temp\test.exe $session
     
    #>

    
    param(
        ## The path on the local computer
        [Parameter(Mandatory = $true)]
        $Source,
    
        ## The target path on the remote computer
        [Parameter(Mandatory = $true)]
        $Destination,
    
        ## The session that represents the remote computer
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.Runspaces.PSSession] $Session
    )
    
    Set-StrictMode -Version 3
    
    $remoteScript = {
        param($destination, $bytes)
    
        ## Convert the destination path to a full filesystem path (to support
        ## relative paths)
        $Destination = $executionContext.SessionState.`
            Path.GetUnresolvedProviderPathFromPSPath($Destination)
    
        ## Write the content to the new file
        $file = [IO.File]::Open($Destination, "OpenOrCreate")
        $null = $file.Seek(0, "End")
        $null = $file.Write($bytes, 0, $bytes.Length)
        $file.Close()
    }
    
    ## Get the source file, and then start reading its content
    $sourceFile = Get-Item $source
    
    ## Delete the previously-existing file if it exists
    Invoke-Command -Session $session {
        if(Test-Path $args[0]) { Remove-Item $args[0] }
    } -ArgumentList $Destination
    
    ## Now break it into chunks to stream
    Write-Progress -Activity "Sending $Source" -Status "Preparing file"
    
    $streamSize = 1MB
    $position = 0
    $rawBytes = New-Object byte[] $streamSize
    $file = [IO.File]::OpenRead($sourceFile.FullName)
    
    while(($read = $file.Read($rawBytes, 0, $streamSize)) -gt 0)
    {
        Write-Progress -Activity "Writing $Destination" `
            -Status "Sending file" `
            -PercentComplete ($position / $sourceFile.Length * 100)
    
        ## Ensure that our array is the same size as what we read
        ## from disk
        if($read -ne $rawBytes.Length)
        {
            [Array]::Resize( [ref] $rawBytes, $read)
        }
    
        ## And send that array to the remote system
        Invoke-Command -Session $session $remoteScript `
            -ArgumentList $destination,$rawBytes
    
        ## Ensure that our array is the same size as what we read
        ## from disk
        if($rawBytes.Length -ne $streamSize)
        {
            [Array]::Resize( [ref] $rawBytes, $streamSize)
        }
        
        [GC]::Collect()
        $position += $read
    }
    
    $file.Close()
    
    ## Show the result
    Invoke-Command -Session $session { Get-Item $args[0] } -ArgumentList $Destination
}

function Send-MailMessage
{
    ##############################################################################
    ##
    ## Send-MailMessage
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ## Illustrate the techniques used to send an email in PowerShell.
    ## In PowerShell version two, use the Send-MailMessage cmdlet.
    ##
    ## Example:
    ##
    ## PS > $body = @"
    ## >> Hi from another satisfied customer of The PowerShell Cookbook!
## >> "@
    ## >>
    ## PS > $to = "guide_feedback@leeholmes.com"
    ## PS > $subject = "Thanks for all of the scripts."
    ## PS > $mailHost = "mail.leeholmes.com"
    ## PS > Send-MailMessage $to $subject $body $mailHost
    ##
    ##############################################################################
    
    param(
        ## The recipient of the mail message
        [string[]] $To = $(throw "Please specify the destination mail address"),
    
        ## The subjecty of the message
        [string] $Subject = "<No Subject>",
    
        ## The body of the message
        [string] $Body = $(throw "Please specify the message content"),
    
        ## The SMTP host that will transmit the message
        [string] $SmtpHost = $(throw "Please specify a mail server."),
    
        ## The sender of the message
        [string] $From = "$($env:UserName)@example.com",
    
        ## Any credentials to supply
        $Credential,
    
        ## Determine whether an SSL connection should be used
        [Switch] $UseSSL
    )
    
    ## Create the mail message
    $email = New-Object System.Net.Mail.MailMessage
    
    ## Populate its fields
    foreach($mailTo in $to)
    {
        $email.To.Add($mailTo)
    }
    
    $email.From = $from
    $email.Subject = $subject
    $email.Body = $body
    
    ## Send the mail
    $client = New-Object System.Net.Mail.SmtpClient $smtpHost
    
    if(-not $Credential)
    {
        $client.UseDefaultCredentials = $true
    }
    else
    {
        $actualCred = Get-Credential $Credential
        $networkCred = New-Object System.Net.NetworkCredential
        $networkCred.Username = $actualCred.Username
    
        $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($actualCred.Password)
        $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
        [System.Runtime.InteropServices.Marshal]::ZeroFreeBstr($bstr)
    
        $networkCred.Password = $password
        $client.Credentials = $networkCred
    }
    
    if($UseSSL)
    {
        $client.EnableSSL = $true
    }
    
    $client.Send($email)
}

function Send-TcpRequest
{
    ##############################################################################
    ##
    ## Send-TcpRequest
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Send a TCP request to a remote computer, and return the response.
    If you do not supply input to this script (via either the pipeline, or the
    -InputObject parameter,) the script operates in interactive mode.
     
    .EXAMPLE
     
    PS > $http = @"
      GET / HTTP/1.1
      Host:bing.com
      `n`n
"@
     
    $http | Send-TcpRequest bing.com 80
     
    #>

    
    [CmdletBinding()]
    param(
        ## The computer to connect to
        [string] $ComputerName = "localhost",
    
        ## A switch to determine if you just want to test the connection
        [switch] $Test,
    
        ## The port to use
        [int] $Port = 80,
    
        ## A switch to determine if the connection should be made using SSL
        [switch] $UseSSL,
    
        ## The input string to send to the remote host
        [string] $InputObject,
    
        ## The delay, in milliseconds, to wait between commands
        [int] $Delay = 100
    )
    
    Set-StrictMode -Version 3
    
    [string] $SCRIPT:output = ""
    
    ## Store the input into an array that we can scan over. If there was no input,
    ## then we will be in interactive mode.
    $currentInput = $inputObject
    if(-not $currentInput)
    {
        $currentInput = @($input)
    }
    $scriptedMode = ([bool] $currentInput) -or $test
    
    function Main
    {
        ## Open the socket, and connect to the computer on the specified port
        if(-not $scriptedMode)
        {
            write-host "Connecting to $computerName on port $port"
        }
    
        try
        {
            $tcpClient = New-Object Net.Sockets.TcpClient($computerName, $port)
        }
        catch
        {
            if($test) { $false }
            else { Write-Error "Could not connect to remote computer: $_" }
    
            return
        }
    
        ## If we're just testing the connection, we've made the connection
        ## successfully, so just return $true
        if($test) { $true; return }
    
        ## If this is interactive mode, supply the prompt
        if(-not $scriptedMode)
        {
            write-host "Connected. Press ^D followed by [ENTER] to exit.`n"
        }
    
        $stream = $tcpClient.GetStream()
    
        ## If we wanted to use SSL, set up that portion of the connection
        if($UseSSL)
        {
            try
            {
                $sslStream = New-Object System.Net.Security.SslStream $stream,$false
                $sslStream.AuthenticateAsClient($ComputerName)
                $stream = $sslStream
            }
            catch [System.IO.IOException]
            {
                ## Try again with explicit SSL (TLS)
            
                $tcpClient = new-object System.Net.Sockets.TcpClient($ComputerName, $port)
                $stream = $tcpClient.GetStream()
                
                $writer = new-object System.IO.StreamWriter $stream
                
                $writer.WriteLine("EHLO")
                $writer.Flush()
                
                $writer.WriteLine("STARTTLS")
                $writer.Flush()
                $null = GetOutput
                
                $sslStream = New-Object System.Net.Security.SslStream $stream,$false
                $sslStream.AuthenticateAsClient($ComputerName)
                $stream = $sslStream            
            }
        }
    
        $writer = new-object System.IO.StreamWriter $stream
    
        while($true)
        {
            ## Receive the output that has buffered so far
            $SCRIPT:output += GetOutput
    
            ## If we're in scripted mode, send the commands,
            ## receive the output, and exit.
            if($scriptedMode)
            {
                foreach($line in $currentInput)
                {
                    $writer.WriteLine($line)
                    $writer.Flush()
                    Start-Sleep -m $Delay
                    $SCRIPT:output += GetOutput
                }
    
                break
            }
            ## If we're in interactive mode, write the buffered
            ## output, and respond to input.
            else
            {
                if($output)
                {
                    foreach($line in $output.Split("`n"))
                    {
                        write-host $line
                    }
                    $SCRIPT:output = ""
                }
    
                ## Read the user's command, quitting if they hit ^D
                $command = read-host
                if($command -eq ([char] 4)) { break; }
    
                ## Otherwise, Write their command to the remote host
                $writer.WriteLine($command)
                $writer.Flush()
            }
        }
    
        ## Close the streams
        $writer.Close()
        $stream.Close()
    
        ## If we're in scripted mode, return the output
        if($scriptedMode)
        {
            $output
        }
    }
    
    ## Read output from a remote host
    function GetOutput
    {
        ## Create a buffer to receive the response
        $buffer = new-object System.Byte[] 1024
        $encoding = new-object System.Text.AsciiEncoding
    
        $outputBuffer = ""
        $foundMore = $false
    
        ## Read all the data available from the stream, writing it to the
        ## output buffer when done.
        do
        {
            ## Allow data to buffer for a bit
            start-sleep -m 1000
    
            ## Read what data is available
            $foundmore = $false
            $stream.ReadTimeout = 1000
    
            do
            {
                try
                {
                    $read = $stream.Read($buffer, 0, 1024)
    
                    if($read -gt 0)
                    {
                        $foundmore = $true
                        $outputBuffer += ($encoding.GetString($buffer, 0, $read))
                    }
                } catch { $foundMore = $false; $read = 0 }
            } while($read -gt 0)
        } while($foundmore)
    
        $outputBuffer
    }
    
    . Main
}

function Set-Clipboard
{
    #############################################################################
    ##
    ## Set-Clipboard
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Sends the given input to the Windows clipboard.
     
    .EXAMPLE
     
    PS > dir | Set-Clipboard
    This example sends the view of a directory listing to the clipboard
     
    .EXAMPLE
     
    PS > Set-Clipboard "Hello World"
    This example sets the clipboard to the string, "Hello World".
     
    #>

    
    param(
        ## The input to send to the clipboard
        [Parameter(ValueFromPipeline = $true)]
        [object[]] $InputObject
    )
    
    begin
    {
        Set-StrictMode -Version 3
        $objectsToProcess = @()
    }
    
    process
    {
        ## Collect everything sent to the script either through
        ## pipeline input, or direct input.
        $objectsToProcess += $inputObject
    }
    
    end
    {
        ## Convert the input objects to text
        $clipText = ($objectsToProcess | Out-String -Stream) -join "`r`n"
    
        ## And finally set the clipboard text
        Add-Type -Assembly PresentationCore
        [Windows.Clipboard]::SetText($clipText)
    }
}

function Set-ConsoleProperties
{
    ##############################################################################
    ##
    ## Set-ConsoleProperties
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Configures PowerShell windows launched through Start | Run to have the same
    appearance as those launched through the Start Menu. On Windows 8, this is not
    required.
     
    #>

    
    Set-StrictMode -Version 3
    
    Push-Location
    Set-Location HKCU:\Console
    $exePath = (Get-Process -id $pid).Path -replace "\\","_"
    New-Item ".\$exePath"
    Set-Location ".\$exePath"
    
    New-ItemProperty . ColorTable00 -type DWORD -value 0x00562401
    New-ItemProperty . ColorTable07 -type DWORD -value 0x00f0edee
    New-ItemProperty . FaceName -type STRING -value "Lucida Console"
    New-ItemProperty . FontFamily -type DWORD -value 0x00000036
    New-ItemProperty . FontSize -type DWORD -value 0x000c0000
    New-ItemProperty . FontWeight -type DWORD -value 0x00000190
    New-ItemProperty . HistoryNoDup -type DWORD -value 0x00000000
    New-ItemProperty . QuickEdit -type DWORD -value 0x00000001
    New-ItemProperty . ScreenBufferSize -type DWORD -value 0x0bb80078
    New-ItemProperty . WindowSize -type DWORD -value 0x00320078
    Pop-Location
}

function Set-PsBreakPointLastError
{
    Set-StrictMode -Version 3
    
    $lastError = $error[0]
    Set-PsBreakpoint $lastError.InvocationInfo.ScriptName `
        $lastError.InvocationInfo.ScriptLineNumber
}

function Set-RemoteRegistryKeyProperty
{
    ##############################################################################
    ##
    ## Set-RemoteRegistryKeyProperty
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Set the value of a remote registry key property
     
    .EXAMPLE
     
    PS >$registryPath =
        "HKLM:\software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
    PS >Set-RemoteRegistryKeyProperty LEE-DESK $registryPath `
          "ExecutionPolicy" "RemoteSigned"
     
    #>

    
    param(
        ## The computer to connect to
        [Parameter(Mandatory = $true)]
        $ComputerName,
    
        ## The registry path to modify
        [Parameter(Mandatory = $true)]
        $Path,
    
        ## The property to modify
        [Parameter(Mandatory = $true)]
        $PropertyName,
    
        ## The value to set on the property
        [Parameter(Mandatory = $true)]
        $PropertyValue
    )
    
    Set-StrictMode -Version 3
    
    ## Validate and extract out the registry key
    if($path -match "^HKLM:\\(.*)")
    {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(
            "LocalMachine", $computername)
    }
    elseif($path -match "^HKCU:\\(.*)")
    {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(
            "CurrentUser", $computername)
    }
    else
    {
        Write-Error ("Please specify a fully-qualified registry path " +
            "(i.e.: HKLM:\Software) of the registry key to open.")
        return
    }
    
    ## Open the key and set its value
    $key = $baseKey.OpenSubKey($matches[1], $true)
    $key.SetValue($propertyName, $propertyValue)
    
    ## Close the key and base keys
    $key.Close()
    $baseKey.Close()
}

function Show-ColorizedContent
{
    ##############################################################################
    ##
    ## Show-ColorizedContent
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Displays syntax highlighting, line numbering, and range highlighting for
    PowerShell scripts.
     
    .EXAMPLE
     
    PS > Show-ColorizedContent Invoke-MyScript.ps1
     
    001 | function Write-Greeting
    002 | {
    003 | param($greeting)
    004 | Write-Host "$greeting World"
    005 | }
    006 |
    007 | Write-Greeting "Hello"
     
    .EXAMPLE
     
    PS > Show-ColorizedContent Invoke-MyScript.ps1 -highlightRange (1..3+7)
     
    001 > function Write-Greeting
    002 > {
    003 > param($greeting)
    004 | Write-Host "$greeting World"
    005 | }
    006 |
    007 > Write-Greeting "Hello"
     
    #>

    
    param(
        ## The path to colorize
        [Parameter(Mandatory = $true)]
        $Path,
    
        ## The range of lines to highlight
        $HighlightRange = @(),
    
        ## Switch to exclude line numbers
        [Switch] $ExcludeLineNumbers
    )
    
    Set-StrictMode -Version 3
    
    ## Colors to use for the different script tokens.
    ## To pick your own colors:
    ## [Enum]::GetValues($host.UI.RawUI.ForegroundColor.GetType()) |
    ## Foreach-Object { Write-Host -Fore $_ "$_" }
    $replacementColours = @{
        'Attribute' = 'DarkCyan'
        'Command' = 'Blue'
        'CommandArgument' = 'Magenta'
        'CommandParameter' = 'DarkBlue'
        'Comment' = 'DarkGreen'
        'GroupEnd' = 'Black'
        'GroupStart' = 'Black'
        'Keyword' = 'DarkBlue'
        'LineContinuation' = 'Black'
        'LoopLabel' = 'DarkBlue'
        'Member' = 'Black'
        'NewLine' = 'Black'
        'Number' = 'Magenta'
        'Operator' = 'DarkGray'
        'Position' = 'Black'
        'StatementSeparator' = 'Black'
        'String' = 'DarkRed'
        'Type' = 'DarkCyan'
        'Unknown' = 'Black'
        'Variable' = 'Red'
    }
    
    $highlightColor = "Red"
    $highlightCharacter = ">"
    $highlightWidth = 6
    if($excludeLineNumbers) { $highlightWidth = 0 }
    
    ## Read the text of the file, and tokenize it
    $content = Get-Content $Path -Raw
    $parsed = [System.Management.Automation.PsParser]::Tokenize(
        $content, [ref] $null) | Sort StartLine,StartColumn
    
    ## Write a formatted line -- in the format of:
    ## <Line Number> <Separator Character> <Text>
    function WriteFormattedLine($formatString, [int] $line)
    {
        if($excludeLineNumbers) { return }
    
        ## By default, write the line number in gray, and use
        ## a simple pipe as the separator
        $hColor = "DarkGray"
        $separator = "|"
    
        ## If we need to highlight the line, use the highlight
        ## color and highlight separator as the separator
        if($highlightRange -contains $line)
        {
            $hColor = $highlightColor
            $separator = $highlightCharacter
        }
    
        ## Write the formatted line
        $text = $formatString -f $line,$separator
        Write-Host -NoNewLine -Fore $hColor -Back White $text
    }
    
    ## Complete the current line with filler cells
    function CompleteLine($column)
    {
        ## Figure how much space is remaining
        $lineRemaining = $host.UI.RawUI.WindowSize.Width -
            $column - $highlightWidth + 1
    
        ## If we have less than 0 remaining, we've wrapped onto the
        ## next line. Add another buffer width worth of filler
        if($lineRemaining -lt 0)
        {
            $lineRemaining += $host.UI.RawUI.WindowSize.Width
        }
    
        Write-Host -NoNewLine -Back White (" " * $lineRemaining)
    }
    
    
    ## Write the first line of context information (line number,
    ## highlight character.)
    Write-Host
    WriteFormattedLine "{0:D3} {1} " 1
    
    ## Now, go through each of the tokens in the input
    ## script
    $column = 1
    foreach($token in $parsed)
    {
        $color = "Gray"
    
        ## Determine the highlighting colour for that token by looking
        ## in the hashtable that maps token types to their color
        $color = $replacementColours[[string]$token.Type]
        if(-not $color) { $color = "Gray" }
    
        ## If it's a newline token, write the next line of context
        ## information
        if(($token.Type -eq "NewLine") -or ($token.Type -eq "LineContinuation"))
        {
            CompleteLine $column
            WriteFormattedLine "{0:D3} {1} " ($token.StartLine + 1)
            $column = 1
        }
        else
        {
            ## Do any indenting
            if($column -lt $token.StartColumn)
            {
                $text = " " * ($token.StartColumn - $column)
                Write-Host -Back White -NoNewLine $text
                $column = $token.StartColumn
            }
    
            ## See where the token ends
            $tokenEnd = $token.Start + $token.Length - 1
    
            ## Handle the line numbering for multi-line strings and comments
            if(
                (($token.Type -eq "String") -or
                ($token.Type -eq "Comment")) -and
                ($token.EndLine -gt $token.StartLine))
            {
                ## Store which line we've started at
                $lineCounter = $token.StartLine
    
                ## Split the content of this token into its lines
                ## We use the start and end of the tokens to determine
                ## the position of the content, but use the content
                ## itself (rather than the token values) for manipulation.
                $stringLines = $(
                    -join $content[$token.Start..$tokenEnd] -split "`n")
    
                ## Go through each of the lines in the content
                foreach($stringLine in $stringLines)
                {
                    $stringLine = $stringLine.Trim()
    
                    ## If we're on a new line, fill the right hand
                    ## side of the line with spaces, and write the header
                    ## for the new line.
                    if($lineCounter -gt $token.StartLine)
                    {
                        CompleteLine $column
                        WriteFormattedLine "{0:D3} {1} " $lineCounter
                        $column = 1
                    }
    
                    ## Now write the text of the current line
                    Write-Host -NoNewLine -Fore $color -Back White $stringLine
                    $column += $stringLine.Length
                    $lineCounter++
                }
            }
            ## Write out a regular token
            else
            {
                ## We use the start and end of the tokens to determine
                ## the position of the content, but use the content
                ## itself (rather than the token values) for manipulation.
                $text = (-join $content[$token.Start..$tokenEnd])
                Write-Host -NoNewLine -Fore $color -Back White $text
            }
    
            ## Update our position in the column
            $column = $token.EndColumn
        }
    }
    
    CompleteLine $column
    Write-Host
}

function Show-Object
{
    #############################################################################
    ##
    ## Show-Object
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Provides a graphical interface to let you explore and navigate an object.
     
     
    .EXAMPLE
     
    PS > $ps = { Get-Process -ID $pid }.Ast
    PS > Show-Object $ps
     
    #>

    
    param(
        ## The object to examine
        [Parameter(ValueFromPipeline = $true)]
        $InputObject
    )
    
    Set-StrictMode -Version 3
    
    Add-Type -Assembly System.Windows.Forms
    
    ## Figure out the variable name to use when displaying the
    ## object navigation syntax. To do this, we look through all
    ## of the variables for the one with the same object identifier.
    $rootVariableName = dir variable:\* -Exclude InputObject,Args |
        Where-Object {
            $_.Value -and
            ($_.Value.GetType() -eq $InputObject.GetType()) -and
            ($_.Value.GetHashCode() -eq $InputObject.GetHashCode())
    }
    
    ## If we got multiple, pick the first
    $rootVariableName = $rootVariableName| % Name | Select -First 1
    
    ## If we didn't find one, use a default name
    if(-not $rootVariableName)
    {
        $rootVariableName = "InputObject"
    }
    
    ## A function to add an object to the display tree
    function PopulateNode($node, $object)
    {
        ## If we've been asked to add a NULL object, just return
        if(-not $object) { return }
    
        ## If the object is a collection, then we need to add multiple
        ## children to the node
        if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object))
        {
            ## Some very rare collections don't support indexing (i.e.: $foo[0]).
            ## In this situation, PowerShell returns the parent object back when you
            ## try to access the [0] property.
            $isOnlyEnumerable = $object.GetHashCode() -eq $object[0].GetHashCode()
    
            ## Go through all the items
            $count = 0
            foreach($childObjectValue in $object)
            {
                ## Create the new node to add, with the node text of the item and
                ## value, along with its type
                $newChildNode = New-Object Windows.Forms.TreeNode
                $newChildNode.Text = "$($node.Name)[$count] = $childObjectValue"
                $newChildNode.ToolTipText = $childObjectValue.GetType()
                   
                ## Use the node name to keep track of the actual property name
                ## and syntax to access that property.
                ## If we can't use the index operator to access children, add
                ## a special tag that we'll handle specially when displaying
                ## the node names.
                if($isOnlyEnumerable)
                {
                    $newChildNode.Name = "@"
                }
    
                $newChildNode.Name += "[$count]"
                $null = $node.Nodes.Add($newChildNode)               
    
                ## If this node has children or properties, add a placeholder
                ## node underneath so that the node shows a '+' sign to be
                ## expanded.
                AddPlaceholderIfRequired $newChildNode $childObjectValue
    
                $count++
            }
        }
        else
        {
            ## If the item was not a collection, then go through its
            ## properties
            foreach($child in $object.PSObject.Properties)
            {
                ## Figure out the value of the property, along with
                ## its type.
                $childObject = $child.Value
                $childObjectType = $null
                if($childObject)
                {
                    $childObjectType = $childObject.GetType()
                }
    
                ## Create the new node to add, with the node text of the item and
                ## value, along with its type
                $childNode = New-Object Windows.Forms.TreeNode
                $childNode.Text = $child.Name + " = $childObject"
                $childNode.ToolTipText = $childObjectType
                if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($childObject))
                {
                    $childNode.ToolTipText += "[]"
                }
                
                $childNode.Name = $child.Name
                $null = $node.Nodes.Add($childNode)
    
                ## If this node has children or properties, add a placeholder
                ## node underneath so that the node shows a '+' sign to be
                ## expanded.
                AddPlaceholderIfRequired $childNode $childObject
            }
        }
    }
    
    ## A function to add a placeholder if required to a node.
    ## If there are any properties or children for this object, make a temporary
    ## node with the text "..." so that the node shows a '+' sign to be
    ## expanded.
    function AddPlaceholderIfRequired($node, $object)
    {
        if(-not $object) { return }
    
        if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object) -or
            @($object.PSObject.Properties))
        {
            $null = $node.Nodes.Add( (New-Object Windows.Forms.TreeNode "...") )
        }
    }
    
    ## A function invoked when a node is selected.
    function OnAfterSelect
    {
        param($Sender, $TreeViewEventArgs)
    
        ## Determine the selected node
        $nodeSelected = $Sender.SelectedNode
    
        ## Walk through its parents, creating the virtual
        ## PowerShell syntax to access this property.
        $nodePath = GetPathForNode $nodeSelected
    
        ## Now, invoke that PowerShell syntax to retrieve
        ## the value of the property.
        $resultObject = Invoke-Expression $nodePath
        $outputPane.Text = $nodePath
    
        ## If we got some output, put the object's member
        ## information in the text box.
        if($resultObject)
        {
            $members = Get-Member -InputObject $resultObject | Out-String       
            $outputPane.Text += "`n" + $members
        }
    }
    
    ## A function invoked when the user is about to expand a node
    function OnBeforeExpand
    {
        param($Sender, $TreeViewCancelEventArgs)
    
        ## Determine the selected node
        $selectedNode = $TreeViewCancelEventArgs.Node
    
        ## If it has a child node that is the placeholder, clear
        ## the placehoder node.
        if($selectedNode.FirstNode -and
            ($selectedNode.FirstNode.Text -eq "..."))
        {
            $selectedNode.Nodes.Clear()
        }
        else
        {
            return
        }
    
        ## Walk through its parents, creating the virtual
        ## PowerShell syntax to access this property.
        $nodePath = GetPathForNode $selectedNode 
    
        ## Now, invoke that PowerShell syntax to retrieve
        ## the value of the property.
        Invoke-Expression "`$resultObject = $nodePath"
    
        ## And populate the node with the result object.
        PopulateNode $selectedNode $resultObject
    }
    
    ## A function to handle key presses on the tree view.
    ## In this case, we capture ^C to copy the path of
    ## the object property that we're currently viewing.
    function OnTreeViewKeyPress
    {
        param($Sender, $KeyPressEventArgs)
    
        ## [Char] 3 = Control-C
        if($KeyPressEventArgs.KeyChar -eq 3)
        {
            $KeyPressEventArgs.Handled = $true
    
            ## Get the object path, and set it on the clipboard
            $node = $Sender.SelectedNode
            $nodePath = GetPathForNode $node
            [System.Windows.Forms.Clipboard]::SetText($nodePath)
    
            $form.Close()
        }
        elseif([System.Windows.Forms.Control]::ModifierKeys -eq "Control")
        {
            if($KeyPressEventArgs.KeyChar -eq '+')
            {
                $SCRIPT:currentFontSize++
                UpdateFonts $SCRIPT:currentFontSize
            
                $KeyPressEventArgs.Handled = $true
            }
            elseif($KeyPressEventArgs.KeyChar -eq '-')
            {
                $SCRIPT:currentFontSize--
                if($SCRIPT:currentFontSize -lt 1) { $SCRIPT:currentFontSize = 1 }
                UpdateFonts $SCRIPT:currentFontSize
            
                $KeyPressEventArgs.Handled = $true
            }
        }
    }

    ## A function to handle key presses on the form.
    ## In this case, we handle Ctrl-Plus and Ctrl-Minus
    ## to adjust font size.
    function OnKeyUp
    {
        param($Sender, $KeyUpEventArgs)

        if([System.Windows.Forms.Control]::ModifierKeys -eq "Control")
        {
            if($KeyUpEventArgs.KeyCode -in 'Add','OemPlus')
            {
                $SCRIPT:currentFontSize++
                UpdateFonts $SCRIPT:currentFontSize
            
                $KeyUpEventArgs.Handled = $true
            }
            elseif($KeyUpEventArgs.KeyCode -in 'Subtract','OemMinus')
            {
                $SCRIPT:currentFontSize--
                if($SCRIPT:currentFontSize -lt 1) { $SCRIPT:currentFontSize = 1 }
                UpdateFonts $SCRIPT:currentFontSize
            
                $KeyUpEventArgs.Handled = $true
            }
            elseif($KeyUpEventArgs.KeyCode -eq 'D0')
            {
                $SCRIPT:currentFontSize = 12
                UpdateFonts $SCRIPT:currentFontSize
            
                $KeyUpEventArgs.Handled = $true
            }
        }
    }

    ## A function to handle mouse wheel scrolling.
    ## In this case, we translate Ctrl-Wheel to zoom.
    function OnMouseWheel
    {
        param($Sender, $MouseEventArgs)
    
        if(
            ([System.Windows.Forms.Control]::ModifierKeys -eq "Control") -and
            ($MouseEventArgs.Delta -ne 0))
        {
            $SCRIPT:currentFontSize += ($MouseEventArgs.Delta / 120)
            if($SCRIPT:currentFontSize -lt 1) { $SCRIPT:currentFontSize = 1 }

            UpdateFonts $SCRIPT:currentFontSize
            $MouseEventArgs.Handled = $true
        }
    }
    
    ## A function to walk through the parents of a node,
    ## creating virtual PowerShell syntax to access this property.
    function GetPathForNode
    {
        param($Node)
    
        $nodeElements = @()
    
        ## Go through all the parents, adding them so that
        ## $nodeElements is in order.
        while($Node)
        {
            $nodeElements = ,$Node + $nodeElements
            $Node = $Node.Parent
        }
    
        ## Now go through the node elements
        $nodePath = ""
        foreach($Node in $nodeElements)
        {
            $nodeName = $Node.Name
    
            ## If it was a node that PowerShell is able to enumerate
            ## (but not index), wrap it in the array cast operator.
            if($nodeName.StartsWith('@'))
            {
                $nodeName = $nodeName.Substring(1)
                $nodePath = "@(" + $nodePath + ")"
            }
            elseif($nodeName.StartsWith('['))
            {
                ## If it's a child index, we don't need to
                ## add the dot for property access
            }
            elseif($nodePath)
            {
                ## Otherwise, we're accessing a property. Add a dot.
                $nodePath += "."
            }
    
            ## Append the node name to the path
            $tempNodePath = $nodePath + $nodeName
            if($nodeName -notmatch '^[$\[\]a-zA-Z0-9]+$')
            {
                $nodePath += "'" + $nodeName + "'"
            }
            else
            {
                $nodePath = $tempNodePath
            }
        }
    
        ## And return the result
        $nodePath
    }

    function UpdateFonts
    {
        param($fontSize)
    
        $treeView.Font = New-Object System.Drawing.Font "Consolas",$fontSize
        $outputPane.Font = New-Object System.Drawing.Font "Consolas",$fontSize
    }
    
    $SCRIPT:currentFontSize = 12

    ## Create the TreeView, which will hold our object navigation
    ## area.
    $treeView = New-Object Windows.Forms.TreeView
    $treeView.Dock = "Top"
    $treeView.Height = 500
    $treeView.PathSeparator = "."
    $treeView.ShowNodeToolTips = $true
    $treeView.Add_AfterSelect( { OnAfterSelect @args } )
    $treeView.Add_BeforeExpand( { OnBeforeExpand @args } )
    $treeView.Add_KeyPress( { OnTreeViewKeyPress @args } )
    
    ## Create the output pane, which will hold our object
    ## member information.
    $outputPane = New-Object System.Windows.Forms.TextBox
    $outputPane.Multiline = $true
    $outputPane.WordWrap = $false
    $outputPane.ScrollBars = "Both"
    $outputPane.Dock = "Fill"

    ## Create the root node, which represents the object
    ## we are trying to show.
    $root = New-Object Windows.Forms.TreeNode
    $root.ToolTipText = $InputObject.GetType()
    $root.Text = $InputObject
    $root.Name = '$' + $rootVariableName
    $root.Expand()
    $null = $treeView.Nodes.Add($root)

    UpdateFonts $currentFontSize
    
    ## And populate the initial information into the tree
    ## view.
    PopulateNode $root $InputObject
    
    ## Finally, create the main form and show it.
    $form = New-Object Windows.Forms.Form
    $form.Text = "Browsing " + $root.Text
    $form.Width = 1000
    $form.Height = 800
    $form.Controls.Add($outputPane)
    $form.Controls.Add($treeView)
    $form.Add_MouseWheel( { OnMouseWheel @args } )
    $treeView.Add_KeyUp( { OnKeyUp @args } )
    $treeView.Select()
    $null = $form.ShowDialog()
    $form.Dispose()
}

function Start-ProcessAsUser
{
    ##############################################################################
    ##
    ## Start-ProcessAsUser
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Launch a process under alternate credentials, providing functionality
    similar to runas.exe.
     
    .EXAMPLE
     
    PS > $file = Join-Path ([Environment]::GetFolderPath("System")) certmgr.msc
    PS > Start-ProcessAsUser Administrator mmc $file
     
    #>

    
    param(
        ## The credential to launch the process under
        $Credential = (Get-Credential),
    
        ## The process to start
        [Parameter(Mandatory = $true)]
        [string] $Process,
    
        ## Any arguments to pass to the process
        [string] $ArgumentList = ""
    )
    
    Set-StrictMode -Version 3
    
    ## Create a real credential if they supplied a username
    $credential = Get-Credential $credential
    
    ## Exit if they canceled out of the credential dialog
    if(-not ($credential -is "System.Management.Automation.PsCredential"))
    {
        return
    }
    
    ## Prepare the startup information (including username and password)
    $startInfo = New-Object Diagnostics.ProcessStartInfo
    $startInfo.Filename = $process
    $startInfo.Arguments = $argumentList
    
    ## If we're launching as ourselves, set the "runas" verb
    if(($credential.Username -eq "$ENV:Username") -or
        ($credential.Username -eq "\$ENV:Username"))
    {
        $startInfo.Verb = "runas"
    }
    else
    {
        $startInfo.UserName = $credential.Username
        $startInfo.Password = $credential.Password
        $startInfo.UseShellExecute = $false
    }
    
    ## Start the process
    [Diagnostics.Process]::Start($startInfo)
}

function Test-Uri
{
    ##############################################################################
    ##
    ## Test-Uri
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Connects to a given URI and returns status about it: URI, response code,
    and time taken.
     
    .EXAMPLE
     
    PS > Test-Uri bing.com
     
    Uri : bing.com
    StatusCode : 200
    StatusDescription : OK
    ResponseLength : 34001
    TimeTaken : 459.0009
     
    #>

    
    param(
        ## The URI to test
        $Uri
    )
    
    $request = $null
    $time = try
    {
        ## Request the URI, and measure how long the response took.
        $result = Measure-Command { $request = Invoke-WebRequest -Uri $uri }
        $result.TotalMilliseconds
    }
    catch
    {
        ## If the request generated an exception (i.e.: 500 server
        ## error or 404 not found), we can pull the status code from the
        ## Exception.Response property
        $request = $_.Exception.Response
        $time = -1
    }
    
    $result = [PSCustomObject] @{
        Time = Get-Date;
        Uri = $uri;
        StatusCode = [int] $request.StatusCode;
        StatusDescription = $request.StatusDescription;
        ResponseLength = $request.RawContentLength;
        TimeTaken = $time;
    }
    
    $result
}

function Use-Culture
{
    #############################################################################
    ##
    ## Use-Culture
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    #############################################################################
    
    <#
     
    .SYNOPSIS
     
    Invoke a scriptblock under the given culture
     
    .EXAMPLE
     
    PS > Use-Culture fr-FR { Get-Date -Date "25/12/2007" }
    mardi 25 decembre 2007 00:00:00
     
    #>

    
    param(
        ## The culture in which to evaluate the given script block
        [Parameter(Mandatory = $true)]
        [System.Globalization.CultureInfo] $Culture,
    
        ## The code to invoke in the context of the given culture
        [Parameter(Mandatory = $true)]
        [ScriptBlock] $ScriptBlock
    )
    
    Set-StrictMode -Version 3
    
    ## A helper function to set the current culture
    function Set-Culture([System.Globalization.CultureInfo] $culture)
    {
        [System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture
        [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture
    }
    
    ## Remember the original culture information
    $oldCulture = [System.Threading.Thread]::CurrentThread.CurrentUICulture
    
    ## Restore the original culture information if
    ## the user's script encounters errors.
    trap { Set-Culture $oldCulture }
    
    ## Set the current culture to the user's provided
    ## culture.
    Set-Culture $culture
    
    ## Invoke the user's scriptblock
    & $ScriptBlock
    
    ## Restore the original culture information.
    Set-Culture $oldCulture
}

function Watch-Command
{
    ##############################################################################
    ##
    ## Watch-Command
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################

    <#
 
    .SYNOPSIS
 
    Watches the result of a command invocation, alerting you when the output
    either matches a specified string, lacks a specified string, or has simply
    changed.
 
    .EXAMPLE
 
    PS > Watch-Command { Get-Process -Name Notepad | Measure } -UntilChanged
    Monitors Notepad processes until you start or stop one.
 
    .EXAMPLE
 
    PS > Watch-Command { Get-Process -Name Notepad | Measure } -Until "Count : 1"
    Monitors Notepad processes until there is exactly one open.
 
    .EXAMPLE
 
    PS > Watch-Command {
         Get-Process -Name Notepad | Measure } -While 'Count : \d\s*\n'
    Monitors Notepad processes while there are between 0 and 9 open
    (once number after the colon).
 
    #>


    [CmdletBinding(DefaultParameterSetName = "Forever")]
    param(
        ## The script block to invoke while monitoring
        [Parameter(Mandatory = $true, Position = 0)]
        [ScriptBlock] $ScriptBlock,

        ## The delay, in seconds, between monitoring attempts
        [Parameter()]
        [Double] $DelaySeconds = 1,

        ## Specifies that the alert sound should not be played
        [Parameter()]
        [Switch] $Quiet,

        ## Monitoring continues only while the output of the
        ## command remains the same.
        [Parameter(ParameterSetName = "UntilChanged", Mandatory = $false)]
        [Switch] $UntilChanged,

        ## The regular expression to search for. Monitoring continues
        ## until this expression is found.
        [Parameter(ParameterSetName = "Until", Mandatory = $false)]
        [String] $Until,

        ## The regular expression to search for. Monitoring continues
        ## until this expression is not found.
        [Parameter(ParameterSetName = "While", Mandatory = $false)]
        [String] $While
    )

    Set-StrictMode -Version 3

    $initialOutput = ""
    $lastCursorTop = 0
    Clear-Host

    ## Start a continuous loop
    while($true)
    {
        ## Run the provided script block
        $r = & $ScriptBlock
       
        ## Clear the screen and display the results
        $buffer = $ScriptBlock.ToString().Trim() + "`r`n"
        $buffer += "`r`n"
        $textOutput = $r | Out-String
        $buffer += $textOutput

        [Console]::SetCursorPosition(0, 0)
        [Console]::Write($buffer)
        
        $currentCursorTop = [Console]::CursorTop
        $linesToClear = $lastCursorTop - $currentCursorTop
        if($linesToClear -gt 0)
        {
            [Console]::Write((" " * [Console]::WindowWidth * $linesToClear))
        }
        
        $lastCursorTop = [Console]::CursorTop
        [Console]::SetCursorPosition(0, 0)

        ## Remember the initial output, if we haven't
        ## stored it yet
        if(-not $initialOutput)
        {
            $initialOutput = $textOutput
        }

        ## If we are just looking for any change,
        ## see if the text has changed.
        if($UntilChanged)
        {
            if($initialOutput -ne $textOutput)
            {
                break
            }
        }

        ## If we need to ensure some text is found,
        ## break if we didn't find it.
        if($While)
        {
            if($textOutput -notmatch $While)
            {
                break
            }
        }

        ## If we need to wait for some text to be found,
        ## break if we find it.
        if($Until)
        {
            if($textOutput -match $Until)
            {
                break
            }
        }

        ## Delay
        Start-Sleep -Seconds $DelaySeconds
    }

    ## Notify the user
    if(-not $Quiet)
    {
        [Console]::Beep(1000, 1000)
    }
}

function Watch-DebugExpression
{
    #############################################################################
    ##
    ## Watch-DebugExpression
    ##
    ## From Windows PowerShell Cookbook (O'Reilly)
    ## by Lee Holmes (http://www.leeholmes.com/guide)
    ##
    ##############################################################################
    
    <#
     
    .SYNOPSIS
     
    Updates your prompt to display the values of information you want to track.
     
    .EXAMPLE
     
    PS > Watch-DebugExpression { (Get-History).Count }
     
    Expression Value
    ---------- -----
    (Get-History).Count 3
     
    PS > Watch-DebugExpression { $count }
     
    Expression Value
    ---------- -----
    (Get-History).Count 4
    $count
     
    PS > $count = 100
     
    Expression Value
    ---------- -----
    (Get-History).Count 5
    $count 100
     
    PS > Watch-DebugExpression -Reset
    PS >
     
    #>

    
    param(
        ## The expression to track
        [ScriptBlock] $ScriptBlock,
    
        ## Switch to no longer watch an expression
        [Switch] $Reset
    )
    
    Set-StrictMode -Version 3
    
    if($Reset)
    {
        Set-Item function:\prompt ([ScriptBlock]::Create($oldPrompt))
    
        Remove-Item variable:\expressionWatch
        Remove-Item variable:\oldPrompt
    
        return
    }
    
    ## Create the variableWatch variable if it doesn't yet exist
    if(-not (Test-Path variable:\expressionWatch))
    {
        $GLOBAL:expressionWatch = @()
    }
    
    ## Add the current variable name to the watch list
    $GLOBAL:expressionWatch += $scriptBlock
    
    ## Update the prompt to display the expression values,
    ## if needed.
    if(-not (Test-Path variable:\oldPrompt))
    {
        $GLOBAL:oldPrompt = Get-Content function:\prompt
    }
    
    if($oldPrompt -notlike '*$expressionWatch*')
    {
        $newPrompt = @'
            $results = foreach($expression in $expressionWatch)
            {
                New-Object PSObject -Property @{
                    Expression = $expression.ToString().Trim();
                    Value = & $expression
                } | Select Expression,Value
            }
            Write-Host "`n"
            Write-Host ($results | Format-Table -Auto | Out-String).Trim()
            Write-Host "`n"
     
'@

    
        $newPrompt += $oldPrompt
    
        Set-Item function:\prompt ([ScriptBlock]::Create($newPrompt))
    }
}