Я начал думать о пространствах выполнения в powershell и Я смог сделать следующее
Проблема в одной из кнопок (BTN_Exit)...
Я хотел бы добиться этого при нажатии, он не только закрывает графический интерфейс, но и закрывает RunSpace, где он работает. Я дошел до того, что мне удалось закрыть графический интерфейс и даже вызвать последовательность закрытия для Runspace, но (я предполагаю), поскольку вызываемая функция выполняется в самом RunSpace, она просто зависает (RunSpace остается в «закрытии» состояние) -Очевидно, Powershell не может убить себя :) К счастью, я все еще могу удалить его из основного потока с помощью $(Get-Runspace)[-1].Dispose() (вручную)
Я считаю, что мне нужно подключиться к основному потоку, где было создано GUI Runspace, и закрыть его оттуда, но я не могу вернуться туда в функции. Вручную, если окно открыто, я могу выполнить все командлеты в функции close-runspace, и это достигает желаемой цели.
Я пытался добавить $rs.connect($(Get-Runspace).Name -eq "Runspace1") и $RS.disconnect(). Если я попробую то же самое с «дескриптором» или экземпляром, это даст тот же результат. Я также не смог вызвать исходную функцию при нажатии кнопки без передачи функции в RunSpace.
Как мне программно дойти до того, что при нажатии кнопки он закрывает графический интерфейс и освобождает пространство выполнения?
Вот код:
#===[___VARIABLES___]===
$GUI = [hashtable]::Synchronized(@{})
$GUI.Host = $host
#===[___FUNCTIONS___]===
function Close-RunSpace {
if (!($RSI01H.Iscompleted)){
$GUI.BS.Dispatcher.invoke([action]{
$GUI.BS.Close()
})
}
$RSI01.EndInvoke($RSI01H)
$RS.Close()
$RS.Dispose()
}
#===[__RunSpaceCfg__]===
$RS_ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$RS_ISS.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'Close-RunSpace', (Get-Content Function:\Close-RunSpace -ErrorAction Stop)))
$RS = [runspacefactory]::CreateRunspace($RS_ISS)
$RS.ApartmentState = "STA"
$RS.ThreadOptions = "ReuseThread"
$RS.Open()
$RS.SessionStateProxy.SetVariable("GUI",$GUI)
$RS.SessionStateProxy.SetVariable("RS",$RS)
$RS.SessionStateProxy.SetVariable("RSI01",$RSI01)
$RS.SessionStateProxy.SetVariable("RSI01H",$RSI01H)
#===[___EXECUTION___]===
$RSI01 = [powershell]::Create().AddScript(
{
[void][System.Reflection.Assembly]::LoadWithPartialName("PresentationCore")
[void][System.Reflection.Assembly]::LoadWithPartialName("PresentationFramework")
[void][System.Reflection.Assembly]::LoadWithPartialName("WindowsBase")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Def_XAML = DATA {'
<Window x:Name = "BS" x:Class = "PST.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local = "clr-namespace:PST"
mc:Ignorable = "d"
Title = "BootStrapR"
Height = "450"
Width = "800"
ResizeMode = "NoResize"
Topmost = "True"
FontFamily = "Arial"
WindowStartupLocation = "CenterScreen"
WindowStyle = "None">
<Grid>
<Button x:Name = "BTN_Update"
Content = "Update"
HorizontalAlignment = "Left"
VerticalAlignment = "Top"
Margin = "109,352,0,0"
Width = "75"/>
<Button x:Name = "BTN_Exit"
Content = "exit"
HorizontalAlignment = "Left"
Margin = "431,352,0,0"
VerticalAlignment = "Top"
Width = "75"/>
<ProgressBar x:Name = "pb"
HorizontalAlignment = "Left"
Height = "29"
Margin = "10,150,0,0"
VerticalAlignment = "Top"
Width = "780"
Background = "White"
Foreground = "Black"
BorderBrush = "White"
Value = "0" />
<TextBlock x:Name = "pstext"
HorizontalAlignment = "Left"
Height = "266"
Margin = "26,22,0,0"
TextWrapping = "Wrap"
Text = "TextBlock"
VerticalAlignment = "Top"
Width = "223"/>
</Grid>
</Window>
'}
[xml]$XAML = $Def_XAML -replace 'x:Class=*.*','' `
-replace 'mc:Ignorable = "d"','' `
-replace "x:Name",'Name'
$WPF = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $XAML))
$XAML.SelectNodes("//*[@Name]") | ForEach-Object{
$GUI."$($_.Name)" = $WPF.FindName($_.Name)
}
$GUI.Error = $Error
$GUI.BTN_Exit.Add_Click({
#$GUI.BS.Close()
Close-RunSpace
})
$GUI.BTN_Update.Add_Click({
$GUI.pb.Value+=1
})
$GUI.BS.ShowDialog() | Out-Null
}
)
$RSI01.Runspace = $RS
$RSI01.Runspace.Name = "GUI"
$RSI01H = $RSI01.BeginInvoke()





