PowerShell:未执行表单的作业事件操作

时间:2011-09-14 09:36:13

标签: winforms events powershell jobs

如果我运行以下代码,则执行事件操作:

$Job = Start-Job {'abc'}
Register-ObjectEvent -InputObject $Job -EventName StateChanged `
    -Action {
             Start-Sleep -Seconds 1
             Write-Host '*Event-Action*'
            } 

显示字符串' Event-Action '。

如果我使用表单并通过单击按钮启动上述代码,

未执行事件操作:

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Form1 = New-Object Windows.Forms.Form
$Form1.Add_Shown({
   $Form1.Activate()
})
$Button1 = New-Object System.Windows.Forms.Button
$Button1.Text = 'Test'
$Form1.Controls.Add($Button1)
$Button1.Add_Click({
   Write-Host 'Test-Button was clicked'
   $Job = Start-Job {'abc'}
   Register-ObjectEvent -InputObject $Job -EventName StateChanged `
       -Action {
                Start-Sleep -Seconds 1
                Write-Host '*Event-Action*'
               }
})
$Form1.ShowDialog()

只有当我再次单击该按钮时,才会执行第一个事件操作。

第三次单击,执行第二个事件操作,依此类推。

如果我快速连续多次点击,结果是不可预测的。

此外,当我使用右上角的按钮关闭表单时,

执行最后一次“打开”事件操作。

注意:为了测试PowerShell ISE是首选,因为PS Console显示

仅在某些情况下才能使用该字符串。

有人可以告诉我这里发生了什么吗?

提前致谢!


nimizen。

感谢您的解释,但我真的不明白,为什么在对Form进行某些操作之前,StateChanged事件不会被触发或对主脚本可见。我很感激另一种向我解释的尝试。

我想要实现的是一种使用PowerShell和Forms的多线程。

我的计划如下:

该脚本向用户显示一个表单。

用户进行一些输入并单击按钮。 根据用户的输入,使用Start-Job启动一组作业,并为每个作业注册StateChanged事件。

当Jobs正在运行时,用户可以对表单执行任何操作(包括通过按钮停止作业),并在必要时重新绘制表单。

脚本会对Form或其子控件触发的任何事件做出反应。

此脚本也会对每个作业的StateChanged事件做出反应。

当发生StateChanged事件时,将检查每个作业的状态,如果所有作业都处于“已完成”状态,则使用Receive-Job获取作业的结果并显示给用户。

除了主脚本看不到StateChanged事件外,所有这一切都正常。

以上仍然是我最喜欢的解决方案,如果你有任何想法如何实现,请告诉我。

否则我很可能会采用一种解决方法,这至少会给用户带来多线程的感觉。它在以下示例中说明:

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Form1 = New-Object Windows.Forms.Form
$Form1.Add_Shown({
   $Form1.Activate()
})
$Button1 = New-Object System.Windows.Forms.Button
$Button1.Text = 'Test'
$Form1.Controls.Add($Button1)
$Button1.Add_Click({
   $Form1.Focus()
   Write-Host 'Test-Button was clicked'
   $Job = Start-Job {Start-Sleep -Seconds 1; 'abc'}
   Do {
      Start-Sleep -Milliseconds 100
      Write-Host 'JobState: ' $Job.State
      [System.Windows.Forms.Application]::DoEvents()
   }
   Until ($Job.State -eq 'Completed')
   Write-Host '*Action*'
})
$Form1.ShowDialog()

2 个答案:

答案 0 :(得分:0)

Powershell工作的国家财产是只读的;这意味着在实际启动作业之前,您无法将作业状态配置为任何内容。当您监视statechanged事件时,它不会触发,直到click事件再次出现并且状态被“看到”从“running”更改为“completed”,此时脚本块将执行。这也是关闭表单时脚本块执行的原因。

以下脚本消除了监视事件的需要,而是监视状态。我假设你想在状态“运行”时触发'statechanged'代码。

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Form1 = New-Object Windows.Forms.Form
$Form1.Add_Shown({
   $Form1.Activate()
})
$Button1 = New-Object System.Windows.Forms.Button
$Button1.Text = 'Test'
$Form1.Controls.Add($Button1)

$Button1.Add_Click({
$this.Enabled = $false
   Write-Host $Job.State " - (Before job started)"
    $Job = Start-Job {'abc'}
   Write-Host $Job.State " - (After job started)"
         If ($Job.State -eq 'Running') {

                Start-Sleep -Seconds 1
                Write-Host '*Doing Stuff*'
               }

   Write-Host $Job.State " - (After IF scriptblock finished)"
[System.Windows.Forms.Application]::DoEvents()
$this.Enabled = $true

})

$Form1.ShowDialog()

另外,请注意以下几行:

$this.Enabled = $false
[System.Windows.Forms.Application]::DoEvents()
$this.Enabled = $true

这些行确保按钮不会对点击事件进行排队。显然你可以删除'write-host'行,我把它们留下了,这样你就可以看到状态在脚本执行时如何变化。

希望这有帮助。

答案 1 :(得分:0)

有很多(StackOverflow)问题和答案关于将表格(或WPF)事件与.NET事件(例如EngineEventsObjectEvents和{组合在一起的'enduring mystique' PowerShell中的{1}}}:

他们都归结为两点:即使有多个线程设置,也有两个不同的听众'在一个线程中。当您的脚本准备好接收表单事件(使用WmiEventsShowDialog)时,它无法同时监听.NET事件。反之亦然:如果脚本在处理命令时打开.NET事件(如DoEvents或专门使用Start-SleepWait-Event等命令监听.NET事件),则表单不会能够听取形式事件。这意味着.NET事件或表单事件正在排队,因为您的表单与您尝试创建的.NET侦听器位于同一个线程中。 与nimizen示例一样,在第一个龟头看起来是正确的,当您检查背景工作者的状态时,您的表单将对所有其他表单事件(按钮单击)无响应,您必须一遍又一遍地单击按钮再次查明它是否仍然是Wait-Job。要解决此问题,您可以考虑在循环中结合‘*Doing Stuff’方法,同时连续检查后台工作者的状态,但这看起来也不是一个好方法,请参阅:Use of Application.DoEvents() 所以唯一的出路(我看)是有一个线程来触发另一个线程中的表单我认为只能使用DoEvents来完成,因为它可以同步一个对待之间的表单控制与直接触发表单事件(例如[runspacefactory]::CreateRunspace())。

(如果以另一种方式,我渴望学习如何看待工作示例。)

表格示例:

TextChanged

有关 WPF 示例,请参阅:Write PowerShell Output (as it happens) to WPF UI Control