Events powershell多运行空间事件传递

Events powershell多运行空间事件传递,events,powershell,inter-process-communicat,runspace,Events,Powershell,Inter Process Communicat,Runspace,我一直在寻找在不同运行空间之间传递事件的方法,但还没有找到任何方法。以下截图创建了一个背景运行空间,其中显示了一个只有一个按钮的小窗口。单击后,它将发布主运行空间应接收的事件: $Global:x = [Hashtable]::Synchronized(@{}) $x.Host = $Host $Global:rs = [RunspaceFactory]::CreateRunspace() $rs.ApartmentState,$rs.ThreadOptions = "STA","ReUseTh

我一直在寻找在不同运行空间之间传递事件的方法,但还没有找到任何方法。以下截图创建了一个背景运行空间,其中显示了一个只有一个按钮的小窗口。单击后,它将发布主运行空间应接收的事件:

$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}
。。。这也没用。我猜,因为我无法从Child-RS中的父RS调用函数。奇怪的是,在某些情况下,Get事件返回了一些“TestClicked”事件,但我无法回忆或再现

编辑:显然,上面的工作方式-我只是再次遇到我的问题,这是与一些功能相结合。大多数人都知道Scripting Guy在Powershell博客上发布的Show Control函数。由于我宁愿显示整个GUI而不是单个控件,因此我对其进行了如下修改:

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)
            }
        }
    }
}
应该注意,$ui是一个全局同步表。奇怪的是,“$ParentHost.UI.WriteLine()”调用在父控制台上工作并产生输出。“GenerateEvent”调用似乎根本不起作用。Get事件既不显示任何事件,也不触发通过Register EngineeEvent设置的操作


有什么想法吗?

这里有一篇关于使用同步哈希表在运行空间之间传递数据的文章,如果有帮助的话:


我能够使用以下代码在父运行空间上接收事件:

$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!")}
$Global:x=[Hashtable]::Synchronized(@{})
$x.Host=$Host
$rs=[RunspaceFactory]::CreateRunspace()
$rs.ApartmentState,$rs.ThreadOptions=“STA”,“ReUseThread”
$rs.未结()
$rs.SessionStateProxy.SetVariable(“x”,$x)
$cmd=[PowerShell]::Create().AddScript({
添加类型-AssemblyName PresentationFramework、PresentationCore、WindowsBase
$x.w=[Windows.Markup.XamlReader]::解析(@)
"@)
$x.test=$x.w.FindName('test'))
$x.test.Add\u单击({
$x.Host.Runspace.Events.GenerateEvent(“TestClicked”、$x.test、$null,“测试事件”)
} )  
$x.w.ShowDialog()
})
$cmd.Runspace=$rs
$handle=$cmd.BeginInvoke()
注册EngineeEvent-SourceIdentifier“TestClicked”-操作{$Global:x.host.UI.Write(“事件发生了!”)}

谢谢,这没用。如果你读过我的文章,你会发现我已经在使用SyncHashTable(第一行代码)。这不是关于交换数据,而是关于交换事件而不必主动轮询。但无论如何还是要谢谢你。奇怪的是,我已经尝试过了,结果几乎无法重现。不知何故,将剪下的部分复制粘贴到ISE并运行起来效果很好。我将在我当前的项目中再试一次(它在WindowsPE中),顺便说一句。它真的工作得很好,即使在WindowsPE中也是如此-非常感谢你,Boe!哇!这真漂亮。非常感谢。
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()))
        }
    }
}
$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!")}