Powershell: Quitting script from a Windows Form

oynaz

Platinum Member
May 14, 2003
2,449
2
81
Hi guys,

I wonder if this is doable?
I am making a script (which is turning into more of an application), and during this I want the user to be able to click OK or quit at certain points.

I am using Windows.Forms to present the script to the user. How do I make the script accept an exit command?

I have tried this:

Code:
#Load .NET classes nescessary for creating boxes
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

function quittest {	
#Add form and controls
	$objForm = New-Object System.Windows.Forms.Form 
	$objForm.Text = "Quit test"
	$objForm.Size = New-Object System.Drawing.Size(400,600) 
	$objForm.StartPosition = "CenterScreen"

	#Add text box label for presentation
	$objLabel = New-Object System.Windows.Forms.Label
	$objLabel.Location = New-Object System.Drawing.Size(5,10) 
	$objLabel.Size = New-Object System.Drawing.Size(390,490) 
	$objLabel.Text = "blah blah"
	$objForm.Controls.Add($objLabel)
	
	#Create OK button
	$OKButton = New-Object System.Windows.Forms.Button
	$OKButton.Location = New-Object System.Drawing.Size(225,505)
	$OKButton.Size = New-Object System.Drawing.Size(75,23)
	$OKButton.Text = "OK"
	$OKButton.Add_Click({
	$objForm.Close()})
	$objForm.Controls.Add($OKButton)
	
	#Create Quit button
	$QuitButton = New-Object System.Windows.Forms.Button
	$QuitButton.Location = New-Object System.Drawing.Size(100,505)
	$QuitButton.Size = New-Object System.Drawing.Size(75,23)
	$QuitButton.Text = "Quit"
	$QuitButton.Add_Click({
	exit})
	$objForm.Controls.Add($QuitButton)
	
	#Activate form
	$objForm.Add_Shown({$objForm.Activate()})
	[void] $objForm.ShowDialog()
}

quittest

But this throws an exception, any ideas?:


************** Exception Text **************
System.Management.Automation.ExitException: System error.
at System.Management.Automation.FlowControlNode.Execute(Array input, Pipe outputPipe, ExecutionContext context)
at System.Management.Automation.ParseTreeNode.Execute(Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context)
at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context)
at System.Management.Automation.StatementListNode.Execute(Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context)
at System.Management.Automation.ScriptBlock.InvokeWithPipe(Boolean useLocalScope, Boolean writeErrors, Object dollarUnder, Object input, Object scriptThis, Pipe outputPipe, ArrayList& resultList, Object[] args)
at System.Management.Automation.ScriptBlock.InvokeAsDelegateHelper(Object dollarUnder, Object dollarThis, Object[] args)
at System.Management.Automation.ScriptBlock.InvokeAsDelegate[T1,T2](T1 t1, T2 t2)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
 

KB

Diamond Member
Nov 8, 1999
5,406
389
126
its an issue with trying to exit the powershell session from a thread. You are going to need do a workaround like editing a variable and have the script behave different ways on that variable. Liek this:

Code:
#Load .NET classes nescessary for creating boxes
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$objForm = New-Object System.Windows.Forms.Form

[bool] $global:QUIT = $false

function quittest {	
#Add form and controls
	
	$objForm.Text = "Quit test"
	$objForm.Size = New-Object System.Drawing.Size(400,600) 
	$objForm.StartPosition = "CenterScreen"

	#Add text box label for presentation
	$objLabel = New-Object System.Windows.Forms.Label
	$objLabel.Location = New-Object System.Drawing.Size(5,10) 
	$objLabel.Size = New-Object System.Drawing.Size(390,490) 
	$objLabel.Text = "blah blah"
	$objForm.Controls.Add($objLabel)
	
	#Create OK button
	$OKButton = New-Object System.Windows.Forms.Button
	$OKButton.Location = New-Object System.Drawing.Size(225,505)
	$OKButton.Size = New-Object System.Drawing.Size(75,23)
	$OKButton.Text = "OK"
	$OKButton.Add_Click({
	$objForm.Close()})
	$objForm.Controls.Add($OKButton)
	
	#Create Quit button
	$QuitButton = New-Object System.Windows.Forms.Button
	$QuitButton.Location = New-Object System.Drawing.Size(100,505)
	$QuitButton.Size = New-Object System.Drawing.Size(75,23)
	$QuitButton.Text = "Quit"
	$QuitButton.Add_Click({
	exitme})
	$objForm.Controls.Add($QuitButton)
	
	#Activate form
	$objForm.Add_Shown({$objForm.Activate()})
	[void] $objForm.ShowDialog()
    
    Write-Host $global:QUIT
    
    if($global:QUIT -eq $false){
        Write-Host "Continuing"
    } else {
        Write-Host "Quiting"
    }
}

