Powershell pervent GUI from closing when executing a application with a button - powershell

In my Powershell GUI i'd like to press a button and open notepad without having the GUI closing in the background.
The dialog closes immediately with the following code
$button.Add_Click({Start-Process notepad.exe $file}) ;
When using -Wait the dialog will stay open until i close notepad but still close the
$button.Add_Click({Start-Process -Wait notepad.exe $file}) ;
The same happens when using variables
$button.Add_Click({& $notepad $file}) ;
the following is the full code block:
$file = '*\file.txt'
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = "Don't close"
$form.Size = New-Object System.Drawing.Size(280,160)
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'FixedDialog'
$form.Topmost = $true
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$button = New-Object System.Windows.Forms.Button
$button.Location = New-Object System.Drawing.Point(20,40)
$button.Size = New-Object System.Drawing.Size(160,23)
$button.Text = "button"
$button.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $button
$form.Controls.Add($button)
$button.Add_Click({Start-Process -Wait notepad.exe $file}) ;
$form.ShowDialog()
Where am i going wrong?

This is because you are setting the button to be the Form's AcceptButton and also the control that gets the DialogResult.
Simply remove two lines (to show which ones, I add the code commented out below).
Then, also do not use the -Wait switch on the Start-Process.
The code adjusted:
$file = 'D:\blah.txt'
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = "Don't close"
$form.Size = New-Object System.Drawing.Size(280,160)
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'FixedDialog'
$form.Topmost = $true
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$button = New-Object System.Windows.Forms.Button
$button.Location = New-Object System.Drawing.Point(20,40)
$button.Size = New-Object System.Drawing.Size(160,23)
$button.Text = "button"
# drop these two lines
# $button.DialogResult = [System.Windows.Forms.DialogResult]::OK
# $form.AcceptButton = $button
$form.Controls.Add($button)
# do not use the -Wait parameter on Start-Process here
$button.Add_Click({Start-Process notepad.exe $file}) ;
$form.ShowDialog()
$form.Dispose()

Related

Powershell Winforms doesn't execute button_click action locally

I'm making winforms with powershell for the first time.
I was working hard on it. but I ran into a problem. I tried to solve it by myself, but I don't know..
When I run the below code in powershell ise, $button_click works.
In other words, it worked fine in ise.
However, after saving to the desktop, $button_click does not work when running through cmd.
# start.bat
C:\Users\User\Desktop> powershell -ExecutionPolicy Unrestricted %CD%\code.ps1
I wrote the following code:
# code.ps1
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
# form specs
$Form = New-object System.Windows.Forms.Form
$Form.Text = "Check Network Status"
$Form.Size = New-object System.Drawing.Size(700,700)
$Form.StartPosition = "CenterScreen"
$Form.FormBorderStyle = "None"
$Form.KeyPreview = $True
$Form.MaximumSize = $Form.Size
$Form.MinimumSize = $Form.Size
$Form.BackColor = "Lavender"
# form icon
$Icon = New-object system.drawing.icon ("icon.ico")
$Form.Icon = $Icon
# form image
$Image = [system.drawing.image]::FromFile("img.jpg")
$PictureBox = New-Object System.Windows.Forms.PictureBox
$PictureBox.Image = $Image
$PictureBox.Size = New-object System.Drawing.Size(700,550)
$PictureBox.SizeMode = "StretchImage"
$Form.Controls.Add($PictureBox)
# ok button
$ButtonSizeWidth = 200
$ButtonSizeHeight = 50
$Button = New-object System.Windows.Forms.Button
$Font = New-Object System.Drawing.Font("Arial",20)
$Button.Location = New-object System.Drawing.Size((350 - $ButtonSizeWidth / 2),(650 - $ButtonSizeHeight))
$Button.Size = New-object System.Drawing.Size($ButtonSizeWidth,$ButtonSizeHeight)
$Button.Text = "Okay"
$Button.BackColor = "PowderBlue"
$Button.FlatAppearance.BorderSize = 0
$Button.FlatStyle = "Flat"
$Button.Font = $Font
$Button.Add_Click($button_click)
$Form.Controls.Add($Button)
# action here
$button_click =
{
# netsh int set int admin=disable
$Form.Close()
}
$Form.Add_KeyDown({if ($_.KeyCode -eq "Enter"){& $button_click}})
$Form.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$Form.Close()}})
# modal
$Form.Topmost = $True
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
Obviously it works fine in powershell ise, but I want to know why it doesn't work when I save and run it.
Is there something I'm missing out on?

