Search code examples
powershelltry-catch-finallyread-host

Try.. catch.. finally fails during Read-Host


When the script is executing the Read-Host cmdlet, closing the window does not activate the finally block. Below is an arbitrary but minimally functional example. I'm using PowerShell 5.0. The Beep() is just to make it obvious the finally block executes.

try {
    $value= Read-Host -Prompt "Input"
    sleep 5
} finally {
    [Console]::Beep(880,1000)
}
  1. If you close the window by clicking the red X during the Read-Host the finally block will NOT execute.
  2. If you close the window by clicking the red X during the sleep the finally block WILL execute.
  3. If you interrupt with Ctrl-C at any point, the finally block WILL execute.

Is there something fundamental I'm missing about why the finally block is NOT executing when closing the window during a Read-Host?

The full case involves starting a service on an Amazon Snowball device and needing to stop the service if the script is closed. The full case behavior mirrors the example case above.

EDIT: Changed variable from $input to $value due to comments saying $input is a reserved variable. Does not change behavior.


Solution

  • Continuing from my comment.

    The console host is a bit inflexible based on what you are doing natively from it. That 'X' has to do with the PowerShell session/process, not the code running in it. Hence why CRTL+C works, as you are stopping the code run, not the PowerShell session/process.

    Here are a couple of approaches to get you thinking about your options.

    ###############################################################################
    #region Begin initialize environment                                          #
    ###############################################################################
        
        # Initialize GUI resources
        Add-Type -AssemblyName  System.Drawing,
                                PresentationCore,
                                PresentationFramework,
                                System.Windows.Forms,
                                microsoft.VisualBasic
        [System.Windows.Forms.Application]::EnableVisualStyles()
        
    ###############################################################################
    #endregion End initialize environment                                         #
    ###############################################################################
    
    # Prevent the MessageBox UI from closing until an entry is made
    while (
        ($UserEntry = [Microsoft.VisualBasic.Interaction]::
        InputBox('Enter a Host/User', 'Add Item')) -eq ''
    )
    {
        [System.Windows.Forms.MessageBox]::
        Show(
            'Entry cannot be empty', 
            "Error on close" , 
            0, 
            [System.Windows.MessageBoxImage]::Error
        )
    }
    "You entered $UserEntry"
    

    Or a full custom form for more granular control

    # Initialize the form object
    $form = New-Object System.Windows.Forms.Form
    
    # Define form elements
    $form.Text = 'Data Entry'
    
    $txtUserInput           = New-Object system.Windows.Forms.TextBox
    $txtUserInput.multiline = $false
    $txtUserInput.width     = 120
    $txtUserInput.height    = 20
    $txtUserInput.location  = New-Object System.Drawing.Point(40,29)
    $txtUserInput.Font      = 'Microsoft Sans Serif,10'
    
    $form.controls.AddRange(@(
        $txtUserInput
        )
    )
    
    # Evaluate form events
    $form.Add_Closing(
    {
        param
        (
            $Sender,$Event
        )
    
        $result = [System.Windows.Forms.MessageBox]::Show(
                    'Are you sure you want to exit?', 
                    'Close', 
                    [System.Windows.Forms.MessageBoxButtons]::YesNo
                )
    
        if ($result -ne [System.Windows.Forms.DialogResult]::Yes)
        {$Event.Cancel = $true}
    })
    
    # Start the form
    $form.ShowDialog() | Out-Null
    
    # Resource disposal
    $form.Dispose()