Remote execute powershell script that displays a form - powershell

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.

Related

How to ensure Forms are the top window

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()

Making a powershell form button call to a function

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()

Creating a powershell GUI that calls seperate powershell scripts

Can someone point me in the right direction? I want to create a powershell script that opens up to a module with check boxes and fields.
Say I have individual powershell scripts that I want to run on a server. I want to be able to have them all in the window and be able to toggle them on and off (with a checkbox) as needed. Here is an example of some code and the GUI I want to make.
So each check box would add the respective code to the list to run. I should be able to get the GUI setup. Its the run button, getting the checkbox to call up a script and the variable field I'm not too sure about.
Screenshot
Here is the code I am working with
#This creates the form and sets its size and position
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Server Setup / Install Roles"
$objForm.Size = New-Object System.Drawing.Size(800,800)
#This creates a checkbox called Set Remote Execution to: unrestricted
$objSet_RECheckbox = New-Object System.Windows.Forms.Checkbox
$objSet_RECheckbox.Location = New-Object System.Drawing.Size(10,10)
$objSet_RECheckbox.Size = New-Object System.Drawing.Size(500,20)
$objSet_RECheckbox.Text = "Set Remote Execution to: unrestricted"
$objSet_RECheckbox.TabIndex = 1
$objForm.Controls.Add($objSet_RECheckbox)
#This creates a checkbox called Change time zone
$objTimeCheckbox = New-Object System.Windows.Forms.Checkbox
$objTimeCheckbox.Location = New-Object System.Drawing.Size(10,30)
$objTimeCheckbox.Size = New-Object System.Drawing.Size(500,20)
$objTimeCheckbox.Text = "Change time zone"
$objTimeCheckbox.TabIndex = 2
$objForm.Controls.Add($objTimeCheckbox)
#This creates a checkbox called Install DNS Role
$objDNSCheckbox = New-Object System.Windows.Forms.Checkbox
$objDNSCheckbox.Location = New-Object System.Drawing.Size(10,50)
$objDNSCheckbox.Size = New-Object System.Drawing.Size(500,20)
$objDNSCheckbox.Text = "Install DNS Role"
$objDNSCheckbox.TabIndex = 3
$objForm.Controls.Add($objDNSCheckbox)
#This creates a checkbox called Install DHCP Role
$objDHCPCheckbox = New-Object System.Windows.Forms.Checkbox
$objDHCPCheckbox.Location = New-Object System.Drawing.Size(10,70)
$objDHCPCheckbox.Size = New-Object System.Drawing.Size(500,20)
$objDHCPCheckbox.Text = "Install DHCP Role"
$objDHCPCheckbox.TabIndex = 4
$objForm.Controls.Add($objDHCPCheckbox)
#This creates a checkbox called Install Print Role
$objPrintCheckbox = New-Object System.Windows.Forms.Checkbox
$objPrintCheckbox.Location = New-Object System.Drawing.Size(10,90)
$objPrintCheckbox.Size = New-Object System.Drawing.Size(500,20)
$objPrintCheckbox.Text = "Install Print Role"
$objPrintCheckbox.TabIndex = 5
$objForm.Controls.Add($objPrintCheckbox)
#This creates a checkbox called Install AD and DC promo
$objAD_NewCheckbox = New-Object System.Windows.Forms.Checkbox
$objAD_NewCheckbox.Location = New-Object System.Drawing.Size(10,110)
$objAD_NewCheckbox.Size = New-Object System.Drawing.Size(500,20)
$objAD_NewCheckbox.Text = "Install AD and DC promo"
$objAD_NewCheckbox.TabIndex = 6
$objForm.Controls.Add($objAD_NewCheckbox)
#This creates a label for the DomainMode TextBox1
$objLabel1 = New-Object System.Windows.Forms.Label
$objLabel1.Location = New-Object System.Drawing.Size(10,140)
$objLabel1.Size = New-Object System.Drawing.Size(280,20)
$objLabel1.Text = "Domain Mode"
$objForm.Controls.Add($objLabel1)
#This creates the DomainMode TextBox1
$objTextBox1 = New-Object System.Windows.Forms.TextBox
$objTextBox1.Location = New-Object System.Drawing.Size(10,160)
$objTextBox1.Size = New-Object System.Drawing.Size(100,20)
$objTextBox1.TabIndex = 0
$objForm.Controls.Add($objTextBox1)
#This creates the RUN button and sets the event
$RUNButton = New-Object System.Windows.Forms.Button
$RUNButton.Location = New-Object System.Drawing.Size(10,190)
$RUNButton.Size = New-Object System.Drawing.Size(75,23)
$RUNButton.Text = "RUN"
$RUNButton.Add_Click({$objForm.Close()})
$RUNButton.TabIndex = 9
$objForm.Controls.Add($RUNButton)
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
I think you have answered all your questions there.
In the other code you are referring to, the button has an event handler. Whatever is inside this code block, gets executed when you press the button.
Inside the code block, the code is looking for which checkbox has been checked using the if loop. All you need to do is call the script you want from the correct if loop. Instead of external script files, you could also call scriptblocks which I think is a cleaner way.
$handler_button1_Click=
{
$listBox1.Items.Clear();
if ($checkBox1.Checked) { "C:\MyScript\Script1.ps1" }
if ($checkBox2.Checked) { "C:\MyScript\Script2.ps1" }
if ($checkBox3.Checked) { "C:\MyScript\Script3.ps1" }
}
If you want to do more with the results of the script after execution, you can put that too inside the if block.
I have written one in GUI that uses checkboxes if you want to refer: Windows Patching Assistant

How to create a popup message in Powershell without buttons

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

Powershell mask password

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.