2

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.

Alex Buck
  • 197
  • 2
  • 2
  • 10
  • [1] `$Input` is a reserved automatic variable. DO NOT write to it unless you are totally sure you understand the side effects. [*grin*] [2] you likely are seeing the result of the way "host" and the various PoSh streams interact. i presume that killing the console while in a console-direct action bypasses powershell entirely. – Lee_Dailey Aug 23 '20 at 00:08
  • [1] I updated the code snippet to not have $input. The behavior is the same even with a different variable. [2] I'm sure there is a difference. Whats weird is killing the console window during the `sleep` DOES activate `finally`. – Alex Buck Aug 23 '20 at 00:15
  • that is likely because you are no longer acting _directly in the host_ ... you are back inside PoSh. – Lee_Dailey Aug 23 '20 at 00:28
  • Is there a way to execute a `finally` block in that case? I want to run some clean-up code even if the user just closes the window. – Alex Buck Aug 23 '20 at 00:33
  • CRTL+C means stop the running code and return to the prompt. 'X", means exit the session now. If the user closes the 'x' to close the console window box and not respond to the read-host, no matter what you do, no code will happen. regardless of the language you used. The only way to do this to create your own form to collect user info, and on the form close/dispose events, you can take action. You can disable the 'X' in a form and force the use use your close button and thus fire code form that. – postanote Aug 23 '20 at 06:32
  • @AlexBuck - not natively. squashing the console means the host never returns to the code. it looks like postanote has a useful workaround. ///// another option would be to run a master session in a hidden window and run all the rest in visible sessions started with something like `Start-Process` with the `-Wait` parameter. i have not tested that, tho. [*blush*] – Lee_Dailey Aug 23 '20 at 13:33

1 Answers1

2

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()
postanote
  • 15,138
  • 2
  • 14
  • 25