function exitme{
  $global:QUIT = $true
  
  $objForm.Close()
}

quittest
 

oynaz

Platinum Member
May 14, 2003
2,449
2
81
Thanks for the reply.

Some questions (I am still and newbie, so bear with me):

1) Why do you declare the variables as Global? Wouldn't it make more sense to declare them as Script?

2) Your changes still only closes the form, without ending the script, right? I modified them a bit, thus:

Code:
#Load .NET classes nescessary for creating boxes
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$objForm = New-Object System.Windows.Forms.Form

[bool] $script:QUIT = $false

function quittest {	
#Add form and controls
	
	$objForm.Text = "Quit test"
	$objForm.Size = New-Object System.Drawing.Size(400,600) 
	$objForm.StartPosition = "CenterScreen"

	#Add text box label for presentation
	$objLabel = New-Object System.Windows.Forms.Label
	$objLabel.Location = New-Object System.Drawing.Size(5,10) 
	$objLabel.Size = New-Object System.Drawing.Size(390,490) 
	$objLabel.Text = "blah blah"
	$objForm.Controls.Add($objLabel)
	
	#Create OK button
	$OKButton = New-Object System.Windows.Forms.Button
	$OKButton.Location = New-Object System.Drawing.Size(225,505)
	$OKButton.Size = New-Object System.Drawing.Size(75,23)
	$OKButton.Text = "OK"
	$OKButton.Add_Click({
	$objForm.Close()
	$script:QUIT = $false})
	$objForm.Controls.Add($OKButton)
	
	#Create Quit button
	$QuitButton = New-Object System.Windows.Forms.Button
	$QuitButton.Location = New-Object System.Drawing.Size(100,505)
	$QuitButton.Size = New-Object System.Drawing.Size(75,23)
	$QuitButton.Text = "Quit"
	$QuitButton.Add_Click({
	exitme})
	$objForm.Controls.Add($QuitButton)
	
	#Activate form
	$objForm.Add_Shown({$objForm.Activate()})
	[void] $objForm.ShowDialog()
    
    Write-Host $script:QUIT
    
    if($script:QUIT -eq $false){
        Write-Host "Continuing"
    } else {
        Write-Host "Quiting"
    }
}

function exitme{
  $script:QUIT = $true
  
  $objForm.Close()
}

quittest

if ($script:QUIT = $true){exit}

Write-Host "If you can read this, you did not quit"

Now, the Quit button works, but the OK button ends the scrip as well. I think the problem is my if statement. What am I doing wrong here? Do I need an Else ?
 

KB

Diamond Member
Nov 8, 1999
5,406
389
126
I used $global just because its easier to undestand than $script. $script is the child scope of $global, which is more confusion than its worth, but effectively acts the same in this case.

http://blogs.technet.com/b/heyscrip...02/11/hey-scripting-guy-february-11-2010.aspx

The way I wrote the code I never needed to call exit because my code body would be in the if statement. Once the code was executed it would exit on its own. Replace Write-Host "Continuing" with whatever code to execute when continueing.

Doing it your way is fine too, but
if ($script:QUIT = $true){ is actually an assignment. Use
if ($script:QUIT -eq $true){ to perform a comparison
 

oynaz

Platinum Member
May 14, 2003
2,449
2
81
OK, I get it now. Thanks for clearing that up :)

And thanks for the -eq pointer as well. I can get it to work now.
 

forjo

Junior Member
Jul 9, 2014
1
0
0
No need for a workaround.

Try

[System.Environment]::Exit(0)

instead of

Exit