Trying to make a basic Ping tool with a simple GUI, However at the moment the ping button does nothing even though I've defined it to have an action

However at the moment the ping button does nothing even though I've defined it to have an action.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Data Entry Form'
$form.Size = New-Object System.Drawing.Size(400,400)
$form.StartPosition = 'CenterScreen'
$form = New-Object System.Windows.Forms.Form
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(35,35)
$Button.Size = New-Object System.Drawing.Size(120,23)
$Button.Text = "Ping"
$Button.Add_Click($Button_Click)
$Form.Controls.Add($Button)
$form.showdialog()
$Button_Click = $pinger
$pinger = Ping 8.8.8.8 -t > C:\Tools\Pingtoool.txt
$form.Topmost = $true
$form.Add_Shown({$textBox.Select()})
$result = $form.ShowDialog()
If anyone could take a look and perhaps recommend better ways of doing things, I would gladly be all ears.
Thank you.
You just need to store your event as a scriptblock, in addition to this, the scriptblock should be defined before adding it to the Click event of your button. I have added a few modifications to your code, test it now :)
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Data Entry Form'
$form.Size = New-Object System.Drawing.Size(400,400)
$form.StartPosition = 'CenterScreen'
$form.Topmost = $true
$pingEvent = {
$file = New-TemporaryFile
Test-Connection 8.8.8.8 | Out-File $file
notepad.exe $file
}
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(35,35)
$Button.Size = New-Object System.Drawing.Size(120,23)
$Button.Text = "Ping"
$Button.Add_Click($pingEvent)
$Form.Controls.Add($Button)
$form.Add_Shown({$Button.Select()})
$result = $form.ShowDialog()
A few considerations:
ping -t would make your GUI freeze since it will run until being manually stopped.
ping has been changed for Test-Connection as it's PowerShell's built-in cmdlet intended for pinging.
$pinger = Ping 8.8.8.8 -t > C:\Tools\Pingtoool.txt is being executed at the moment this line is interpreted, if you want to execute this line when clicking the button you would store the code as a scriptblock:
$pinger = { Ping 8.8.8.8 -t > C:\Tools\Pingtoool.txt }
$Button.Add_Click($pinger)
It can be also simplified to:
$Button.Add_Click({
Ping 8.8.8.8 -t > C:\Tools\Pingtoool.txt
})
On line $Button.Add_Click($Button_Click), $Button_Click is being added as an event before being defined which is why when you click nothing happens (in addition to what was mentioned before, the event not being a scriptblock).

Show popup message with auto close and on top of all of the windows

I need to create a function that shows a message to the user and if the user doesn't respond then it automatically closes the windows.
function ShowMsg ($timeout,$message)
{
Add-Type -AssemblyName system.windows.forms
Add-Type -AssemblyName system.drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = "Company Name"
$form.Size = New-Object System.Drawing.Size(300,200)
$form.StartPosition = 'CenterScreen'
$label = New-Object System.Windows.Forms.label
$label.Text = $message
$label.Size = New-Object System.Drawing.Size(200,40)
$form.Controls.Add($label)
#add button to form
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(75,120)
$okButton.Size = New-Object System.Drawing.Size(75,23)
$okButton.Text = 'OK'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $okButton
$form.Controls.Add($okButton)
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = $timeout * 1000
$timer.add_tick({$form.Close()})
$timer.Start()
$form.Topmost = $true
$form.ShowDialog()
$form.Dispose()
}
$timeout = 30
ShowMsg -message “This is the message you will see on the window`nMessage on new line” -timeout $timeout
$r = ShowMsg -message “This is the message you will see on the window`nMessage on new line” -timeout $timeout
if ($r -eq [System.Windows.Forms.DialogResult]::OK)
{
Write-Host “Press ok now”
}else{
Write-Host "$r"
}
I try to use the above code but after showing the message 3 or 4 times it starts closing the popup and in the output, I see "cancel"
enter image description here
The problem is with the timer.
In the form declaration, where you create the timer object, initially set it to $timer.Enabled = $false.
Next, add a $form.Add_Shown({$timer.Enabled = $true; $timer.Start()}) event handler to start the timer when the form is first shown.
In the Tick event of the timer, tell it to Stop() and close the form.
Don't forget to dispose of the timer aswell:
Try
function ShowMsg ([int]$timeout, [string]$message){
Add-Type -AssemblyName system.windows.forms
Add-Type -AssemblyName system.drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = "Company Name"
$form.Size = New-Object System.Drawing.Size(300,200)
$form.StartPosition = 'CenterScreen'
$form.Topmost = $true
$label = New-Object System.Windows.Forms.label
$label.Text = $message
$label.Location = New-Object System.Drawing.Point(10,10)
$label.Size = New-Object System.Drawing.Size(200,40)
$form.Controls.Add($label)
#add button to form
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(75,120)
$okButton.Size = New-Object System.Drawing.Size(75,23)
$okButton.Text = 'OK'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.Controls.Add($okButton)
$form.AcceptButton = $okButton
$timer = New-Object System.Windows.Forms.Timer
$timer.Enabled = $false # disabled at first
$timer.Interval = $timeout * 1000
$timer.Add_Tick({ $timer.Stop(); $form.Close() })
# start the timer as soon as the form is shown
$form.Add_Shown({$timer.Enabled = $true; $timer.Start()})
$form.ShowDialog()
# clean-up
$timer.Dispose()
$form.Dispose()
}

