PowerShell ISE在代码更改后有时会表现不可预测。

5

我正在使用PowerShell ISE (PS版本5.0)。如果我运行以下代码:

Write-Host "This"

它输出:

This

如果我像这样修改脚本:
Write-Host "That"

它的输出结果为:

That

太好了。如预期所料。现在,如果我有这段代码:
$Form = New-Object System.Windows.Forms.Form
$Timer = New-Object System.Windows.Forms.Timer

$Timer.Add_Tick(
{
    &{
    Write-Output "Here"
    $Form.Close()} | Write-Host 
})

$Timer.Interval = 3000
$Timer.start()
$result = $Form.ShowDialog()

它的输出结果是:
Here

如果我更改脚本中的任何内容,比如将"Here"更改为"There"或将$Timer.Interval = 3000更改为$Timer.Interval = 4000并运行它,它会出现两个意外的情况:1)不是按照预期的时间显示窗体,而是在屏幕上短暂闪现;2)输出原始的Here而不是There。如果我关闭ISE并重新打开它,则脚本会按预期运行。
发生了什么?

看起来你没有处理对象的释放,所以你只是创建了很多对象实例。从头开始重新启动会清除内存中的对象。 - gvee
当你说“运行它”时,你是运行整个文件/代码片段还是只运行最后一部分?你不能在关闭表单后再次显示它,除非重新实例化$timer,否则原始事件注册仍将存在。 - Mathias R. Jessen
@gvee -- 所以你的意思是ISE即使在停止后仍然保留对象?作为一个来自.NET和Visual Studio,没有太多PowerShell经验的人,我觉得这很奇怪和意外。我认为“对象应该在停止执行后自动被处理掉”。 - rory.ap
@MathiasR.Jessen ^ - rory.ap
@rory.ap - 在VS中,当调试器停止时,它会被处理掉,对吧?在PS ISE中的等效操作是关闭会话。 - gvee
@gvee -- 是的,没错。所以你可以看出来,当ISE顶部的红色“停止”按钮变成绿色的“运行”按钮(就像在VS中一样),我会认为一切都被处理/重置了。 - rory.ap
2个回答

2

简而言之:

  • 计时器实例是在“会话”范围内创建的,无论您是否在ISE中运行脚本,以及任何引用它的变量是否在作用域中。

  • 始终处置计时器(或至少禁用它),以防止它生成更多事件。

  • 通常情况下 - 虽然这不是当前问题的原因 - 请注意,在ISE中运行脚本隐式点源,因此重复执行在相同的范围内运行,具有来自先前执行的变量值残留,这可能导致意外行为。


您的代码从未处置(或禁用)计时器,因此:

  • 无论变量是否引用它,它都会保持活动状态整个会话期间

  • 继续生成事件,

  • 但只有在显示窗体时才会触发它们。

这解释了您的症状:一旦您再次显示窗体,排队的原始事件就会立即触发

解决方案是在计时器完成其任务并触发事件(一次)后处置计时器

Add-Type -AssemblyName System.Windows.Forms

$Form = New-Object System.Windows.Forms.Form
$Timer = New-Object System.Windows.Forms.Timer

$Timer.Add_Tick({
    & {
      Write-Output "Here"
      $Form.Close()
    } | Write-Host 
})

$Timer.Interval = 3000
$Timer.Start()
$result = $Form.ShowDialog()
$Timer.Dispose() # IMPORTANT: Dispose of the timer so it won't generate more events.

即使使用了上述ISE的隐式源代码行为,对该代码的重复调用也能按预期工作。

1
我认为这与ISE中的变量在脚本结束后仍然在内存中有关。如果您添加

$Timer.Stop() 

将脚本的最后一行添加后,关闭并重新打开ISE,它将正常工作。

运行 $Timer.Stop() 是一个好建议,但这个行为与 ISE 无关。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接