从其他运行空间Powershell中的WPF文本框中读取文本

时间:2018-08-02 20:09:29

标签: wpf powershell textbox runspace

我正在编写的工具包遇到了一个新的绊脚石。到目前为止,我已经设法处理带有数组的复选框并切换大小写以处理后续命令。但是,我有几件事需要用户输入,我想通过TextBox来获取它,到目前为止,我无法做到这一点。

主要程序代码:

$ScriptRoot = $pwd.Path
$ScriptRoot
$Functionsfile = "$ScriptRoot" + "\Functions.ps1"
. $Functionsfile

$Global:Return
$Global:syncHash = [hashtable]::Synchronized(@{})

$newRunspace =[runspacefactory]::CreateRunspace()

$newRunspace.ApartmentState = "STA"

$newRunspace.ThreadOptions = "ReuseThread"

$newRunspace.Open()

$newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
$newRunspace.SessionStateProxy.SetVariable("Functionsfile",$Functionsfile)
$newRunspace.SessionStateProxy.SetVariable("Scriptroot",$ScriptRoot)

# Load WPF assembly if necessary

[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')



$psCmd = [PowerShell]::Create().AddScript({

   [xml]$xaml = @"
<Window x:Name="Main_Form" x:Class="UnattendCheck.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:UnattendCheck"
        mc:Ignorable="d"
        Title="Unattend Check" Height="413.414" Width="509.756" Background="#FFEAEAEA">
<Grid Margin="0,0,2,1">
    <Label x:Name="ContactLabel" Content="Select Techs to contact on Failure" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
    <CheckBox x:Name="Email1ChBox" Content="Ben Church" HorizontalAlignment="Left" Margin="10,41,0,0" VerticalAlignment="Top"/>
    <CheckBox x:Name="Email2ChBox" Content="Dustin Klingele" HorizontalAlignment="Left" Margin="10,56,0,0" VerticalAlignment="Top"/>
    <CheckBox x:Name="Email3ChBox" Content="Brian Oitker" HorizontalAlignment="Left" Margin="10,71,0,0" VerticalAlignment="Top"/>
    <Label x:Name="OptionsLabel" Content="Options" HorizontalAlignment="Left" Margin="221,10,0,0" VerticalAlignment="Top"/>
    <CheckBox x:Name="Option1ChBox" Content="OptiPlex 755" HorizontalAlignment="Left" Margin="221,41,0,0" VerticalAlignment="Top"/>
    <CheckBox x:Name="Option2ChBox" Content="F216 Lab" HorizontalAlignment="Left" Margin="221,56,0,0" VerticalAlignment="Top"/>
    <CheckBox x:Name="Option3ChBox" Content="F105 Lab" HorizontalAlignment="Left" Margin="221,71,0,0" VerticalAlignment="Top"/>
    <CheckBox x:Name="Option4ChBox" Content="Email on completion" HorizontalAlignment="Left" Margin="221,86,0,0" VerticalAlignment="Top"/>
    <TextBox x:Name="OutputBox" HorizontalAlignment="Left" Height="196" Margin="10,162,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="480" Text="Output Logging"/>
    <Button x:Name="StartBtn" Content="Start" HorizontalAlignment="Left" Margin="415,137,0,0" VerticalAlignment="Top" Width="75"/>
    <TextBox x:Name="DesignationBox" HorizontalAlignment="Left" Height="23" Margin="10,134,0,0" TextWrapping="Wrap" Text="Designation" VerticalAlignment="Top" Width="120"/>
    <TextBox x:Name="StartNumberBox" HorizontalAlignment="Left" Height="23" Margin="135,134,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="45" Text="First #"/>
    <TextBox x:Name="EndNumberBox" HorizontalAlignment="Left" Height="23" Margin="185,134,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="45" Text="Last #"/>
    <PasswordBox x:Name="PasswordBox" HorizontalAlignment="Left" Margin="370,109,0,0" VerticalAlignment="Top" Width="120" Height="23"/>
    <TextBox x:Name="UsernameBox" HorizontalAlignment="Left" Height="23" Margin="370,56,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
    <Label x:Name="PasswordLabel" Content="Password:" HorizontalAlignment="Left" Margin="365,83,0,0" VerticalAlignment="Top"/>
    <Label x:Name="UsernameLabel" Content="Username:" HorizontalAlignment="Left" Margin="365,30,0,0" VerticalAlignment="Top"/>
    <Label x:Name="CredLabel" Content="Enter credentials for email." HorizontalAlignment="Left" Margin="339,10,0,0" VerticalAlignment="Top"/>


    </Grid>
</Window>

"@

# Variables

$Emails = @($null, $null, $null)
$Options = @($null, $null, $null, $null)

#Tech emails
$email1 = 'butter@butterzone.com'
$email2 = 'margarine@butterzone.com'
$email3 = 'pad@butterzone.com'

. $Functionsfile

 Process-XML


    $Script:JobCleanup = [hashtable]::Synchronized(@{})

    $Script:Jobs = [system.collections.arraylist]::Synchronized((New-Object System.Collections.ArrayList))

    #region Background runspace to clean up jobs

    $jobCleanup.Flag = $True

    $newRunspace =[runspacefactory]::CreateRunspace()

    $newRunspace.ApartmentState = "STA"

    $newRunspace.ThreadOptions = "ReuseThread"          

    $newRunspace.Open()        

    $newRunspace.SessionStateProxy.SetVariable("jobCleanup",$jobCleanup)     

    $newRunspace.SessionStateProxy.SetVariable("jobs",$jobs) 


    $jobCleanup.PowerShell = [PowerShell]::Create().AddScript({

        #Routine to handle completed runspaces

        Do {    

            Foreach($runspace in $jobs) {            

                If ($runspace.Runspace.isCompleted) {

                    [void]$runspace.powershell.EndInvoke($runspace.Runspace)

                    $runspace.powershell.dispose()

                    $runspace.Runspace = $null

                    $runspace.powershell = $null               

                } 

            }

            #Clean out unused runspace jobs

            $temphash = $jobs.clone()

            $temphash | Where {

                $_.runspace -eq $Null

            } | ForEach {

                $jobs.remove($_)

            }        

            Start-Sleep -Seconds 1     

        } while ($jobCleanup.Flag)

    })

    $jobCleanup.PowerShell.Runspace = $newRunspace

    $jobCleanup.Thread = $jobCleanup.PowerShell.BeginInvoke()  

    #endregion Background runspace to clean up jobs


 <#Start of checkbox processing #> 

 <#Emails #>

    $syncHash.Email1ChBox.Add_Click({ 
    If ( $Emails[0] -ne $email1 ) {
    $Emails[0] = $email1
    }
    Else { $Emails[0] = $null }
    })

    $syncHash.Email2ChBox.Add_Click({ 
    If ( $Emails[1] -ne $email2 ) {
    $Emails[1] = $email2
    }
    Else { $Emails[1] = $null }
    })

    $syncHash.Email3ChBox.Add_Click({ 
    If ( $Emails[2] -ne $email3 ) {
    $Emails[2] = $email3
    }
    Else { $Emails[2] = $null }
    })

    <# Option Items #>

    $syncHash.Option1ChBox.Add_Click({ 
    If ( $Options[0] -ne "755" ) {
    $Options[0] = "755"
    }
    Else { $Options[0] = $null }
    })

    $syncHash.Option2ChBox.Add_Click({ 
    If ( $Options[1] -ne "F216" ) {
    $Options[1] = "F216"
    }
    Else { $Options[1] = $null }
    })

    $syncHash.Option3ChBox.Add_Click({ 
    If ( $Options[2] -ne "F105" ) {
    $Options[2] = "F105"
    }
    Else { $Options[2] = $null }
    })

    $syncHash.Option4ChBox.Add_Click({ 
    If ( $Options[3] -ne "Success" ) {
    $Options[3] = "Success"
    }
    Else { $Options[3] = 0 }
    })

    $syncHash.UsernameBox.TextChanged({
    $Username = $syncHash.UsernameBox.Text
    })

    $syncHash.StartBtn.Add_Click({   # The big red button.
     #Start-Job -Name Sleeping -ScriptBlock {start-sleep 5}

        #while ((Get-Job Sleeping).State -eq 'Running'){

            $x+= "."

        #region Boe's Additions

        $newRunspace =[runspacefactory]::CreateRunspace()

        $newRunspace.ApartmentState = "STA"

        $newRunspace.ThreadOptions = "ReuseThread"          

        $newRunspace.Open()

        $newRunspace.SessionStateProxy.SetVariable("SyncHash",$SyncHash) 
        $newRunspace.SessionStateProxy.SetVariable("Emails",$Emails)
        $newRunspace.SessionStateProxy.SetVariable("Functionsfile",$Functionsfile)
        $newRunspace.SessionStateProxy.SetVariable("Options",$Options)
        $newRunspace.SessionStateProxy.SetVariable("Scriptroot",$ScriptRoot)
        $newRunspace.SessionStateProxy.SetVariable("Username",$Username)


        $PowerShell = [PowerShell]::Create().AddScript({
        $names = @("Ah ah ah... You didn't say the magic word...", $null, $null)
. $Functionsfile
$synchash.PasswordBox
$counter = 0
While ( $counter -lt 5 ) {
$counter++
$output = $output + $names[0]
Update-Window -Control OutputBox -Property Text -Value $output
$output = $output + "`n"
}

 $text = $syncHash.UsernameBox.GetLineText()
Update-Window -Control OutputBox -Property Text -Value $Username
#Update-Window -Control OutputBox -Property Text -Value $syncHash.UsernameBox
<#
$Cred = New-Object System.Management.Automation.PSCredential($synchash.UsernameBox.Text,$synchash.PasswordBox.SecurePassword)
Email "Unattend complete." "Computername has finished the unattend. If it needs anything else you can go do that now." -InputList $Emails $Cred
#>
})

     $PowerShell.Runspace = $newRunspace

        [void]$Jobs.Add((

            [pscustomobject]@{

                PowerShell = $PowerShell

                Runspace = $PowerShell.BeginInvoke()

            }

        ))

        })

    #region Window Close 

    $syncHash.Window.Add_Closed({

        Write-Verbose 'Halt runspace cleanup job processing'

        $jobCleanup.Flag = $False



        #Stop all runspaces

        $jobCleanup.PowerShell.Dispose()   

    })

    #endregion Window Close 

    #endregion Boe's Additions



#$x.Host.Runspace.Events.GenerateEvent( "TestClicked", $x.test, $null, "test event")



    #$syncHash.Window.Activate()

    $syncHash.Window.ShowDialog() | Out-Null

    $syncHash.Error = $Error

})

$psCmd.Runspace = $newRunspace

$data = $psCmd.BeginInvoke()

Functions.ps1代码

Function Update-Window () {

    Param (

        $Control,

        $Property,

        $Value,

        [switch]$AppendContent

    )



    # This is kind of a hack, there may be a better way to do this

    If ($Property -eq "Close") {

        $syncHash.Window.Dispatcher.invoke([action]{$syncHash.Window.Close()},"Normal")

        Return

    }



    # This updates the control based on the parameters passed to the function

    $syncHash.$Control.Dispatcher.Invoke([action]{

        # This bit is only really meaningful for the TextBox control, which might be useful for logging progress steps

        If ($PSBoundParameters['AppendContent']) {

            $syncHash.$Control.AppendText($Value)

        } Else {

            $syncHash.$Control.$Property = $Value

        }

    }, "Normal")

}

Function Read-Textbox(){
write-host "`nBegin Read-Textbox [$(get-date)]"
$SyncHash.UsernameBox.Dispatcher.Invoke($Normal,[action]{$Global:Return = $SyncHash.UsernameBox.Text})
$Global:Return
remove-variable -Name Return -scope Global
write-host "End Read-Textbox [$(get-date)]"
}

 Function Process-XML () {

# Remove XML attributes that break a couple things.

#   Without this, you must manually remove the attributes

#   after pasting from Visual Studio. If more attributes

#   need to be removed automatically, add them below.

$AttributesToRemove = @(

    'x:Class',

    'mc:Ignorable'

)



foreach ($Attrib in $AttributesToRemove) {

    if ( $xaml.Window.GetAttribute($Attrib) ) {

         $xaml.Window.RemoveAttribute($Attrib)

    }

}



$reader=(New-Object System.Xml.XmlNodeReader $xaml)



$syncHash.Window=[Windows.Markup.XamlReader]::Load( $reader )



[xml]$XAML = $xaml

    $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | %{

    #Find all of the form types and add them as members to the synchash

    $syncHash.Add($_.Name,$syncHash.Window.FindName($_.Name) )



}
}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}