Script runs with ISE but not powershell.exe

After months of "scripting" I finally got my script working as I need it to, except it only does what I want when running it from ISE. When I start it using powershell.exe it throws a fit something about unable to find [system.windows.forms. "dialogresult]".
I have attached the relevant portion of the script, TYIA
$cred = Get-Credential
$Job = Start-Job -ScriptBlock {
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Admin Tools’
$form.Size = New-Object System.Drawing.Size(900,600)
$form.StartPosition = 'CenterScreen'
$form.AutoSize = $true
$form.MaximizeBox = $false
$form.FormBorderStyle = 'FixedSingle'
$img = [System.Drawing.Image]::Fromfile("c:\users\$env:username\Pictures\logo.png")
$pictureBox = New-Object Windows.Forms.PictureBox
$pictureBox.Width = $img.Size.Width
$pictureBox.Height = $img.Size.Height
$pictureBox.Location = New-Object System.Drawing.Size(600,465)
$pictureBox.Image = $img
$form.controls.add($pictureBox)
$ADUCButton = New-Object System.Windows.Forms.Button
$ADUCButton.Location = New-Object System.Drawing.Point(10,25)
$ADUCButton.Size = New-Object System.Drawing.Size(300,100)
$ADUCButton.Font = New-Object System.Drawing.Font(“Times New Roman”,14, [System.Drawing.Fontstyle]::Bold)
$ADUCButton.Text = ' Active Directory Users and Computers '
$ADUCButton.Add_Click({Start-Process -filepath 'c:\windows\system32\cmd.exe' -WindowStyle maximized})
$ADUCButton.FlatAppearance.BorderColor = [System.Drawing.Color]::DarkBlue
$ADUCButton.BackColor = [System.Drawing.Color]::CornflowerBlue
$form.Controls.Add($ADUCButton)
$label = New-Object System.Windows.Forms.Label
$label.location = New-Object System.Drawing.Point(100,500)
$label.Size = New-Object System.Drawing.Size(280,70)
$label.Font = New-Object System.Drawing.Font("Lucida Console",8,
[System.Drawing.FontStyle]::Italic)
$label.Text = 'Created by a PowerShell Novice'
$form.Controls.Add($label)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(850,300)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = 'Close'
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$x = $listBox.SelectedItems
$x
}
} -Credential $cred
Recieve-Job $job
Any assistance you can provide is greatly appreciated.
When you are in the ISE it will autoload modules needed to do many things, the consoelhost does not.
If you have form code in your scripts, you must at needed resources, for it to properly run in the consolehost.
Put this at the top of your script. Here is a snippet I keep in for functions
# Initialize GUI resources
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName presentationframework
Add-type -AssemblyName microsoft.VisualBasic
[System.Windows.Forms.Application]::EnableVisualStyles()
Add-Type -AssemblyName System.Drawing
# Required for use with web SSL sites
[Net.ServicePointManager]::
SecurityProtocol = [Net.ServicePointManager]::
SecurityProtocol -bor
[Net.SecurityProtocolType]::
Tls12
You don't need them all, depending on what you are doing or plan to. At the minimum, you need this...
Add-Type -AssemblyName System.Windows.Forms
BTW, this is probably a posting error, but this line is not syntactically correct.
if ($result -eq [System.Windows.Forms.DialogResult}::OK)
It should be this...
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
Open/Close bracket type must match

