PowerShell Cancel Button Stop Script

BRBT picture BRBT · Mar 18, 2014 · Viewed 31.6k times · Source

I am having trouble with some PS GUI functionality.

I have tried quite a few things and have been looking everywhere (ALL THE LINKS ARE PURPLE!).

I have a cancel button on my form, right now when the user clicks the cancel button, it points to this handler, and my form will close.

  $handler_cancelButton_Click={
      $MainForm.Close()
  }

However, once the form closes, all of the rest of my script tries to keep running (FTP, run Excel, sends emails, etc.) and throws me errors.

I have tried many different things, I have ran just the Exit command and that closes my form, throws an error, and closes down my entire IDE (PowerGUI).

Now I have also tried things like this:

[environment]::exit(0)

[System.Windows.Forms.Application]::Exit($null)

Both, will do exactly what calling just Exit will do. Close my form, and PowerGUI.

What I want to happen is when the user presses cancel, my form closes, and the rest of my script stops or terminates.

EDIT

Now once I have a GUI.ps1, and a Code.ps1. I guess I would call GUI.ps1 inside of Code.ps1, get the user input that I need into variables. Then take those variables and pass them from GUI.ps1, over to Code.ps1 (I need that info in order to run anything here).

Then once I have those values in Code.ps1, run everything else I am doing..?

But now if my user decided to only hit the cancel button for some reason...

(likely to not happen a lot to be honest, I more so want to get this functionality working for learning sake. They might accidentally run the script, somehow..)

...it would just close the GUI, but wouldn't the rest of Code.ps1 still run?

Or now would I be able to say something like this in my Code.ps1 script:

if($someGUIValue -eq $null -and $someOtherGUIValues - eq $null){
    Exit   #would I be able to run this now the way I think it would?
}
else{
    #yes, run the script..
}

**FOUND ANSWER

EDIT..

What I did was I created a flag. Before any of my script had started I declared a variable like so

$terminateScript = $false

Within my event handler I have added this

$handler_cancelButton_Click={
    $terminateScript = $true
    $MainForm.Close()
    return $terminateScript 
}

Once they hit the cancel button it will set $terminateScript = $true. So now where I am calling all of my functions I have something like this:

$username, $password, $someOtherValue, $anotherVaklue, $terminateScript = MainForm
if($terminateScript -eq $false){
    SomeFunction
    AnotherFunction
}
else{
     Exit #this now works here, and cancels my script, without closing my IDE.
}

Thanks for the help

Answer

HAL9256 picture HAL9256 · Mar 18, 2014

PowerShell is doing exactly what it is designed to do. PowerShell is a scripting environment that launches multiple separate programs, not like a C# program that runs with a single thread that can be terminated.

Every process that you launch from PowerShell is launched separately in their own process. There is no one command to end all processes. Instead you have to keep a handle on all the processes that you open, and close each one.

$MainForm.Close()

Will close the GUI. It does not close anything else.

Exit

Will exit the entire PowerShell prompt. If you have launched an executable like Excel, it will still be open in it's own process.

If you are launching something like Excel, you have to use the handle to close it:

#create Excel object
$Excel = New-Object -comobject Excel.Application

#.... do stuff...

#Close Excel
$Excel.quit()
[Runtime.Interopservices.Marshal]::ReleaseComObject($excel)   # Release COM
Remove-Variable excel   # Remove the Variable

And similarly if you launch a process like notepad using Start-Process, you can use the -Passthru parameter to get a handle in order for you to close it:

#Run process

$Handle = Start-Process notepad.exe -PassThru

#.... do stuff...

#End process
$Handle | Stop-Process

So, if you want your script to "exit" you have to keep track of everything that you are opening, and close them, stop your other scripts, end all the processes that were spawned... and then finally close your GUI.

--Edit---

Replying to your comment:

Ok. If I understand you, ignore what I said above. You basically want the GUI to show, offer the option of either running the rest of the script, and exit (without continuing and running the remainder of the script) or to carry on and run the rest of the script.

The problem you are having is how PowerShell loads the script. First it creates the form, creates the button and then creates the event handler to handle the button click, and displays the form. It then proceeds to run the rest of your script. Because you are creating the GUI using Windows Forms, it is non-blocking i.e. it doesn't stop the execution of the script to wait for your input before continuing. An example of a blocking command is the Read-Host command:

$Response = Read-Host -Prompt "Do I continue (y/n)?"

... Which would stop the execution of the current script, prompt if you want to continue. and then carry on. You then use a simple if statement afterwards to handle whether or not to continue and run the rest of the script:

if($Response -eq "y")
{
    #Run rest of script
}
#else the script ends and you do nothing.

If you want to handle it with a GUI, like windows forms, then the easiest way is to split your code into two parts.

The first part (GUI.ps1) is your GUI. All it does is display the prompt, and then the script finishes. (Remember, the GUI is created, and continues to run after the script finishes executing)

The second part (Code.ps1) is your code that you want it to execute.

In the GUI code you have 2 buttons, and 2 handlers, you have your "Run" button, and your "Cancel" button.

Your "Run" button code would be:

$handler_RunButton_Click={
      #Run Code script
      . .\Code.ps1
}

Your "Cancel" button has the same code that you currently have:

$handler_cancelButton_Click={
      $MainForm.Close()
}