I'm trying to create a message dialogue in Powershell where the user has no option to action on the message as that is the intention. So the message will have the X button grayed along with the buttons (not showing buttons are even better).
The closest I could reach was disabling the X via below code:
$wshell = New-Object -ComObject Wscript.Shell -ErrorAction Stop
$wshell.Popup("Aborted",0,"ERROR!",48+4)
But cannot figure out disabling button part. Below MS articles were of little help as well:
http://blogs.technet.com/b/heyscriptingguy/archive/2006/07/27/how-can-i-display-a-message-box-that-has-no-buttons-and-that-disappears-after-a-specified-period-of-time.aspx
https://msdn.microsoft.com/en-us/library/x83z1d9f(v=vs.84).aspx
Referred to few other articles over net some even suggesting custom made buttons using HTML, or VB library. But not what I was looking for.
Any help/hint/suggestion would be deeply appreciated.
Regards,
Shakti
Dig into the .NET Windows.Forms namespace, you can make pretty much any kind of window you want with that:
https://msdn.microsoft.com/en-us/library/system.windows.forms.aspx
Here's a quick sample window w/ no buttons that can't be moved/closed by the user, but closes itself after 5 seconds:
Function Generate-Form {
Add-Type -AssemblyName System.Windows.Forms
# Build Form
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Test"
$objForm.Size = New-Object System.Drawing.Size(220,100)
# Add Label
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(80,20)
$objLabel.Size = New-Object System.Drawing.Size(100,20)
$objLabel.Text = "Hi there!"
$objForm.Controls.Add($objLabel)
# Show the form
$objForm.Show()| Out-Null
# wait 5 seconds
Start-Sleep -Seconds 5
# destroy form
$objForm.Close() | Out-Null
}
generate-form
Using the script above as a launching point I'm attempting to make a function that will allow me to popup a please wait message run some more script then close the popup
Function Popup-Message {
param ([switch]$show,[switch]$close)
Add-Type -AssemblyName System.Windows.Forms
# Build Form
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Test"
$objForm.Size = New-Object System.Drawing.Size(220,100)
# Add Label
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(80,20)
$objLabel.Size = New-Object System.Drawing.Size(100,20)
$objLabel.Text = "Hi there!"
$objForm.Controls.Add($objLabel)
If ($show)
{
$objForm.Show() | Out-Null
$global:test = "Show"
}
If ($close)
{
# destroy form
$objForm.Close() | Out-Null
$global:test = "Close"
}
}
I can then get the popup to display by:
Popup-Message -show
At this point I can see the $test variable as Show
But when I try to close the window with:
Popup-Message -close
But the popup window will not close
If I look at $test again it will show as Close
I'm assuming this has something to do with keeping the function in the Global Scope but I can't figure out how to do this with the form
Related
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).
I have a PS script that implements System.Windows.Forms in order to query technicians for some data.
I create the forms and set both .Topmost and .TopLevel to true in an attempt to have them show up over the Powershell window, but they continue to (for some reason inconsistently) appear behind the Powershell window. This slows down the process and is confusing in its inconsistency.
If anyone knows how to ensure these windows stay top without a mountain of code larger than the script itself that would be incredibly useful. I'll include the code I use to build one of the basic forms below.
Any simple solution that will allow these Forms to appear over the Powershell window is appreciated. It could even just minimize the PS window, but I don't want to launch without the window as we need it open. Thanks.
$form.Text = 'Computer Name Entry'
$form.Size = New-Object System.Drawing.Size(550,400)
$form.StartPosition = 'CenterScreen'
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(75,300)
$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)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(400,40)
$label.Text = 'Text is here:'
$form.Controls.Add($label)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10,70)
$textBox.Size = New-Object System.Drawing.Size(400,20)
$form.Controls.Add($textBox)
$form.Topmost = $true
$form.TopLevel = $true
$form.Add_Shown({$textBox.Select()})
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
Do-Stuff
}
The easiest way I know of forcing the form to be topmost is to open it with a new temporary form that is TopMost as parameter for ShowDialog().
First, from your code remove the lines $form.Topmost = $true and $form.TopLevel = $true
Next, show your form like this:
# force the dialog TopMost by creating a temporary parent window for this form
$result = $form.ShowDialog((New-Object System.Windows.Forms.Form -Property #{TopMost = $true }))
Another way of doing this is to use a piece of C# to return a windowhandle which implements the IWin32Window interface.
Then use this handle as the owner window for this form in the .ShowDialog() method of the form.
For this method, also remove the lines $form.Topmost = $true and $form.TopLevel = $true from your original code.
$iWin32Code = #"
using System;
using System.Windows.Forms;
public class Win32Window : IWin32Window {
public Win32Window(IntPtr handle) {
Handle = handle;
}
public IntPtr Handle { get; private set; }
}
"#
if (-not ([System.Management.Automation.PSTypeName]'Win32Window').Type) {
Add-Type -TypeDefinition $iWin32Code -ReferencedAssemblies System.Windows.Forms.dll
}
Now, using that code, create a handle for the currently running PowerShell process
# get the owner handle from this PowerShell process
$ownerHandle = New-Object Win32Window -ArgumentList ([System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle)
# and use that in the ShowDialog method as argument
$result = $form.ShowDialog($ownerHandle)
P.S. do not forget to clear your form from memory after you are done with it by calling
$form.Dispose()
Im trying to build a gui form using powershell, i want to add a button
$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 = "Show Dialog Box"
$Form.Controls.Add($Button)
$Button.Add_Click($Button_Click)
now i want to create a function called button_click with a few commands, lets say: echo "Hello world"
so i write this:
Function Button_Click(){
echo "Hello World"}
But clicking on the button wont give me any result, what am i doing wrong here?
You were using Write-Output which will write to the pipeline and in this case will not be displayed because your PowerShell console is locked while the $form.ShowDialog() is active.
However you can still do this! Write-host is another cmdlet for returning output and it can directly write to the PowerShell host window in real time. This is one of those rare times when you probably do want to use Write-Host.
Then, some small tweaks to make. Your event handler behavior should generally be defined before adding the $button to the $Form.
You made a function named Button_click, but you add the event handler like it is a variable. Here's how to do that instead, by making a variable which contains a {scriptblock}:
$button_click = {write-host "hi Sahar"}
And with that done, your code should look like this, and it will work as expected
$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 = "Show Dialog Box"
$Button.Add_Click($Button_Click)
$Form.Controls.Add($Button)
$form.showdialog()
I am trying to achieve the following
User clicks a button
Label1 appears at the top left (text = "Hi there")
Label1 disappears after 5 seconds
I have tried to work with the timer function but the Interval setting just appears to work as it states, in intervals.
I don't want to use the Start-Sleep option because I need the form to still be active while this prompt appears.
$timerPrompt = New-Object System.Windows.Forms.Timer
$timerPrompt.Interval = 3000
$timerPrompt.Add_Tick({$form.Controls.Remove($label1)})
As Drew already pointed out, your code does not show where you start, stop or dispose of the timer object.
This very simple form below creates a timer that gets started when the user clicks the button. In the Tick event of the timer it shuts itself down again.
Instead of removing the label, it hides and unhides it from view .
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Test Timer'
$form.Size = New-Object System.Drawing.Size(300,200)
$form.MinimumSize = New-Object System.Drawing.Size(300,150)
$form.StartPosition = "CenterScreen"
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(($form.Width - 20),50)
$label.Text = "Hi There"
$label.Anchor = "Top","Left","Right"
$label.Visible = $false
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 5000
$timer.Add_Tick({
# hide the label and stop the timer
$label.Visible = $false
$timer.Stop()
})
$button = New-Object System.Windows.Forms.Button
$button.Location = New-Object System.Drawing.Point(($form.Width - 185),($form.Height - 80))
$button.Size = New-Object System.Drawing.Size(75,23)
$button.Anchor = "Bottom","Left","Right"
$button.Text = "&Click Me"
$button.Add_Click({
# show the label and start the timer
$label.Visible = $true
$timer.Start()
})
# add the controls to the form
$form.Controls.Add($label)
$form.Controls.Add($button)
[void]$form.ShowDialog()
# when done, clean up the objects
$timer.Stop()
$timer.Dispose()
$form.Dispose()
Hope that explains
I am not very good with GUI programming but I have a trick that will solve that problem for you as I have used it many of my GUI programs.
for ($i = 0; $i -lt 25; $i++)
{
Start-Sleep -Milliseconds 200
[System.Windows.Forms.Application]::DoEvents()
}
Be advised that this is not ideal but I can't argue with the results. The little loop will give you a delay of 5 seconds while the DoEvents() part will keep ur form from going non responsive. Basically it checks for interrupts every 200ms giving the impression that the form is active.
Make the loop count 50 and reduce time to 100ms if you like. same results but extra responsiveness.
I am trying to run a powershell script remotely that displays a pop-up alert. We are attempting to create a sort of "Emergency Notification System". On the admin machine there is a script to choose which emergency alert to send to everyone, and then it should run/appear on everyone's screen.
I've used the /msg command to achieve this, but the message is so plain that it doesn't catch the user's attention and the text can't be customized (unless it can be, which in that case PLEASE enlighten me).
I am receiving the error below when attempting to do this. I've also attempted to do it via PsExec but receive the same error.
Error:
Exception calling "ShowDialog" with "0" argument(s): "Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application."
Below is the PowerShell script. It's nothing fancy, just wanting something that works for our purpose.
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object system.Windows.Forms.Form
$Form.Text = "Disaster Alert!"
$Form.AutoScroll = $True
$Form.AutoSize = $True
$Form.AutoSizeMode = "GrowAndShrink"
$Form.WindowState = "Normal"
$Form.StartPosition = "CenterScreen"
$Form.Font = New-Object System.Drawing.Font("Calibri",60,[System.Drawing.FontStyle]::Bold)
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(500,200)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "Close"
$OKButton.Font = New-Object System.Drawing.Font("Calibri",11)
$OKButton.UseVisualStyleBackColor = $True
$OKButton.Add_Click({$Form.Close()})
$Form.Controls.Add($OKButton)
$Label = New-Object System.Windows.Forms.Label
$Label.Text = "Please Exit the Building`nBuilding Fire Alarm Sounding!!"
$Label.ForeColor = "Red"
$Label.TextAlign = "MiddleCenter"
$Label.AutoSize = $True
$Form.Controls.Add($Label)
$Form.ShowDialog()
Is it possible to have a pop-up via PowerShell? If not, does anyone have any recommendations?
Thank you very much in advance.