WPF 运行空间使用 powershell 版本 2 崩溃

WPF runspace crashes using powershell version 2

我正在编写一个 powershell 脚本,用于安装各种驱动程序。

我的脚本工作正常,所以我想使用 WPF 添加一个图形用户界面。 首先,我使用 WPF 创建了一个图形用户界面,没什么了不起的,只是一个带有标签的 window。

我想从我的安装脚本更新这个标签。所以我创建了两个运行空间,一个创建并显示 WPF gui,另一个执行我的安装脚本。 只要我使用 powershell 版本 3 或更高版本,它就可以正常工作。使用 powershell 2,我不得不在新的 Windows 7 安装中使用它,wpf 运行空间崩溃了。

我希望有办法让它与 powershell 版本 2 一起工作。

这是一个示例脚本,展示了我在做什么。

#########################################################################################
#
#   W P F - R U N S P A C E
#
#########################################################################################
$syncHashWpfLuaNotification = [hashtable]::Synchronized(@{})
$runspaceWpfLuaNotification =[runspacefactory]::CreateRunspace()
$runspaceWpfLuaNotification.ApartmentState = "STA"
$runspaceWpfLuaNotification.ThreadOptions = "ReuseThread"         
$runspaceWpfLuaNotification.Open()
$runspaceWpfLuaNotification.SessionStateProxy.SetVariable("syncHashWpfLuaNotification",$syncHashWpfLuaNotification)          
$psCmd = [PowerShell]::Create().AddScript({   
    [void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
    [xml]$xaml = @"
<Window 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TreiberInstaller" Height="431" Width="626" Background="Black"
        WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="2*" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*" />
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>

        <Label Name="lblProgress" Content="Progress"  HorizontalAlignment="Center" Grid.Column="1" Grid.Row="2" VerticalAlignment="Top" Foreground="White" FontFamily="Calibri" FontSize="18" HorizontalContentAlignment="Center"/>

    </Grid>
</Window>
"@
    $reader=(New-Object System.Xml.XmlNodeReader $xaml)
    $syncHashWpfLuaNotification.Window = [Windows.Markup.XamlReader]::Load( $reader )
    $syncHashWpfLuaNotification.lblProgress = $syncHashWpfLuaNotification.window.FindName("lblProgress")
    $syncHashWpfLuaNotification.Window.ShowDialog() | Out-Null
    $syncHashWpfLuaNotification.Error = $Error
})
$psCmd.Runspace = $runspaceWpfLuaNotification
$data = $psCmd.BeginInvoke()
Sleep -Milliseconds 450 # Wait a moment that the gui is ready




#########################################################################################
#
#   W O R K E R - R U N S P A C E
#
#########################################################################################
$ScriptBlock = { 
    #----------------------------------------------------------------------
    #   SetLabelText: Sets the lable-text
    #----------------------------------------------------------------------
    function SetLabelText
    {
        param(
                [Parameter(Position=0, Mandatory = $true, ValueFromPipeline = $false)]
                [ValidateNotNullOrEmpty()]
                [string]$Text
            )

        if(-not $syncHashWpfLuaNotification) {return}

        try
        {
            $syncHashWpfLuaNotification.Window.Dispatcher.invoke(
                    [action]{$syncHashWpfLuaNotification.lblProgress.Content = $Text},
                    "Normal"
            )
        }
        catch {}
    }

    #----------------------------------------------------------------------
    #   CloseProgressWindow: Closes the window
    #----------------------------------------------------------------------
    function CloseProgressWindow()
    {
        if(-not $syncHashWpfLuaNotification) {return}

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



    #Starting here
    SetLabelText -Text "Starting installation..."

    Sleep 2

    for($i=1;$i -le 19; $i++)
    {
        SetLabelText -Text ("Progress Step " + $i)
    }

    for($i=20;$i -le 24; $i++)
    {
        SetLabelText -Text ("Progress Step " + $i)
        Sleep 1
    }

    CloseProgressWindow
} #End of $ScriptBlock


$syncHash1 = [hashtable]::Synchronized(@{})
$workerRunspace =[runspacefactory]::CreateRunspace()
$workerRunspace.ApartmentState = "STA"
$workerRunspace.ThreadOptions = "ReuseThread"         
$workerRunspace.Open()
$workerRunspace.SessionStateProxy.SetVariable("syncHash1",$syncHash1)          
$workerRunspace.SessionStateProxy.SetVariable("syncHashWpfLuaNotification",$syncHashWpfLuaNotification)          
$psCmd1 = [PowerShell]::Create().AddScript($ScriptBlock)
$psCmd1.Runspace = $workerRunspace
$data = $psCmd1.BeginInvoke()






#########################################################################################
#
#   S C R I P T   E N D   
#
#########################################################################################

#Wait for end of both runspaces
while(($runspaceWpfLuaNotification.RunspaceAvailability -eq "Busy") -or ($workerRunspace.RunspaceAvailability -eq "Busy"))
{
    if($runspaceWpfLuaNotification.RunspaceAvailability -eq "Busy") { Write-Host "Window is open" }
    if($workerRunspace.RunspaceAvailability -eq "Busy") { Write-Host "Worker is running" }

    Sleep 1
}

Write-Host "Script ended"

您的通行证委托 Dispatcher.Invoke 调用的方式存在一些问题。

  • ScriptBlock 文字绑定到当前会话状态。如果将有界 ScriptBlock 传递给不同的 Runspace,则可能会导致一些不良影响,例如未在目标 Runspace 中调用的代码或死锁。有关此内容,请参阅 。因此,您需要做的第一件事是创建新的无界 ScriptBlock。你可以用 [ScriptBlock]::Create 方法做到这一点。
  • 由于 ScriptBlock 不再局限于当前会话状态,因此您不能从中引用变量,就像在代码中使用 $Text 一样。您应该将它们作为附加参数传递给 Dispatcher.Invoke.
  • 在将与 PowerShell v2 一起使用的 .NET Framework 3.5 中,Dispatcher class 中没有 Invoke(Action, DispatcherPriority) 重载,因此 Invoke([Action]{...}, "Normal") 将是改用此 Invoke(Delegate, Object[]) 方法。您应该使用不同的重载:Invoke(DispatcherPriority, Delegate),它存在于 .NET Framework 3.5 中。

尝试下载 WPFRunspace,它应该适用于 PS V2。它为基于 WPF 和 Forms 的脚本提供了后台工作者。