Recently I was contacted by one of our PrimalForms users. He commented that the behavior of a script deviated from what he was expecting. He provided me with the following script block for the Button Click Event so that I could help clear up the confusion:
$handler_button3_Click={
# Check that a logfile was specified. If not close the form.
if ( $textbox2.text -eq "" )
{
Write-host "No Logfile, closing"
$form1.close()
} # A logfile was specified
Write-Host "I have the logfile"
}
As you can see, the user is testing to make sure that the the name of the log file was entered into the textbox. If the value is empty, the script will close the form. This is where the behavior deviates from what the user expects. When the user presses the button with the textbox empty, the script generates the following output:
PowerShell continues to execute the last line despite the fact that the script closed the form in the following line. The user’s perception was that the script block will stop executing after the form was closed, but that is not the case. The reason is that PowerShell is not dependent on the form to execute the script, thus it continues to process the rest of script. To PowerShell the $form1.Close() line is just another command to execute. Since PowerShell does not stop the script when the form is closed, the script block needs to be modified in order prevent the execution of the rest of the script. Below I present two alternatives to prevent this from happening:
Add “return” command after closing the form:
$handler_button3_Click={
# Check that a logfile was specified. If not close the form.
if ( $textbox2.text -eq "" )
{
Write-host "No Logfile, closing"
$form1.close()
return #exit the script block
} # A logfile was specified
Write-Host "I have the logfile"
}
An alternative would be to add in an “else” statement:
$handler_button3_Click={
# Check that a logfile was specified. If not close the form.
if ( $textbox2.text -eq "" )
{
Write-host "No Logfile, closing"
$form1.close()
} # A logfile was specified
else #add else statement
{
Write-Host "I have the logfile"
}
}
The corrected script block generates the expected output:
As demonstrated, forms in PowerShell may not behave as expected. Users must keep in mind that PowerShell will continue to execute the remaining lines in a script even after closing the form in a previous line; therefore, users must accommodate accordingly.
The Form.Close() method of WinForms is used to post the WM_CLOSE message to the message loop. This message causes a sequence of events to occur that can end in the window closing; assuming no other event cancels the process. This has been standard Windows behavior since version 1.0.
The important thing to know is that no action is actually taken when the method is called. The method simply places a message in the Windows Message Loop Queue to be processed in order of precedents the next time the Queue is serviced. ( I know – it sounds confusing but…)
In Windows only one thread can execute at a time on a single processor. A Window (Form) has a single user thread on which it executes code. Even if the message loop is on a separate
thread the code thread will always run each event to completion before executing a new block of code. As David has pointed out the PowerShell script block will always run to completion BEFORE the message loop will be allowed to process the WM_CLOSE message.
This should be true of nearly every ‘Form’ method. Windows is and has always been an event driven environment. Nearly all methods do no more than “queue” an event. Every executing event will always be guaranteed to complete before a new event will be scheduled. The rules of event scheduling and Message Loop message handling will determine whether an event can be pre-empted by a newly scheduled event. Only unscheduled events can be preempted or reordered.
This is all good to know as it can be helpful in understanding much of the behavior of Windows Forms code in PowerShell or any other language.