ProgressBar显示在ISE上,但不显示在控制台上。运行空间上的Powershell响应GUI

ProgressBar显示在ISE上,但不显示在控制台上。运行空间上的Powershell响应GUI,powershell,user-interface,progress-bar,responsive,runspace,Powershell,User Interface,Progress Bar,Responsive,Runspace,我一直在开发一个简单的脚本概念验证/模板,在这个脚本中,我有默认的运行空间来运行繁重的任务,还有一个运行响应GUI 我已经测试了各种方法,以便能够与运行空间进行通信。起初我尝试了Control.Invoke,但不同论坛上的一些观点和测试中的一种奇怪行为使我使用了基于消息的基于同步哈希表的双向通信。ProgressBar与control.Invoke一起工作,但是,执行一些其他操作(如禁用表单上的多个按钮)的速度非常慢 第一个问题:我想在任务执行时显示一个进度条(选框),改变它的可见状态。但是,当

我一直在开发一个简单的脚本概念验证/模板,在这个脚本中,我有默认的运行空间来运行繁重的任务,还有一个运行响应GUI

我已经测试了各种方法,以便能够与运行空间进行通信。起初我尝试了Control.Invoke,但不同论坛上的一些观点和测试中的一种奇怪行为使我使用了基于消息的基于同步哈希表的双向通信。ProgressBar与control.Invoke一起工作,但是,执行一些其他操作(如禁用表单上的多个按钮)的速度非常慢

第一个问题:我想在任务执行时显示一个进度条(选框),改变它的可见状态。但是,当脚本在ISE上运行时会显示progressbar,而在控制台上运行时不会显示progressbar。我认为这是因为主运行空间很忙,但我不明白它会如何影响GUI运行空间

第二:在我下面发布的脚本中,我通过堆栈变量使用scriptblocks在运行空间之间传递命令。然后,每个运行空间(主运行空间通过池和GUI通过计时器执行)检查任务是否在堆栈中等待执行,如果是,则执行它。但是,如果我想调用在另一个运行空间上声明的函数(本例中为TestOutOfMainScopeUIFunction),我不能。我会在scriptblock上得到一个运行时错误。我已经想到了这样的解决方案: -在两个运行空间上导入函数 -使函数成为全局函数还是使用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
    }
  }
}
找到了解决方案

必须首先启用VisualStyles。这个问题与运行空间无关。这是一个简单而清晰的代码示例,它来自于修复程序

Add-Type -AssemblyName System.Windows.Forms

[System.Windows.Forms.Application]::EnableVisualStyles()

$window = New-Object Windows.Forms.Form
$window.Size = New-Object Drawing.Size @(400,75)
$window.StartPosition = "CenterScreen"
$window.Font = New-Object System.Drawing.Font("Calibri",11,[System.Drawing.FontStyle]::Bold)
$window.Text = "STARTING UP"

$ProgressBar1 = New-Object System.Windows.Forms.ProgressBar
$ProgressBar1.Location = New-Object System.Drawing.Point(10, 10)
$ProgressBar1.Size = New-Object System.Drawing.Size(365, 20)
$ProgressBar1.Style = "Marquee"
$ProgressBar1.MarqueeAnimationSpeed = 20
$window.Controls.Add($ProgressBar1)

$window.ShowDialog()

这是同样的问题。将代码复制并粘贴到ISE和works中。在powershell上,不会: