I have been trying to create a Powershell script that performs the following
Shows a dialog box that an update is about to occur
Provide a countdown of say 30 seconds
During the countdown, a user can press "Cancel Update"
If the countdown expires and "Cancel Update" was not pressed, then update will occur
Right before the loop, the window shows if I call $Counter_Form.ShowDialog() and I can click the Cancel button. When clicking, the following should occur.
Window should close after pressing the button. This is correct.
$cancel should be set to $true to indicate that Cancel was pressed. However, it remains $false and this is incorrect. Why is this?
Now, for the problems in the while loop
The window refreshes to show the new delay, but I cannot click "Cancel Update" since it just shows an hourglass icon and seems to be frozen
Script
#Adjust delay here
$delay = 5
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Counter_Form = New-Object System.Windows.Forms.Form
$Counter_Form.Text = "Warning"
#Form size options
$Counter_Form.Width = 350
$Counter_Form.Height = 150
#Centers form on screen
$Counter_Form.StartPosition = "CenterScreen"
#Places form on top of everything else
$Counter_Form.TopMost = $true
$Counter_Label = New-Object System.Windows.Forms.Label
$Counter_Label2 = New-Object System.Windows.Forms.Label
#Label2's text
$Counter_Label2.Text = "Please save all your work"
#Labels size and position
$Counter_Label.AutoSize = $true
$Counter_Label.Location = New-Object System.Drawing.Point(50,60)
$Counter_Label2.AutoSize = $true
$Counter_Label2.Location = New-Object System.Drawing.Point(90,30)
$cancel = $false
$button1 = New-Object System.Windows.Forms.Button
$button1.Text = "Cancel Update";
$button1.Location = New-Object System.Drawing.Point(130,80)
$button1.Add_Click({ $Counter_Form.Close(); $cancel = $true})
$Counter_Form.Controls.Add($Counter_Label)
$Counter_Form.Controls.Add($Counter_Label2)
$Counter_Form.Controls.Add($button1)
#LOOP!
while ($delay -ge 0 -And $cancel -eq $false)
{
$Counter_Form.Show()
#Timer label's text
$Counter_Label.Text = "Update will occur in $($delay) seconds."
start-sleep 1
$delay -= 1
}
$Counter_Form.Close()
For the first question about $cancel not being updated to $true, it is a result of scope.
Add the script scope, with $script:cancel = $true. So that line of code should be:
$button1.Add_Click({ $Counter_Form.Close(); $script:cancel = $true})
I didn't find an exact solution to what I started. A pieced together some code from various sources and found the following worked. I'm posting it here in case it ends up helping someone:
function ShowMsg ($timeout, $message)
{
Add-Type -AssemblyName system.windows.forms
Add-Type -AssemblyName system.drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = "Window Title"
$form.Size = New-Object System.Drawing.Size(300,300)
$form.StartPosition = 'CenterScreen'
$label = New-Object System.Windows.Forms.label
$label.Text = $message
$label.Size = New-Object System.Drawing.Size(280,205)
$form.Controls.Add($label)
#add button to form
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(160,215)
$okButton.Size = New-Object System.Drawing.Size(100,23)
$okButton.Text = 'Cancel Update'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $okButton
$form.Controls.Add($okButton)
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Point(40,215)
$cancelButton.Size = New-Object System.Drawing.Size(100,23)
$cancelButton.Text = 'Allow Update'
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::CANCEL
$form.CancelButton = $cancelButton
$form.Controls.Add($cancelButton)
$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()
}
Calling the function:
$timeout = 60 #seconds
$message_response = ShowMsg -timeout $timeout -message "Hello, update is happening"
Related
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()
}
I have script that creates pop-up with checkbox.
If checkbox is checked and OK is pressed, $districtArray[1] is True
BUT
If checkbox is checked and CANCEL is pressed, $districtArray[1] is ALSO True
I wish that CANCEL button will make entire $districtArray FALSE.
Hence I included function initArray when CANCEL is clicked
$CancelButton.Add_Click({initArray; $Form.Close()})
And here is entire script for reference
$i = $NULL
$districtArray = #()
$highestDistrict = 33
function initArray{
for ($i = 0; $i -lt $highestDistrict; $i++)
{
$script:districtArray += #($false)
}
}
function checkbox_test{
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
# Set the size of your form
$Form = New-Object System.Windows.Forms.Form
$Form.width = 500
$Form.height = 200
$Form.Text = ”Select District”
# Set the font of the text to be used within the form
$Font = New-Object System.Drawing.Font("Times New Roman",12)
$Form.Font = $Font
# create your checkbox
$checkbox1 = new-object System.Windows.Forms.checkbox
$checkbox1.Location = new-object System.Drawing.Size(30,30)
$checkbox1.Size = new-object System.Drawing.Size(250,50)
$checkbox1.Text = "01"
$checkbox1.Checked = $false
$Form.Controls.Add($checkbox1)
# Add an OK button
$OKButton = new-object System.Windows.Forms.Button
$OKButton.Location = new-object System.Drawing.Size(130,100)
$OKButton.Size = new-object System.Drawing.Size(100,40)
$OKButton.Text = "OK"
$OKButton.Add_Click({$Form.Close()})
$form.Controls.Add($OKButton)
#Add a cancel button
$CancelButton = new-object System.Windows.Forms.Button
$CancelButton.Location = new-object System.Drawing.Size(255,100)
$CancelButton.Size = new-object System.Drawing.Size(100,40)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({initArray; $Form.Close()})
$form.Controls.Add($CancelButton)
########### This is the important piece ##############
# #
# Do something when the state of the checkbox changes #
#######################################################
$checkbox1.Add_CheckStateChanged({
if ($checkbox1.Checked){$districtArray[1] = $true} })
# Activate the form
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
}
initArray
#Call the function
checkbox_test
write-host "Value of districtArray[1] is" $districtArray[1]
What I would like to do is to assign a button to a shortcut key, for example $TurnOnButton as key "Q", and $TurnOffButton as key "W" on my keyboard.
So basically, in my example below. When the script is running and the form is present, I would be able to push button "Q" on my keyboard to run calculator, and press button "W" to terminate it.
Is this possible with PowerShell?
Code example:
Add-Type -AssemblyName System.Windows.Forms
function Return-TurnOff
{
$x = Stop-Process -ProcessName calc
$x
}
function Return-TurnOn
{
$x = Start-Process calc
$x
}
$form = New-Object System.Windows.Forms.Form
$form.Text = "Title of the form"
$form.Size = New-Object System.Drawing.Size(300,200)
$form.minimumSize = New-Object System.Drawing.Size(300,200)
$form.maximumSize = New-Object System.Drawing.Size(300,200)
$form.StartPosition = "CenterScreen"
$TurnOffButton = New-Object System.Windows.Forms.Button
$TurnOffButton.Location = New-Object System.Drawing.Point(10,125)
$TurnOffButton.Size = New-Object System.Drawing.Size(55,25)
$TurnOffButton.Text = "Turn Off"
$TurnOffButton.Add_Click({Return-TurnOff})
$form.AcceptButton = $TurnOffButton
$form.Controls.Add($TurnOffButton)
$TurnOnButton = New-Object System.Windows.Forms.Button
$TurnOnButton.Location = New-Object System.Drawing.Point(10,65)
$TurnOnButton.Size = New-Object System.Drawing.Size(55,25)
$TurnOnButton.Text = "Turn On"
$TurnOnButton.Add_Click({Return-TurnOn})
$form.AcceptButton = $TurnOnButton
$form.Controls.Add($TurnOnButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(215,125)
$CancelButton.Size = New-Object System.Drawing.Size(55,25)
$CancelButton.Text = "Cancel"
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,15)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = "Label for textbox:"
$form.Controls.Add($label)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10,35)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($textBox)
$form.Topmost = $True
$form.Add_Shown({$textBox.Select()})
$result = $form.ShowDialog()
If you don't mind having the label on the button contain the letter that corresponds to the button AND pressing the Alt key, then it's very easy, just put an '&' before the letter in the button text you want to be the accelerator:
$TurnOnButton.Text = "Turn O&n"
$TurnOffButton.Text = "Turn O&ff"
would make Alt-N perform Turn On and Alt-F perform Turn Off.
A more complicated solution is to register for keyboard presses, but it will let you handle any keystroke whether or not Alt is pressed. $form|gm -MemberType event key* will show you the events whose name starts with "Key". You can then google for how to handle events from Powershell with WinForms.
I would like to prompt user to enter a list of passwords, one line at a time. When the person types the passwords, it should appear as *
I have a function
function Read-MultiLineInputBoxDialogPwd([string]$Message, [string]$WindowTitle, [string]$DefaultText){
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms
# Create the label
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Size(10,10)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.AutoSize = $true
$label.Text = $Message
# Create the TextBox used to capture the user's text
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Size(10,40)
$textBox.Size = New-Object System.Drawing.Size(575,200)
$textBox.AcceptsReturn = $true
$textBox.AcceptsTab = $false
$textBox.Multiline = $true
$textBox.ScrollBars = 'Both'
$textBox.Text = $DefaultText
$textBox.UseSystemPasswordChar = $True
# Create the OK button.
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Size(415,250)
$okButton.Size = New-Object System.Drawing.Size(75,25)
$okButton.Text = "OK"
$okButton.Add_Click({ $form.Tag = $textBox.Text; $form.Close() })
# Create the Cancel button.
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Size(510,250)
$cancelButton.Size = New-Object System.Drawing.Size(75,25)
$cancelButton.Text = "Cancel"
$cancelButton.Add_Click({ $form.Tag = $null; $form.Close() })
# Create the form.
$form = New-Object System.Windows.Forms.Form
$form.Text = $WindowTitle
$form.Size = New-Object System.Drawing.Size(610,320)
$form.FormBorderStyle = 'FixedSingle'
$form.StartPosition = "CenterScreen"
$form.AutoSizeMode = 'GrowAndShrink'
$form.Topmost = $True
$form.AcceptButton = $okButton
$form.CancelButton = $cancelButton
$form.ShowInTaskbar = $true
# Add all of the controls to the form.
$form.Controls.Add($label)
$form.Controls.Add($textBox)
$form.Controls.Add($okButton)
$form.Controls.Add($cancelButton)
# Initialize and show the form.
$form.Add_Shown({$form.Activate()})
$form.ShowDialog() > $null # Trash the text of the button that was clicked.
# Return the text that the user entered.
return $form.Tag
}
And I call the function
$multiLineTextPwd = Read-MultiLineInputBoxDialogPwd -Message "All possible passwords" -WindowTitle "Passwords" -DefaultText "Please enter all possible passwords, one line at a time..."
But when it pops up, the text still appears in plaintext, even though I set the following
$textBox.UseSystemPasswordChar = $True
How to fix this?
I honestly feel that this would be better accomplished by having a single-line text box and an 'Add Another Password' button where the user could enter a password, and then click the button to add another password. You would just keep adding them to an array, and would have to make sure that when they submit that it checks for anything in that box and adds anything left to the array before performing actions.
All password masking references when I went and looked at the MSDN listing for the Textbox class all specifically state Single Line Textbox, so it may well be that you can't use masking with a multiline textbox.
If you read the documentation here you'll see that for multiline text boxes, the UseSystemPasswordChar has no effect.
implement keydown event of mutiline textbox, append * into TB, and append key code string into a string variable.
I have created a powershell timer, and after every 1 second, I would like to run a function to execute or post text to a text box log on the interface.
Run the below. When you click Start, every 1 second, the log text area should show "Post to log every 1 second, not as a batch". However, this messages only appear as a batch, all at once, when you click Stop.
This question does not appear to be answered on the internet!
Code:
$global:timer = New-Object System.Timers.Timer
$global:timer.Interval = 1000
function AddToLog($logtext)
{
$txtLog.Text = $txtLog.Text + "`r`n" + $logtext
$txtLog.ScrolltoCaret
}
function startTimer() {
Register-ObjectEvent -InputObject $global:timer -EventName Elapsed -SourceIdentifier theTimer -Action {AddToLog('Post to log every 1 second, not as a batch') }
$global:timer.start()
Write-Host "Start Timer"
}
function stopTimer() {
$global:timer.stop()
Write-Host "Close Function"
Unregister-Event theTimer
}
########################
# Setup User Interface
########################
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Timer Example"
$objForm.Size = New-Object System.Drawing.Size(330,380)
$objForm.StartPosition = "CenterScreen"
#Start Button
$btnStart = New-Object System.Windows.Forms.Button
$btnStart.Location = New-Object System.Drawing.Size(10,190)
$btnStart.Size = New-Object System.Drawing.Size(140,35)
$btnStart.Text = "Start"
$btnStart.Add_Click({StartTimer; })
$objForm.Controls.Add($btnStart)
#Stop Button
$btnStop = New-Object System.Windows.Forms.Button
$btnStop.Location = New-Object System.Drawing.Size(150,190)
$btnStop.Size = New-Object System.Drawing.Size(140,35)
$btnStop.Text = "Stop"
$btnStop.Add_Click({StopTimer; })
$objForm.Controls.Add($btnStop)
$btnStop.Enabled = $true
#Log Area
$lblLog = New-Object System.Windows.Forms.Label
$lblLog.Location = New-Object System.Drawing.Size(10,230)
$lblLog.Size = New-Object System.Drawing.Size(80,20)
$lblLog.Text = "Event Log:"
$objForm.Controls.Add($lblLog)
$txtLog = New-Object System.Windows.Forms.Textbox
$txtLog.Location = New-Object System.Drawing.Size(10,250)
$txtLog.Size = New-Object System.Drawing.Size(290,90)
$txtLog.Multiline = $True
$txtLog.Scrollbars = "vertical"
$txtLog.Add_Click({$txtLog.SelectAll(); $txtLog.Copy()})
$objForm.Controls.Add($txtLog)
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
Thanks for your help in advanced.
-R
I took what you had, and looked at this egg_timer project and came up with the following:
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.add_tick({AddToLog 'Post to log every 1 second, not as a batch'})
function AddToLog($logtext)
{
$txtLog.Text = $txtLog.Text + "`r`n" + $logtext
$txtLog.ScrolltoCaret
}
function startTimer() {
$timer.start()
}
function stopTimer() {
$timer.Enabled = $false
Write-Host "Close Function"
}
########################
# Setup User Interface
########################
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Timer Example"
$objForm.Size = New-Object System.Drawing.Size(330,380)
$objForm.StartPosition = "CenterScreen"
#Start Button
$btnStart = New-Object System.Windows.Forms.Button
$btnStart.Location = New-Object System.Drawing.Size(10,190)
$btnStart.Size = New-Object System.Drawing.Size(140,35)
$btnStart.Text = "Start"
$btnStart.Add_Click({StartTimer; })
$objForm.Controls.Add($btnStart)
#Stop Button
$btnStop = New-Object System.Windows.Forms.Button
$btnStop.Location = New-Object System.Drawing.Size(150,190)
$btnStop.Size = New-Object System.Drawing.Size(140,35)
$btnStop.Text = "Stop"
$btnStop.Add_Click({StopTimer; })
$objForm.Controls.Add($btnStop)
$btnStop.Enabled = $true
#Log Area
$lblLog = New-Object System.Windows.Forms.Label
$lblLog.Location = New-Object System.Drawing.Size(10,230)
$lblLog.Size = New-Object System.Drawing.Size(80,20)
$lblLog.Text = "Event Log:"
$objForm.Controls.Add($lblLog)
$txtLog = New-Object System.Windows.Forms.Textbox
$txtLog.Location = New-Object System.Drawing.Size(10,250)
$txtLog.Size = New-Object System.Drawing.Size(290,90)
$txtLog.Multiline = $True
$txtLog.Scrollbars = "vertical"
$txtLog.Add_Click({$txtLog.SelectAll(); $txtLog.Copy()})
$objForm.Controls.Add($txtLog)
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
Try using a System.Windows.Forms.Timer. See this article for info on the differences between .NET timers.
In the case of a WinForms timer, ditch the Register-ObjectEvent and Unregister-Event and add this line before you call ShowDialog():
$timer.add_Tick({AddToLog('Post to log every 1 second, not as a batch')})
Don't forget to change to System.Windows.Forms.Timer. When I make those change, the UI works.
Use of Register-ObjectEvent requires that PowerShell services the event queue at various safe points in its execution. However, since PowerShell is blocked inside some .NET code (Form.ShowDialog()), it doesn't get a chance to service its event queue.