Windows PowerShell で GUI プログレス バー

# サンプル実装

…ともうしましても、こちらもすでに先達がいらっしゃいましたわけで、それを自分ごのみに改変しただけです。

###### 変更

なにぶん初心者ゆえ、こういう場面でもちいるのが適切かはわかりませんが、クロージャーなる機能をもちいて、できるだけコードがちらからないようにしてみました。

###### 不具合

Windows PowerShell ISE から実行するとバーが中途半端なところで終了してしまいます。

```PowerShell:GUIProgressBar.ps1
Function New-ProgressBar {
    Param([Int]$Minimum, [Int]$Maximum)

    Add-Type -AssemblyName System.Windows.Forms
    $form = New-Object System.Windows.Forms.Form
    $progressBar = New-Object System.Windows.Forms.ProgressBar

    #$form.Text = 'Progress Bar'
    $form.Width = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Width / 2
    $form.Height = $form.Width / 5
    $form.StartPosition = 'CenterScreen'
    $form.TopMost = $true

    $progressBar.Width = $form.ClientRectangle.Width * 0.9
    $progressBar.Height = $form.ClientRectangle.Height * 0.3
    $progressBar.Left = ($form.ClientRectangle.Width - $progressBar.Width) / 2
    $progressBar.Top = ($form.ClientRectangle.Height - $progressBar.Height) / 2
    $progressBar.Visible = $true
    $progressBar.Style = 'Continuous'
    $progressBar.Minimum = $Minimum
    $progressBar.Maximum = $Maximum
    $progressBar.Value = $Minimum
    $progressBar.Step = 1
    $form.Controls.Add($progressBar)

    $form.Show()
    [void]$form.Focus()

    Return {
        $form.Text = ($progressBar.Value / ($Maximum - $Minimum)).ToString('0%')
        $progressBar.PerformStep()
        If ($progressBar.Value -ge $Maximum) {
            $form.Close()
        }
    }.GetNewClosure()
}

$min, $max = 0, 42
$progressBar = New-ProgressBar -Minimum $min -Maximum $max
For ($i = $min; $i -le $max; $i++) {
    & $progressBar
    # Some time-comsuming task ...
    Start-Sleep -Milliseconds 100
}
```

# 別解 (追記)

[ドキュメント](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.form?view=netframework-4.8 "Form クラス (System.Windows.Forms) | Microsoft Docs") にはみつからないのですが、文献 2. に `Add_Shown` メソッドというものがありましたのでつかってみました。

```PowerShell:GUIProgressBar2.ps1
Function Invoke-ProgressBar {
    Param([Int]$Minimum, [Int]$Maximum, [ScriptBlock]$ScriptBlock)

    Add-Type -AssemblyName System.Windows.Forms

    $form = New-Object System.Windows.Forms.Form
    #$form.Text = 'Progress Bar'
    $form.Width = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Width / 2
    $form.Height = $form.Width / 5
    $form.StartPosition = 'CenterScreen'
    $form.TopMost = $true

    $progressBar = New-Object System.Windows.Forms.ProgressBar
    $progressBar.Width = $form.ClientRectangle.Width * 0.9
    $progressBar.Height = $form.ClientRectangle.Height * 0.3
    $progressBar.Left = ($form.ClientRectangle.Width - $progressBar.Width) / 2
    $progressBar.Top = ($form.ClientRectangle.Height - $progressBar.Height) / 2
    $progressBar.Visible = $true
    $progressBar.Style = 'Continuous'
    $progressBar.Minimum = $Minimum
    $progressBar.Maximum = $Maximum
    $progressBar.Value = $Minimum
    $progressBar.Step = 1
    $form.Controls.Add($progressBar)

    $form.Add_Shown({
        For ($i = $Minimum; $i -le $Maximum; $i++) {
            & $ScriptBlock
            $form.Text = ($progressBar.Value / ($Maximum - $Minimum)).ToString('0%')
            $progressBar.PerformStep()
        }
        $form.Close()
    })
    [void]$form.ShowDialog()
}

Invoke-ProgressBar -Minimum 0 -Maximum 42 -ScriptBlock {
    # Some time-comsuming task ...
    Start-Sleep -Milliseconds 100
    Write-Host $i
}
```

# 参考文献

1. [スクリプト Progress Bar With PowerShell](https://gallery.technet.microsoft.com/scriptcenter/Progress-Bar-With-d3924344 "スクリプト Progress Bar With PowerShell")
1. [Creating a Custom Input Box - PowerShell | Microsoft Docs](https://docs.microsoft.com/en-us/powershell/scripting/samples/creating-a-custom-input-box?view=powershell-6 "Creating a Custom Input Box - PowerShell | Microsoft Docs")
1. [ProgressBar クラス (System.Windows.Forms) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.progressbar?view=netframework-4.8 "ProgressBar クラス (System.Windows.Forms) | Microsoft Docs")