如何在 PowerShell 中检查后台作业是否完成但保持 Windows 窗体响应

Nig*_*Job 5 powershell jobs multithreading winforms

Start-Job我创建了一个 Windows 表单,您可以在其中单击一个按钮来启动大约 15 分钟的备份过程(使用)。我使用它是Start-Job为了在备份过程中保持表单响应(响应式我的意思是你可以移动它,最小化它等等)。但是,我希望表单在工作完成后弹出一个消息框,但我无法获得正确的结果。

首先,我尝试了一个While循环,每 10 秒检查一次作业是否完成:

$BackupButton.Add_Click( {

        $BackupJob = Start-Job -ScriptBlock { ... }
        $Completed = $false
        while (!($Completed)) {
            if ($BackupJob.State -ne "Running") {
                $Completed = $true
            }
            Start-Sleep -Seconds 10
        }
        [System.Windows.Forms.MessageBox]::Show('Successfully completed the backup process.', 'Backup Tool', 'OK', 'Info')
    })
Run Code Online (Sandbox Code Playgroud)

这在作业完成后给了我一个消息框,但表单在此过程中没有响应,可能是因为它仍在循环中使用线程的资源While

然后,我尝试使用Register-ObjectEvent调用消息框来显示作业状态何时更改:

$BackupButton.Add_Click( {

        $BackupJob = Start-Job -ScriptBlock { ... }
        Register-ObjectEvent $BackupJob StateChanged -Action {
            [System.Windows.Forms.MessageBox]::Show('Successfully completed the backup process.', 'Backup Tool', 'OK', 'Info')
        }
    })
Run Code Online (Sandbox Code Playgroud)

此选项确实使表单在此过程中保持响应,但消息框(事件的操作块)从未启动,直到我关闭 Windows 表单的那一刻。

是否有任何选项可以使消息框按时显示(不在表单关闭时)并且不使用表单的线程(保持响应)?

编辑:或者,有没有办法从后台作业控制我的表单?我尝试将表单的按钮/控件作为参数发送给作业,然后从作业控制表单的事件,但它不起作用。如果有办法以某种方式从后台作业访问表单,这也将解决我的问题。

提前致谢。

The*_*heo 3

Start-Sleepcmdlet 使您的表单无响应。为了克服这个问题,可以使用一个System.Windows.Forms.Timer对象来代替。

就像是:

$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000   # for demo 1 second
$timer.Enabled = $false  # disabled at first
$timer.Add_Tick({
    # check every 'Interval' milliseconds to see if the backup job is still running
    # if not, stop the timer (this will set the Enabled property to $false)
    if ($script:BackupJob.State -ne "Running") { $timer.Stop() }
})

$BackupButton = New-Object System.Windows.Forms.Button
$BackupButton.Anchor = 'Top','Left'
$BackupButton.Size = [System.Drawing.Size]::new(120, 31)
$BackupButton.Location = [System.Drawing.Point]::new(($form.Width - $BackupButton.Width) / 2, 150)
$BackupButton.Text = 'Start Backup'

$BackupButton.Add_Click( {
    Write-Host "Job started"
    $this.Enabled = $false  # disable the button, to prevent multiple clicks

    # use the script: scope, otherwise the timer event will not have access to it
    # for demo, the job does nothing but wait..
    $script:BackupJob = Start-Job -ScriptBlock { Start-Sleep -Seconds 5 }
    $timer.Start()
    while ($timer.Enabled) {
        [System.Windows.Forms.Application]::DoEvents()
    }
    Write-Host "Job ended"
    # show the messagebox
    [System.Windows.Forms.MessageBox]::Show('Successfully completed the backup process.', 'Backup Tool', 'OK', 'Info')

    # and enable the button again
    $this.Enabled = $true
})
Run Code Online (Sandbox Code Playgroud)

希望有帮助