• We’re currently investigating an issue related to the forum theme and styling that is impacting page layout and visual formatting. The problem has been identified, and we are actively working on a resolution. There is no impact to user data or functionality, this is strictly a front-end display issue. We’ll post an update once the fix has been deployed. Thanks for your patience while we get this sorted.

Powershell: Quitting script from a Windows Form

oynaz

Platinum Member
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)
 
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
 
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 ?
 
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
 
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.
 
Back
Top