Я работал над простым доказательством концепции / шаблона скрипта, в котором у меня по умолчанию есть Runspace для выполнения тяжелых задач и еще один для запуска адаптивного графического интерфейса.
Я проверил различные методы для связи пространств выполнения.Сначала я попробовал Control.Invoke, но некоторые мнения на разных форумах и странное поведение на тестах заставили меня использовать основанную на сообщениях внутреннюю коммуникацию на основе синхронизированной HashTable.ProgressBar работает с control.Invoke, но выполнение некоторых других действий, таких как отключение нескольких кнопок в форме, выполняется очень медленно.
1-я проблема: я хотел бы показать индикатор выполнения (выделение), когда задача выполняется, изменяя ее видимое состояние.Однако индикатор выполнения отображается, когда скрипт работает на ISE, а не когда он работает на консоли.Я думаю, это потому, что основное пространство выполнения занято, но я не понимаю, как это может повлиять на пространство выполнения GUI ...
2-й: в скрипте, который я публикую ниже, я использую блоки скриптов через переменную стекапередавать команды между пространствами выполнения.Затем каждое пространство выполнения (основное пространство выполнения делает это с помощью пула, а GUI - через таймер) проверяет, ожидает ли задача выполнения в стеке, и, если это так, выполняет ее.Однако, если бы я захотел вызвать функцию, объявленную в другом пространстве выполнения (в этом примере Test-OutOfMainScopeUiFunction), я бы не смог.Я получил бы ошибку во время выполнения на блоке сценария.Я думал, что решения для этого, как: -Импортировать функции в обоих пространствах выполнения -Сделать функции глобальными или использовать делегаты functon¿?-Передача строки с командами для выполнения, несмотря на блок скрипта -> Использование этой в данный момент, но не очень нравится ... склонность к ошибкам
Любое решение проблемы с индикатором выполнения или сценарияулучшения будут оценены.Спасибо!
$global:x = [Hashtable]::Synchronized(@{})
$global:x.MainDispatcher = new-object system.collections.stack
$global:x.UiDispatcher = new-object system.collections.stack
$global:x.GuiExited = $false
$formSB = {
[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
[reflection.assembly]::LoadWithPartialName("System.Drawing")
Function Test-OutOfMainScopeUiFunction
{
$x.Host.Ui.WriteLine("Test-OutOfMainScopeUiFunction Executed")
}
Function Execute-OnMainRs
{
param(
[Parameter(Mandatory=$true)]
[ScriptBlock]$ScriptBlock
)
$x.Host.Ui.WriteLine("`r`nAdding task")
$x.MainDispatcher.Push($ScriptBlock)
$x.Host.Ui.WriteLine("Task added")
$x.Host.Ui.WriteLine("Task: $($x.MainDispatcher)")
}
$form = New-Object System.Windows.Forms.Form
$button = New-Object System.Windows.Forms.Button
$button.Text = 'click'
$button.Dock = [System.Windows.Forms.DockStyle]::Right
$progressBar = (New-Object -TypeName System.Windows.Forms.ProgressBar)
$ProgressBar.Style = [System.Windows.Forms.ProgressBarStyle]::Marquee
$ProgressBar.MarqueeAnimationSpeed = 50
$ProgressBar.Dock = [System.Windows.Forms.DockStyle]::Bottom
$ProgressBar.Visible = $false
$label = New-Object System.Windows.Forms.Label
$label.Text = 'ready'
$label.Dock = [System.Windows.Forms.DockStyle]::Top
$timer=New-Object System.Windows.Forms.Timer
$timer.Interval=100
$timer.add_Tick({
if($x.UiDispatcher.Count -gt 0)
{
& $($x.UiDispatcher.Pop())
}
})
$form.Controls.add($label)
$form.Controls.add($button)
$form.Controls.add($progressBar)
Add-Member -InputObject $form -Name Label -Value $label -MemberType NoteProperty
Add-Member -InputObject $form -Name ProgressBar -Value $progressBar -MemberType NoteProperty
$button.add_click({
Execute-OnMainRs -ScriptBlock {
write-host "MainRS: Long Task pushed from the UI started on: $(Get-Date)"
start-sleep -s 2
write-host "MainRS: Long Task pushed from the UI finished on: $(Get-Date)"
}
})
$form.add_closed({ $x.GuiExited = $true })
$x.Form = $form
$timer.Start()
$form.ShowDialog()
}
Function Execute-OnRs
{
param(
[Parameter(Mandatory=$true)]
[ScriptBlock]$ScriptBlock
)
$x.Host = $Host
$rs = [RunspaceFactory]::CreateRunspace()
$rs.ApartmentState,$rs.ThreadOptions = "STA","ReUseThread"
$rs.Open()
$rs.SessionStateProxy.SetVariable("x",$x)
$ps = [PowerShell]::Create().AddScript($ScriptBlock)
$ps.Runspace = $rs
$handle = $ps.BeginInvoke()
#Almacenar variables del RS
$x.Handle = $handle
$x.Ps = $ps
}
Function Execute-OnUiRs
{
param(
[Parameter(Mandatory=$true)]
[ScriptBlock]$ScriptBlock
)
$x.UiDispatcher.Push($ScriptBlock)
}
Function Dispatch-PendingJobs
{
while($global:x.GuiExited -eq $false) {
if($global:x.MainDispatcher.Count -gt 0)
{
Execute-OnUiRs -ScriptBlock {
$msg = "UIRS: MainThread informs: Long Task started on $(Get-Date)."
$global:x.Form.Label.Text = $msg
$global:x.Form.ProgressBar.Visible = $true
$x.host.ui.WriteLine($msg)
#Next line throws an error visible on UI runspace error stream
Test-OutOfMainScopeUiFunction
}
& $($global:x.MainDispatcher.Pop())
Execute-OnUiRs -ScriptBlock {
$msg = "UIRS: MainThread informs: Long Task finished on $(Get-Date)."
$global:x.Form.Label.Text = $msg
$global:x.Form.ProgressBar.Visible = $false
$x.host.ui.WriteLine($msg)
}
write-host "UI Streams: $($global:x.Ps.Streams |out-string)"
}
else
{
start-sleep -m 100
}
}
}