

powershellで、

概略ですが、

[code]

    function func1{
       for ($i = 0;$i -lt 10;$i++){
           Write-Host $i
           start-sleep 1
       }
    }
    $form.Add_Shown({func1})
    $form.ShowDialog()
    
[/code]

このようにしています。  
forループによる処理中に、ウィンドウの右上の×ボタンや、Ctrl+Cが効きません。  
どうすればよいでしょうか？


CC BY-SA 4.0
2023-01-13T18:29:58.297
93327
2023-01-14T04:39:05.767
56253
question
powershell
powershellによるGUIで、sleep中に終了できない。


使われている`$form`はおそらく[System.Windows.Forms.Form](https://learn.microsoft.com/ja-
jp/dotnet/api/system.windows.forms.form?view=windowsdesktop-7.0)でしょうから、まずWindowメッセージ処理を行うことと、想定する事象用のイベントハンドラを用意して登録する必要があるでしょう。

  * WriteとSleepだけのループではWindowメッセージ処理が行われないので、ループの中に[DoEvents()](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.application.doevents?view=windowsdesktop-7.0)を追加する  
(ただし[Application.DoEvents
メソッドを使うべきでない理由](https://blog.jhashimoto.net/entry/20111113/1321133064)といった記事もあるので他の手段を探すのでも良いでしょう)

  * Ctrl+C を検出し、対応する処理を行うために、[KeyDown イベント](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.control.keydown?view=windowsdesktop-7.0)のハンドラを定義・登録し、ハンドラ内では押されたキー種別をチェックしてCtrl+Cなら中断・終了処理をする
  * 閉じる(`X`)ボタンクリックを検出し、対応する処理を行うために、[Closing イベント](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.form.closing?view=windowsdesktop-7.0)のハンドラを定義・登録し、中断・終了処理をする
  * `for`のループを終了させるための判定や手段が無いので、判定用フラグ変数を定義・初期化し、上記ハンドラにて設定を行う。`for`ループの中で判定してフラグが設定されていたらループを終了する  
[about_Scopes](https://learn.microsoft.com/ja-
jp/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7.2)

以下のように出来るでしょう。

[code]

    Add-Type -AssemblyName System.Windows.Forms
    $form = New-Object System.Windows.Forms.Form
    $script:Cancel = $false  #### 中断・終了判定用フラグ変数
    
    #### 閉じるボタン等Closingイベントハンドラ定義・登録
    $Close = {
        $form.Visible = $False
        $script:Cancel = $True
    }
    $form.Add_Closing($Close)
    
    #### Ctrl+Cキー判定用KeyDownイベントハンドラ定義・登録
    $KDown = {
        if (($_.KeyCode -eq "C") -And $_.Control)
        {
            $form.Visible = $False
            $script:Cancel = $True
        }
    }
    $form.Add_KeyDown($KDown)
    
    #### 質問からのコピーおよび対策追加
    function func1{
       for ($i = 0;$i -lt 10;$i++){
           Write-Host $i
           [System.Windows.Forms.Application]::DoEvents()  #### Windowメッセージ処理
           if ($script:Cancel) {  #### 中断・終了判定とループ終了
               break
           }
           start-sleep 1
       }
    }
    $form.Add_Shown({func1})
    $form.ShowDialog()
    $form.Dispose()
    
[/code]


CC BY-SA 4.0
2023-01-14T04:39:05.767
93328
2023-01-14T04:39:05.767
26370
93327
answer