使用PowerShell版本2
问题描述:
WPF运行空间崩溃我正在写一个PowerShell脚本,这installes各种驱动程序。使用PowerShell版本2
我的脚本工作正常,所以我要添加使用WPF的GUI。 首先,我使用WPF创建了一个gui,没有什么特别的,只是一个带有标签的窗口。
我想更新从我的安装脚本此标签。所以我创建了两个运行空间,一个创建并显示WPF gui,另一个执行我的安装脚本。 只要我使用的是PowerShell版本3或更高版本,就可以正常工作。对于必须使用新的Windows 7安装的powershell 2,wpf运行空间会崩溃。
我希望有一种方式来获得这种使用PowerShell版本2
下面是一个示例脚本工作,demonstating我在做什么。
#########################################################################################
#
# 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
或死锁不被调用。关于这个请看my other answer。所以,你需要做的第一件事是创建新的不受限制的ScriptBlock
。你可以用[ScriptBlock]::Create
的方法做到这一点。 - 由于
ScriptBlock
不再一定到当前会话状态,你不能从它指变量,就像你在你的代码$Text
做。您应该将它们作为附加参数传递给Dispatcher.Invoke
。 - 在.NET Framework 3.5中,它将与PowerShell v2一起使用,因此在
Dispatcher
类中没有Invoke(Action, DispatcherPriority)
过载,因此Invoke([Action]{...}, "Normal")
将被解析为此Invoke(Delegate, Object[])
方法。您应该使用不同的重载:Invoke(DispatcherPriority, Delegate)
,它存在于.NET Framework 3.5中。
'[action] {$ syncHashWpfLuaNotification.lblProgress.Content = $ Text},“Normal” - >'“Normal”,[action [object]] [scriptblock] :: create({$ syncHashWpfLuaNotification.lblProgress.Content = $ args [0]}),$ Text'; '[动作] {$ syncHashWpfLuaNotification.Window.Close()}, “正常”' - >' “正常”,[动作] [脚本块] ::创建({$ syncHashWpfLuaNotification.Window.Close()})'。 – PetSerAl
是的,就是这样!这适用于PowerShell 2.0。非常感谢。你可以发表这个答案,所以我可以将其标记为正确答案。 – CrazyMetal