# Functions for UnattendCheck, can be used elsewhere.

Function Email([string]$subject,[string]$message,$Addresses,$Cred) {

$mailparam = @{
To = $Addresses
From = $useremail
Subject = $subject
Body = $message
SmtpServer = 'smtp.office365.com'
Port = '587'
Credential = $Cred
}

Send-MailMessage @mailparam -UseSsl
}

Function unattendcheck {
[string]$user = '*inst*'
<# This checks for the existence of end.txt, if it exists... #>
If (Test-Path \\$computername\C$\install\end.txt) {

    <# If install user is no longer signed in and the end.txt exists, we are done. Probably. #>
If ($active -eq 0) {
        IF ( $email_notify -eq 1 ) {
        email "Unattend complete." "$computername has finished the unattend. If it needs anything else you can go do that now."
        }
    Write-Host "$computername has finished unattending."
    $active = 0
    $endcounter = 0
    $counter = 0
    $query = $null
    exit 0
} 
} ElseIf (-Not (Test-Path \\$computername\C$\install\end.txt)) {
    If ( -NOT ( $counter -eq 0 ) -AND $counter -le 12 -OR $active -eq 1 ) {
    <# If the install user was signed in or not, we wait 5 minutes and check again.
   If after 60 minutes we haven't seen the install user, we exit with a fault message. #>

    Start-Sleep $timer
    $counter++

    If ( $counter -eq 6 -AND $active -eq 0 ) {
    email "Unattend failure on $computername" "There appears to be a failure on $computername during unattend. The install user never signed in."
    Write-Host "There appears to be a failure on $computername during unattend. The install user never signed in."
    exit 88
    } ElseIf ( $counter -eq 12 -AND $active -eq 1 ) {
    email "Unattend failure on $computername" "$computername appears to have gotten stuck. Go check on it."
    Write-Host "$computername appears to have gotten stuck. Go check on it."
    exit 1864
    }

    unattendcheck


} Else {
    <# If we cannot find the end.txt file, we assume the unattend
    is still going, and we wait 90 minutes before checking again. #>
    ($query = query user /server:$computername) 2>&1 | out-null
        <# Check for the install user being signed in.... #> 

    <# Set it as active and reset counter to prevent premature ending of script. #>
    If ( $query -like $user ) { 
    $active = 1
    If ( $endcounter -eq 0 ) {
        $endcounter = 1
        $counter = 0
}        
    } else { $active=0 ; Write-Host "Install user not active yet, will check back in 30 minutes."}

    Start-Sleep 2700
    $counter = 1
    $timer = 300
    unattendcheck
    }
}
}

这是我目前最后一次尝试获得东西的方法。如果将“输出”框更改为$ syncHash.UsernameBox,它将显示System.Windows.Controls.TextBox:作为其输出,这意味着从某种意义上讲它可以拉取值,但是当我将其输出为字符串时,我在任何地方返回的值中都看不到它。

如果我能解决这个问题,那么这将使我向前迈进,因为我所有的底层代码都在标准控制台版本中工作,因此我只需要通过对GUI表单的更改来适应它即可。

0 个答案:

没有答案