Вывести функцию пространства выполнения PowerShell в текстовое поле WPF - PullRequest
0 голосов
/ 01 ноября 2018

У меня проблемы с получением функции "sync-folder" для вывода в текстовое поле во время его работы. Я пытался просмотреть множество руководств по сети, включая поиск здесь, и я не могу понять это. Я надеюсь, что кто-то, кто понимает это лучше меня, сможет показать мне, как это сделать, в моем тестовом примере ниже:

$Global:syncHash = [hashtable]::Synchronized(@{})
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)

# Load WPF assembly if necessary
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')

$psCmd = [PowerShell]::Create().AddScript({
    [xml]$xaml = @"
    <Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="TestApp" Height="450" Width="800">
    <Grid>
        <Button x:Name="runButton" Content="Run" HorizontalAlignment="Left" Margin="293,37,0,0" VerticalAlignment="Top" Width="189" Height="40"/>
        <TextBox x:Name="textbox" TextWrapping="NoWrap" FontFamily="Consolas" ScrollViewer.VerticalScrollBarVisibility="Auto" IsReadOnly="True" Margin="10,137,10,10"/>

    </Grid>
</Window>
"@

    # 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) )
    }

    $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

#=========================================
#=========================================
#=========================================
#================= runButton =============

$syncHash.runButton.Add_Click({
    $newRunspace =[runspacefactory]::CreateRunspace()
    $newRunspace.ApartmentState = "STA"
    $newRunspace.ThreadOptions = "ReuseThread"
    $newRunspace.Open()
    $newRunspace.SessionStateProxy.SetVariable("SyncHash",$SyncHash)
    $PowerShell = [PowerShell]::Create().AddScript({
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")
}

#============== START stuff to do =================

function sync-folder {
function timestamp {
    $timestamp = "$(Get-Date -f 'yyyy-MM-dd HH:mm:ss:fff')"
    Write-Output "$timestamp "
}

$MainLog = "C:\ProgramData\test\Logs\Main_Log.txt"
$SyncLog = "C:\ProgramData\test\Logs\Sync_Log.txt"
$RemoteSync = "\\remote\sync\path\test"
$RobocopySource = "\\remote\location\"
$RobocopyDestination = "C:\ProgramData\test"

# Check if Log exists...
# If not exist, then create it:
If (!(Test-Path $MainLog)) {
    # Create MainLog.txt
    New-Item -ItemType File -Force $MainLog

    # Write to log:
    Write-Output "$(timestamp) Main Log File has been created." >> $MainLog
}

If (!(Test-Path $SyncLog)) {
    # Create MainLog.txt
    New-Item -ItemType File -Force $SyncLog

    # Write to log:
    Write-Output "$(timestamp) Sync Log File has been created." >> $SyncLog
}

if (Test-Path $RemoteSync) {
    Write-Output "$(timestamp) Sync location is reachable. Starting sync..." >> $MainLog
    Write-Output "$(timestamp) Sync location is reachable. Starting sync..." >> $SyncLog
    robocopy $RobocopySource $RobocopyDestination /MIR /FFT /R:3 /W:10 /NP /NDL /UNILOG+:$SyncLog
    Write-Output "$(timestamp) Sync complete. Check $SyncLog for details." >> $MainLog
    Write-Output "$(timestamp) Sync complete." >> $SyncLog
    exit
} else {
    Write-Output "$(timestamp) Sync location is NOT reachable. Synchronization aborted..." >> $MainLog
    Write-Output "$(timestamp) Exiting SYNC Task..." >> $MainLog
    Write-Output "$(timestamp) Sync location NOT reachable. Synchronization aborted..." >> $SyncLog
    Write-Output "$(timestamp) Exiting SYNC Task..." >> $SyncLog
    exit
}
}

sync-folder

#============== END stuff to do ===================

    })
    $PowerShell.Runspace = $newRunspace
    [void]$Jobs.Add((
        [pscustomobject]@{
            PowerShell = $PowerShell
            Runspace = $PowerShell.BeginInvoke()
        }
    ))
})

    $syncHash.Window.Add_Closed({
        Write-Verbose 'Halt runspace cleanup job processing'
        $jobCleanup.Flag = $False

        #Stop all runspaces
        $jobCleanup.PowerShell.Dispose()
    })

    $syncHash.Window.ShowDialog() | Out-Null
    $syncHash.Error = $Error
})

#=====================================
# Shows the form
#=====================================
$psCmd.Runspace = $newRunspace
$data = $psCmd.BeginInvoke()
...