2013-03-19 20 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

相關問題