異なる実行空間間でイベントを渡す方法を探していましたが、まだ見つかりませんでした。次の抜粋では、ボタンが 1 つしかない小さなウィンドウを表示するバックグラウンド ランスペースが作成されます。OnClick は、メインの実行空間が受け取るべきイベントを投稿します。
$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」イベントを返した状況がいくつかありましたが、思い出すことも再現することもできません...
編集:明らかに、上記は何らかの方法で機能します-いくつかの機能と関連して、私の問題に再び遭遇しました。ほとんどの人は、Scripting Guy が Powershell-BLog で公開している 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)
}
}
}
}
ドイツ語でコメントしてすみません。
関数呼び出しで "GuI-events" を使用してこの関数 ("GUIloaded" および "WindowClosed" イベントを送信する手法も使用) を使用すると、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 によって設定されたアクションもトリガーされません。
これに関するアイデアはありますか?