2013-03-19 23 views
4

我一直在寻找一种方法来传递不同运行空间之间的事件,但还没有找到任何。下面的剪切片段创建了一个背景运行空间,它只显示一个只有一个按钮的小窗口。 OnClick它应该发布主运行空间应该收到的事件:powershell多运行空间事件传递

$Global:x = [Hashtable]::Synchronized(@{}) 
$x.Host = $Host 
$Global:rs = [RunspaceFactory]::CreateRunspace() 
$rs.ApartmentState,$rs.ThreadOptions = "STA","ReUseThread" 
$rs.Open() 
$rs.SessionStateProxy.SetVariable("x",$x) 
$Global:cmd = [PowerShell]::Create().AddScript(@' 
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase 
$x.w = [Windows.Markup.XamlReader]::Parse(@" 
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
MaxWidth="800" WindowStartupLocation="CenterScreen" WindowStyle="None" SizeToContent="WidthAndHeight"> 
<Button Name="test" Content="Starte Installation"/> 
</Window> 
"@) 
$x.test = $x.w.Content.FindName('test') 
$x.test.Add_Click({New-Event -SourceIdentifier "TestClicked" -MessageData "test event"}) 
$x.w.ShowDialog() 
'@) 
$cmd.Runspace = $rs 
$null = $cmd.BeginInvoke() 
while(!($x.ContainsKey("test"))) {Sleep -Milliseconds 500} 
Register-EngineEvent -SourceIdentifier "TestClicked" -Action {$event} 

但是这并不奏效。我把最后一行改为:

$x.test.Add_Click({$x.Host.Runspace.Events.GenerateEvent("TestClicked", $x.test, $null, "test event") }) 
$x.w.ShowDialog() 
'@) 
$cmd.Runspace = $rs 
$null = $cmd.BeginInvoke() 
Wait-Event -SourceIdentifier "TestClicked" 

......这也没有工作。我想因为我不能从Child-RS内的父RS调用函数。奇怪的是,我有一些Get-Event返回一些“TestClicked”事件的情况,但我不记得也没有重现...

编辑:显然上面的工作方式 - 我刚刚遇到我的问题,这是结合一些功能。大多数人都知道脚本专家在Powershell-BLog上发布的显示控制功能。正如我宁愿显示整个图形用户界面,而不是单一的控制的,我修改了它这样的:

Add-Type –assemblyName PresentationFramework,PresentationCore,WindowsBase,"System.Windows.Forms" 

<# Die folgende Funktion zeigt eine GUI an. Die Informationen über die GUI 
    müssen in XAML formuliert sein. Sie können als String oder als Dateiname 
    übergeben werden. 
    Die Funktion erlaubt die Übergabe von WindowProperties als Hashtable 
    (-> siehe [System.Windows.Window]), von gemeinsamen Objekten in einer syn- 
    chronized HashTable und von Ereignissen, die mit den entsprechenden im xaml 
    definierten Objekten verbunden werden. 
    Der Switch "backgroundrunspace" macht, was sein Name sagt: er öffnet die GUI 
    im Hintergrund, sodass das Hauptprogramm weiterlaufen kann. 
#> 
function Show-Control { 
    param(
     [Parameter(Mandatory=$true,ParameterSetName="XamlString",ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 
     [string] $xaml, 

     [Parameter(Mandatory=$true,ParameterSetName="XamlFile",ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] 
     [string] $xamlFile, 

     [Parameter(ValueFromPipelineByPropertyName=$true)] 
     [Hashtable] $event, 

     [Parameter(ValueFromPipelineByPropertyName=$true)] 
     [Hashtable] $windowProperties, 

     # If this switch is set, Show-Control will run the control in the background runspace 
     [switch] $backgroundRunspace, 

     # To share Variables with the background runspace 
     [Parameter(ValueFromPipelineByPropertyName=$true)] 
     [Hashtable] $sharedVariables 
    ) 
    Begin 
    { # If it's in a background runspace, create a runspace and populate the runspace with Show-Control. 
     if ($backgroundRunspace) { 
      $newRunspace =[RunspaceFactory]::CreateRunspace() 
      $newRunspace.ApartmentState,$newRunspace.ThreadOptions = "STA","ReuseThread" 
      $newRunspace.Open() 
      $newRunspace.SessionStateProxy.SetVariable("ParentHost",$Host) 
      if ($sharedVariables) { 
       $newRunspace.SessionStateProxy.SetVariable("sharedVariables",$sharedVariables) 
      } 
      $selfDefinition = "function Show-Control { $((Get-Command Show-Control).Definition) }" 
      $psCmd = [PowerShell]::Create().AddScript($selfDefinition, $false) 
      $psCmd.Runspace = $newRunspace 
      $null = $psCmd.Invoke() 
     } else { 
      $window = New-Object Windows.Window 
      $window.SizeToContent = "WidthAndHeight" 
      # das Fenster in die sharedVariables aufnehmen 
      if ($sharedVariables) { 
       $sharedVariables.window=$window 
      } 
      if ($windowProperties) { 
       foreach ($kv in $windowProperties.GetEnumerator()) { 
        $window."$($kv.Key)" = $kv.Value 
       } 
      } 
      $visibleElements = @() 
      $windowEvents = @() 
     } 
    } 
    Process 
    { 
     if ($backgroundRunspace) { # Invoke the command, using each parameter from commandlineparameters 
      $psCmd = [Powershell]::Create().AddCommand("Show-Control",$false) 
      $null = $psBoundParameters.Remove("BackgroundRunspace") 
      $null = $psCmd.AddParameters($psBoundParameters) 
<#   foreach ($namedArg in $psBoundParameters.GetEnumerator()) { 
       $null = $psCmd.AddParameter($namedArg.Key, $namedArg.Value)              
      }#> 
      $psCmd.Runspace = $newRunspace 
      $null = $psCmd.BeginInvoke() 
     } else { 
      # falls eine xaml-datei, dann diese in den xaml-string laden 
      if($PSCmdlet.ParameterSetName -eq "xamlFile") { 
       $xaml = [string](Get-Content -Encoding UTF8 -ReadCount 0 -Path $xamlFile) 
      } 
      # XAML parsen und so zu Objekten machen 
      $window.Content=([system.windows.markup.xamlreader]::parse($xaml)) 
      # wir merken uns, ob wir ein Loaded-Event verknüpft haben 
      $guiloaded_notadded = $true 
      # event-hashtable parsen 
      if($event) { 
       foreach ($singleEvent in $event.GetEnumerator()) { 
        if ($singleEvent.Key.Contains(".")) { 
         # auseinander nehmen von Objektname und Eventname 
         $targetName = $singleEvent.Key.Split(".")[0].Trim() 
         $eventName = $singleEvent.Key.Split(".")[1].Trim() 
         if ($singleEvent.Key -like "Window.*") { 
          $target = $window 
         } else { 
          $target = $window.Content.FindName($targetName)     
         }      
        } else { # kein Objektname -> das Fenster selbst ist das Objekt... 
         $target = $window 
         $eventName = $singleEvent.Key 
        } 
        # Prüfe, ob dieses Objekt auch dieses Event unterstützt, wenn ja: Skriptblock mit dem Event verheiraten 
        if(Get-Member -InputObject $target -MemberType Event -Name $eventName) { 
         $eventMethod = $target."add_$eventName" 
         if(($targetName -eq "Window") -and ($eventName -eq "Loaded") -and ($ParentHost)) { 
          $eventScript = [ScriptBlock]::Create($singleEvent.Value.ToString() + "`n`$null = `$ParentHost.Runspace.Events.GenerateEvent('GUIloaded',$null,$null,$null)") 
          $eventMethod.Invoke($ExecutionContext.InvokeCommand.NewScriptBlock($eventScript)) 
          $guiloaded_notadded = $false 
         } else { 
          $eventMethod.Invoke($ExecutionContext.InvokeCommand.NewScriptBlock($singleEvent.Value)) 
         } 
        } 
       } 
      } 
      # wenn background (können wir hier nur durch Abfragen von "ParentHost" prüfen) und kein "Loaded" event, 
      # dann das GUIloaded-event mit dem window.loaded event senden. 
      if(($guiloaded_notadded) -and ($ParentHost)) { 
       $window.add_Loaded({ 
        $null = $ParentHost.Runspace.Events.GenerateEvent('GUIloaded',$null,$null,$null) 
       }) 
      } 
      # benannte xaml-Objekte in die sharedVariables bringen... 
      if($sharedVariables) { 
       $match = [regex]::Matches($xaml,' [x]?[:]?Name="(\w+)"') 
       foreach ($m in $match) 
       { 
        $name = [string]($m.Groups[1].Value) 
        $sharedVariables.Add($name,$window.Content.FindName($name)) 
       } 
      } 
     } 
    } 
    End 
    { 
     if ($backgroundRunspace) { 
      $newRunspace 
     } else { 
      $null = $window.ShowDialog() 
      $window.Tag 
      if($ParentHost) { 
       $null = $ParentHost.Runspace.Events.GenerateEvent('WindowClosed',$null,$null,$window.Tag) 
      } 
     } 
    } 
} 

我德语注释遗憾。

现在使用此函数(它也使用发送“GUIloaded”和“WindowClosed”事件的技术)在函数调用中使用“GuI事件”,似乎不可能从gui-事件。像这样:

Show-Control -xamlfile ($PSScriptRoot+"\WimMounter.xaml") -backgroundRunspace -sharedVariables $ui -event @{ 
    "Loaded" = { 
     $Global:fdlg = New-Object System.Windows.Forms.OpenFileDialog 
     $fdlg.CheckFileExists = $true 
     $fdlg.Filter = "WIM-Image Files|*.wim" 
     $fdlg.Title = "Bitte WIM-Datei auswählen" 

     $Global:ddlg = New-Object System.Windows.Forms.FolderBrowserDialog 
     $ddlg.Description = "Bitte Verzeichnis zum Mounten des Images auswählen" 
     $ui.fn = "" 
     $ui.in = "" 
     $ui.md = "" 
    } 
    "selectFile.Click" = { 
     if($Global:fdlg.ShowDialog() -eq "OK") { 
      $sharedVariables.ImageFile.Text = $fdlg.FileName.Trim() 
      $sharedVariables.pl.Content = ("Ausgewählt: `""+$fdlg.FileName.Trim()+"`" - wird untersucht...") 
      $sharedVariables.pb.IsIndeterminate = $true 
      $sharedVariables.ImageName.Items.Clear() 
      $ParentHost.UI.WriteLine("gleich gibbs 'ImageSelected'") 
      $ParentHost.Runspace.Events.GenerateEvent("ImageSelected",$null,$null,($fdlg.FileName.Trim())) 
     } 
    } 
} 

需要注意的是$ ui是一个全局的SyncHasTable。奇怪的是,那些“$ ParentHost.UI.WriteLine()”调用在父控制台上工作并产生输出。 “GenerateEvent”呼叫似乎根本不起作用。 Get-Event不会显示任何事件,也不会触发通过Register-EngineEvent设置的操作。

对此的任何想法?

回答

9

我能收到使用下面的代码父运行空间事件:

$Global:x = [Hashtable]::Synchronized(@{}) 
$x.Host = $Host 
$rs = [RunspaceFactory]::CreateRunspace() 
$rs.ApartmentState,$rs.ThreadOptions = "STA","ReUseThread" 
$rs.Open() 
$rs.SessionStateProxy.SetVariable("x",$x) 
$cmd = [PowerShell]::Create().AddScript({ 
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase 
$x.w = [Windows.Markup.XamlReader]::Parse(@" 
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
MaxWidth="800" WindowStartupLocation="CenterScreen" WindowStyle="None" SizeToContent="WidthAndHeight"> 
<Button Name="test" Content="Starte Installation"/> 
</Window> 
"@) 
$x.test = $x.w.FindName('test') 

$x.test.Add_Click({ 
    $x.Host.Runspace.Events.GenerateEvent("TestClicked", $x.test, $null, "test event") 
}) 

$x.w.ShowDialog() 
}) 
$cmd.Runspace = $rs 
$handle = $cmd.BeginInvoke() 
Register-EngineEvent -SourceIdentifier "TestClicked" -Action {$Global:x.host.UI.Write("Event Happened!")} 
+0

奇怪的是,我已经尝试过这种方式,但几乎没有可重现的结果。以某种方式复制并粘贴到ISE并运行它运行良好。我会再次尝试与我目前的项目(它在WindowsPE内;) – exomium 2013-03-21 13:19:05

+0

顺便说一句。它真的很好,即使在Windows PE中 - 非常感谢你,Boe! – exomium 2013-03-22 13:26:47

+0

感谢您的Boe! – slashp 2014-05-16 13:35:02

相关问题