О, я забыл об этом посте! Решение/обходной путь, который я нашел, находится в статье это.
По сути, локальный RunSpace также должен быть передан в RunSpace, где работает графический интерфейс. (Что я и сделал :)) И тогда могут быть вызваны события, вызывающие блоки функций/скриптов и т. д. в основном потоке. Затем этот триггер события выполняет хозяйственную работу и очищает/удаляет пространство выполнения GUI. ПРИМЕЧАНИЕ: само окно необходимо закрыть из GUI RunSpace / нажать кнопку @, в противном случае пространство выполнения не перейдет в состояние IsComplete, и вызываемая функция зависнет ( дождитесь завершения RS)
#===[___VARIABLES___]===
$Global:GUI = [hashtable]::Synchronized(@{})
$Global:VAR = [hashtable]::Synchronized(@{})
$VAR.Host = $Host
#===[______XAML_____]===
$XAML_BS = @"
<Window x:Name = "BootStrapper" x:Class = "SIT_SDM.Window"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local = "clr-namespace:SIT_SDM"
mc:Ignorable = "d"
Height = "450" Width = "800" Background = "#FF2F1333" WindowStyle = "None">
<Grid>
<Button Name = "BS_BTN_X" Content = "Close" Margin = "600,0,25,0" VerticalAlignment = "Top" Width = "75"/>
</Grid>
</Window>
"@
#===[___FUNCTIONS___]===
function BS_Close {
begin {
$Global:GUI.BootStrapper.Add_Closing({$_.Cancel = $true})
}
process {
if ($BS_Handle.IsCompleted) {
$BS.EndInvoke($BS_Handle)
$BS.RunSpace.Close()
$BS.RunSpace.Dispose()
} else {
Write-Error -Text "Runspace job not complete!"
}
Unregister-Event -SourceIdentifier "BS_Close"
}
end {
if ((Get-Runspace).count -ge 2) {
$(Get-Runspace)[-1].Dispose()
}
}
}
function Initialize-XAML {
[CmdletBinding()]
Param(
[Parameter()]
[string]$File,
[string]$Variable
)
[void][System.Reflection.Assembly]::LoadWithPartialName("PresentationCore")
[void][System.Reflection.Assembly]::LoadWithPartialName("PresentationFramework")
if (!([string]::IsNullOrEmpty($File))) {
$inputXAML = Get-Content -Path $File -ErrorAction Stop
} elseif (!([string]::IsNullOrEmpty($Variable))) {
$inputXAML = $Variable
} else {
#Write-Error "Neither File nor Variable has been declared"
break
}
#Clean XAML for PowerShell compatibilty
[xml]$XAML = $inputXAML -replace 'x:Class=*.*','' -replace 'mc:Ignorable = "d"','' -replace "x:Name",'Name'
#Create the XAML reader using XML node reader
$ReadR = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $XAML))
#Grab named objects from tree and put in a flat structure using Xpath
$NamedNodes = $XAML.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]")
$NamedNodes | ForEach-Object {
$Script:GUI.Add($_.Name, $ReadR.FindName($_.Name))
}
}
#===[___EXECUTION___]===
$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$Functions = Get-Command -Module SITSD #Script is being used with import-module
foreach($Function in $Functions){
$functionDefinition = Get-Content function:\$Function
$functionEntry = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $Function.Name, $functionDefinition
$InitialSessionState.Commands.Add($functionEntry)
}
$RS = [runspacefactory]::CreateRunspace($InitialSessionState)
$RS.ApartmentState = "STA"
$RS.ThreadOptions = "ReuseThread"
$RS.Open()
#Passing variables to Runspace
$RS.SessionStateProxy.SetVariable("GUI",$GUI)
$RS.SessionStateProxy.SetVariable("VAR",$VAR)
$RS.SessionStateProxy.SetVariable("XAML_BS",$XAML_BS)
Register-EngineEvent -SourceIdentifier "BS_Close" -Action {BS_Close}
$BS = [PowerShell]::Create().AddScript({
Initialize-XAML -Variable $XAML_BS
$GUI.BS_BTN_X.Add_Click({
$Global:VAR.host.UI.Write("Button pressed")
$GUI.BootStrapper.Close()
$Global:VAR.Host.Runspace.Events.GenerateEvent( "BS_Close", $GUI.BS_BTN_X, $null, "BS_Close")
})
$GUI.BootStrapper.Showdialog()|Out-Null
})
$BS.RunSpace = $RS
$BS_Handle = $BS.BeginInvoke()