Powershell窗体-使用按钮暂停循环

Powershell窗体-使用按钮暂停循环,powershell,Powershell,我有一个基于表单的脚本,可以从各种来源复制大量文件。复制文件的主循环可以运行很长时间,我想添加一个暂停按钮来保持处理(直到再次按下暂停按钮) 按照建议,我正在使用[System.Windows.Forms.Application]::DoEvents()来保持表单的响应性,并提供一种在需要时优雅地中断复制循环的方法 我正努力想知道如何引入一个按钮来暂停“ForEach”循环,而不是打断它——当我尝试使用Do-Until/While时,是否有人能为我指出正确的方向,因为它似乎以某种方式挂起了脚本

我有一个基于表单的脚本,可以从各种来源复制大量文件。复制文件的主循环可以运行很长时间,我想添加一个暂停按钮来保持处理(直到再次按下暂停按钮)

按照建议,我正在使用
[System.Windows.Forms.Application]::DoEvents()
来保持表单的响应性,并提供一种在需要时优雅地中断复制循环的方法

我正努力想知道如何引入一个按钮来暂停“ForEach”循环,而不是打断它——当我尝试使用Do-Until/While时,是否有人能为我指出正确的方向,因为它似乎以某种方式挂起了脚本

下面是我正在做的一个大大简化的示例:

$StartCopyButton.Add_Click({
    $script:CancelLoop = $false
    $script:PauseToggle = $false
    $CancelButton.Enabled = $true
    $StartCopyButton.Enabled = $false

    Get-ChildItem -LiteralPath $Source -Recurse -File | ForEach {
        Copy-Item -Path $.FullName -Destination $NewDestination
        [System.Windows.Forms.Application]::DoEvents()
        If($script:CancelLoop -eq $true) {
            #Exit the loop
            Break;
        }
        If ($script:PauseToggle) {
            Do { 
            [System.Windows.Forms.Application]::DoEvents()
            } Until (!$script:PauseToggle)
        }
    }
    $CancelButton.Enabled = $false
    $StartCopyButton.Enabled = $true
})

$CancelButton.Add_Click({
    $script:CancelLoop = $true
})
$PauseButton.Add_Click({
    # Boolean change value to true/false
    $script:PauseToggle = !$script:PauseToggle
})

您可以检查暂停状态,如果它为真,则使用DoEvents执行空循环。像这样(虽然没有测试):

实际上,启动和暂停只需要一个按钮。这里有一个非常基本的方法来说明我的想法,没有经过测试:

$script:CancelLoop = $false
$script:PauseLoop = $false
$CopyButton.Add_Click({
    # toggle the start/pause state when clicked
    $script:PauseLoop = -not $script:PauseLoop
    if ($script:PauseLoop) {
        $CancelButton.Enabled = $false
        $CopyButton.Text = "Start"
    }
    else {
        $CancelButton.Enabled = $true
        $CopyButton.Text = "Pause"
        # start / resume the loop
        Get-ChildItem $Source -Recurse -File | foreach {
            $newPath = Join-Path $NewDestination $_.Name
            # test if file was already copied
            # (might want to compare modified times too)
            if (-not (Test-Path $newPath)) {
                Copy-Item $_.FullName $newPath
            }
            [System.Windows.Forms.Application]::DoEvents()
            if ($script:CancelLoop -or $script:PauseLoop) {
                # exit loop if cancelled or paused
                break
            }
        }
        $CancelButton.Enabled = $false
        $CopyButton.Text = "Start"
    }
})

另一种方法是首先获取所有文件,将它们保存在一个集合中,然后存储索引并在此时暂停/恢复。但这也带来了风险,即文件列表在此期间可能发生了变化。因此,一个真正“安全”的解决方案将更加复杂。

在复制循环中创建一个循环,该循环在“停止”按钮切换为“活动”时处于活动状态。在该循环中
DoEvents
。不要忘记,您还需要在外部循环中执行
DoEvents
。(还没有测试这是否真的有效,只是一个想法)是的,谢谢,我刚刚意识到我试图在错误的地方编写代码(在按钮而不是循环中)。我已经修改了上面的内容,这似乎是可行的,不过如果有更优雅的方式来编写代码,我很高兴听到。更优雅的方式是制作一个单独的工作线程。但是在Powershell中控制线程确实是一件痛苦的事情。如果您想要一个表单应用程序(然后使用c#或vb.net)或Powershell脚本,您应该下定决心。将这两个世界混为一谈并不那么有效。在表单应用程序中,您将使用辅助线程。在PowerShell中,你不会使用暂停按钮等。@marsze在PowerShell中编写GUI本身就是一种黑客行为:)要获得一个好的结果,需要付出很大的努力。是的,我刚刚就OPNice的想法发表了评论,但如果文件量很大,可能需要一些时间才能恢复。@montonero正如我所说的,混合这两个世界不会很好。只需跳过表单并将该命令输入PowerShell即可。你可以随时在那里暂停管道。编译一个文件列表并不实际——涉及数百万个文件,PS资源将成为一个问题。不久前,我用自己找到的解决方案编辑了我的原始帖子,但我喜欢一个暂停/停止按钮的想法。
$script:CancelLoop = $false
$script:PauseLoop = $false
$CopyButton.Add_Click({
    # toggle the start/pause state when clicked
    $script:PauseLoop = -not $script:PauseLoop
    if ($script:PauseLoop) {
        $CancelButton.Enabled = $false
        $CopyButton.Text = "Start"
    }
    else {
        $CancelButton.Enabled = $true
        $CopyButton.Text = "Pause"
        # start / resume the loop
        Get-ChildItem $Source -Recurse -File | foreach {
            $newPath = Join-Path $NewDestination $_.Name
            # test if file was already copied
            # (might want to compare modified times too)
            if (-not (Test-Path $newPath)) {
                Copy-Item $_.FullName $newPath
            }
            [System.Windows.Forms.Application]::DoEvents()
            if ($script:CancelLoop -or $script:PauseLoop) {
                # exit loop if cancelled or paused
                break
            }
        }
        $CancelButton.Enabled = $false
        $CopyButton.Text = "Start"
    }
})