How do I properly use a function in a do/while loop?

I have created a function that creates a specific UI element with text input forms. This form also has three buttons:
One is supposed to display the text inputs again to do the same function again.
Another is supposed to finish and close the UI, passing the text input into the variables.
The last is supposed to cancel the UI element, doing nothing with anything in the text input forms.
Now I know the loop in the code isn't really complete, but I am having issues with it even performing the loop as well as passing the text forms into the variables. I know its something I am doing but it seems correct to me when I look at it.
Changed from an if loop, a while loop, and now a do/while loop.
Changed the position of the variables between the do section into the while section. Same for the if and while loops.
do {
ChangeDesc
}
while ($result -eq [System.Windows.Forms.DialogResult]::Retry)
{
$PC = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $HNInputBox.Text
$PC.Description = $DescInputBox.Text
$PC.Put()
}
ChangeDesc is the name of the function and works just as intended.
Expected to work is to loop the function 'ChangeDesc', and then when the 'Retry' or 'Ok' button is pressed, pass those forms to the variables as shown.
Currently, it will display the form, and when the 'Retry' button is pressed, the forms are passed properly and then the UI is closed out, the 'Ok' button does not pass any input and the 'Cancel' does the same thing.
Below is the rest of my lines of code for clarification.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function ChangeDesc {
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Data Entry Form'
$form.Size = New-Object System.Drawing.Size(300,210)
$form.StartPosition = 'CenterScreen'
$AnotherButton = New-Object System.Windows.Forms.Button
$AnotherButton.Location = New-Object System.Drawing.Point(15,130)
$AnotherButton.Size = New-Object System.Drawing.Size(75,23)
$AnotherButton.Text = 'Another?'
$AnotherButton.DialogResult = [System.Windows.Forms.DialogResult]::Retry
$form.AcceptButton = $AnotherButton
$form.Controls.Add($AnotherButton)
$FinishedButton = New-Object System.Windows.Forms.Button
$FinishedButton.Location = New-Object System.Drawing.Point(100,130)
$FinishedButton.Size = New-Object System.Drawing.Size(75,23)
$FinishedButton.Text = 'Finished'
$FinishedButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.CancelButton = $FinishedButton
$form.Controls.Add($FinishedButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(185,130)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = 'Cancel'
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
$HNLabel = New-Object System.Windows.Forms.Label
$HNLabel.Location = New-Object System.Drawing.Point(10,20)
$HNLabel.Size = New-Object System.Drawing.Size(280,20)
$HNLabel.Text = 'Enter Host Name:'
$form.Controls.Add($HNLabel)
$HNInputBox = New-Object System.Windows.Forms.TextBox
$HNInputBox.Location = New-Object System.Drawing.Point(10,40)
$HNInputBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($HNInputBox)
$DescLabel = New-Object System.Windows.Forms.Label
$DescLabel.Location = New-Object System.Drawing.Point(10,70)
$DescLabel.Size = New-Object System.Drawing.Size(280,20)
$DescLabel.Text = 'Enter Description:'
$form.Controls.Add($DescLabel)
$DescInputBox = New-Object System.Windows.Forms.TextBox
$DescInputBox.Location = New-Object System.Drawing.Point(10,90)
$DescInputBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($DescInputBox)
$form.Topmost = $true
$form.Add_Shown({$HNInputBox.Select()})
$result = $form.ShowDialog()
}
Your form is already closed when the loop terminates, and the variables you're trying to use are local to your function. Assign the values you're trying to use to script- or global-scope variables at the end of the function, and the code should do what you expect:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function ChangeDesc {
$form = New-Object Windows.Forms.Form
...
$script:result = $form.ShowDialog()
$script:hostname = $HNInputBox.Text
$script:description = $DescInputBox.Text
}
do {
ChangeDesc
} while ($script:result -eq [Windows.Forms.DialogResult]::Retry)
$PC = Get-WmiObject Win32_OperatingSystem -Computer $script:hostname
$PC.Description = $script:description
$PC.Put()