Question

I have a large PowerShell-based GUI utility that runs different operations in the background for users on request. When they ask for the status of the jobs, they can see them via code that looks like this:

Get-Job | foreach {
    $JobID = $_.Id
    $JobCaption = $script:JobList[$JobID]
    if ($JobCaption -ne $null) {
        $JobState = $_.JobStateInfo
        $JobProgress = $_.Progress
        $JobPercent = $JobProgress.PercentComplete
        $JobStatusDescription = $JobProgress.StatusDescription
        $lstJobStatus.Items.Add("$JobID`: $JobCaption is $JobState ($JobStatusDescription), $JobPercent%")
    }
}

This works fine for tasks that are directly jobs. However, some of the tasks in turn have child tasks that are the real work, with some supporting code around it. For example:

$Job = Start-Job -ScriptBlock { 
    # setup omitted
    $ijob = Convert-VHD -Path $VHDPath -DestinationPath $NewVHDPath -ComputerName $TargetServer -VHDType Dynamic -ErrorAction 'SilentlyContinue' -AsJob
    while ($ijob.State -eq "Running") {
        $ip = $ijob.Progress
        Write-Progress -Activity $ip.Activity -CurrentOperation $ip.CurrentOperation -PercentComplete $ip.PercentComplete -Status $ip.Status
        Start-Sleep -Seconds 5
    }
    # cleanup omitted
}

Notice I have a long-running operation, so I make a job out of it and save that. That is a child of the outer job.

How can I pass the inner job progress information up to the outer job status check? The code I have here doesn't seem to work. (Yes, I know polling is not ideal, but I'm not sure of a better way to do it.)

Was it helpful?

Solution

Ultimately, I did have to use Write-Progress - the final code (which I'm not really proud of) looks like this:

$child = Convert-VHD -Path $VHDPath -DestinationPath $NewVHDPath -ComputerName $TargetServer -VHDType Dynamic -ErrorAction 'SilentlyContinue' -AsJob
$child.Name = "... primary conversion work for $VHDPath"
Write-Progress -PercentComplete $child.Progress[-1].PercentComplete -Activity $child.ChildJobs[0].Progress[-1].Activity -Status $child.Progress[-1].StatusDescription
while ($child.State -eq 'NotStarted') {
    Start-Sleep -Seconds 5
}
while ($child.State -eq 'Running') {
    Write-Progress -PercentComplete $child.Progress[-1].PercentComplete -Activity $child.Progress[-1].Activity -Status $child.Progress[-1].StatusDescription
    Start-Sleep -Seconds 5
}
Write-Progress -PercentComplete $child.Progress[-1].PercentComplete -Activity $child.ChildJobs[0].Progress[-1].Activity -Status $child.Progress[-1].StatusDescription

The status check code now looks like this:

function Add-ProgressToList {
    param (
        [object] $actingJob
    )
    $displayTitle = $actingJob.Name
    $JobID = $actingJob.Id
    $JobState = $actingJob.JobStateInfo
    $JobProgress = $actingJob.Progress[-1]
    $JobPercent = $JobProgress.PercentComplete
    $JobActivity = $JobProgress.Activity
    $JobStatusDescription = $JobProgress.StatusDescription
    if ($JobPercent -eq $null) {
        $lstJobStatus.Items.Add("$JobID`: $displayTitle is $JobState")
    } else {
        $lstJobStatus.Items.Add("$JobID`: $displayTitle is $JobState ($JobActivity : $JobStatusDescription), $JobPercent%")
    }
    foreach ($Child in $actingJob.ChildJobs) {
        Add-ProgressToList $Child
    }
}

function Update-JobList {
    Set-Cursor -Busy -Operation 'Updating job list...'
    Clear-Listbox $lstJobStatus
    Get-Job | foreach {
        Add-ProgressToList $_
    }
    $lstJobStatus.SelectedIndex = $lstJobStatus.Items.Count - 1
    $lstJobStatus.SelectedIndex = -1
    Set-Cursor -Normal
    $stsJobs.Text = Get-TabLastUpdateText
}

I stopped carrying around a list of jobs because I realized I could just use the job name in a descriptive way. The recursion allows picking up the child job situation (that isn't happening all that much, actually).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top