How to ensure Forms are the top window - forms

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

Related

PowerShell - Using input from TextBox wont work?

I'm trying to make a GUI for an easy tool I need to check if a specific certificate is included in the file and Output the current and next line. This already works.
If I set the variables $INT1 and $Serial1 manually within the code it also works as intended.
e.g. $INT1=31 and $Serial1="5AA61E07726DAC13".
I always get an Error that the variable is empty when inputing the same values into the GUI and clicking OK. What am I doing wrong?
The code:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$objForm = New-Object System.Windows.Forms.Form
$objForm.StartPosition = "CenterScreen"
$objForm.Size = New-Object System.Drawing.Size(400,250)
$objForm.Topmost = $True
#Label+Textbox first question
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,30)
$objLabel.Size = New-Object System.Drawing.Size(800,20)
$objLabel.Text = "INT of certificate"
$objForm.Controls.Add($objLabel)
$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Size(10,50)
$objTextBox.Size = New-Object System.Drawing.Size(200,20)
$objForm.Controls.Add($objTextBox)
#Label+Textbox second question
$objLabel2 = New-Object System.Windows.Forms.Label
$objLabel2.Location = New-Object System.Drawing.Size(10,130)
$objLabel2.Size = New-Object System.Drawing.Size(800,20)
$objLabel2.Text = "Serialnumber of the certificate"
$objForm.Controls.Add($objLabel2)
$objTextBox2 = New-Object System.Windows.Forms.TextBox
$objTextBox2.Location = New-Object System.Drawing.Size(10,150)
$objTextBox2.Size = New-Object System.Drawing.Size(200,20)
$objForm.Controls.Add($objTextBox2)
$INT1 = $objTextBox.Text;
$Serial1 = $objTextBox2.Text;
#OK Button
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(100,175)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Name = "OK"
#$OKButton.DialogResult = "OK"
$OKButton.Add_Click({Invoke-WebRequest -Uri http://CENSORED.crl -OutFile crl.crl;
$test=certutil.exe -dump crl.crl |Out-String -stream | Select-String -pattern "$Serial1" -Context 0,1
[void] [Windows.Forms.MessageBox]::Show($test)})
$objForm.Controls.Add($OKButton)
[void] $objForm.ShowDialog()
Thank you!
Here's a rewrite of your code.
I'm assuming that you want variables $INT1 and $Serial1 to be available after the windows closes.
In that case, fill them inside the Click() scriptblock and refer to them using $script: scoping, otherwise they are just new variables local to the scriptblock.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# these variables need to visible inside the Click scriptblock of the button
# and be also available after the windows closes
$INT1 = $null
$Serial1 = $null
$objForm = New-Object System.Windows.Forms.Form
$objForm.StartPosition = "CenterScreen"
$objForm.Size = New-Object System.Drawing.Size(400,250)
$objForm.Topmost = $True
#Label+Textbox first question
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,30)
$objLabel.Size = New-Object System.Drawing.Size(800,20)
$objLabel.Text = "INT of certificate"
$objForm.Controls.Add($objLabel)
$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Size(10,50)
$objTextBox.Size = New-Object System.Drawing.Size(200,20)
$objForm.Controls.Add($objTextBox)
#Label+Textbox second question
$objLabel2 = New-Object System.Windows.Forms.Label
$objLabel2.Location = New-Object System.Drawing.Size(10,130)
$objLabel2.Size = New-Object System.Drawing.Size(800,20)
$objLabel2.Text = "Serialnumber of the certificate"
$objForm.Controls.Add($objLabel2)
$objTextBox2 = New-Object System.Windows.Forms.TextBox
$objTextBox2.Location = New-Object System.Drawing.Size(10,150)
$objTextBox2.Size = New-Object System.Drawing.Size(200,20)
$objForm.Controls.Add($objTextBox2)
#OK Button
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(100,175)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Name = "OK"
#$OKButton.DialogResult = "OK"
$OKButton.Add_Click({
# you never seem to use variable $INT1, but if you do, refer to it as $script:INT1
# set the variables defined OUTSIDE the scriptblock using script-scoping
$script:INT1 = $objTextBox.Text
$script:Serial1 = $objTextBox2.Text
# create a full path and filename for the output of the Invoke-WebRequest call
$crlFile = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath 'crl.crl'
Invoke-WebRequest -Uri 'http://CENSORED.crl' -OutFile $crlFile
$test = certutil.exe -dump $crlFile | Out-String -Stream | Select-String -Pattern $script:Serial1 -Context 0,1
[void] [Windows.Forms.MessageBox]::Show($test)
# delete the temporary file
$crlFile | Remove-Item -Force
})
$objForm.Controls.Add($OKButton)
[void] $objForm.ShowDialog()
# clean up the form from memory'
$objForm.Dispose()
# here you can again see what is inside variables $INT1 and $Serial1
Write-Host "Last entered values: $INT1 $Serial1"
All user interaction with your form happens in this, blocking call:
[void] $objForm.ShowDialog()
See the ShowDialog() method.
Therefore, if you want to store results from that user interaction in variables, place the assignments after the call, and reference the controls of interest.
# ... form setup
# Show the form modally (as a dialog).
# The call blocks until the form is closed.
# Note:
# You may want to examine the return value in case there
# are multiple ways to close your form, and one of those ways
# signals having *canceled*, for instance.
$null = $objForm.ShowDialog()
# Now you can get values from the controls and assign them to (script) variables.
$INT1 = $objTextBox.Text
$Serial1 = $objTextBox2.Text
Note: If you need to set script-level variables - i.e., variables visible after a .ShowDialog() call - from inside script blocks serving as event delegates (e.g., the script block passed to $OKButton.Add_Click()), you need to use the $script: scope, given that such script blocks run in a child scope too - see this answer.

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

Show a list when a choice is made

I'm trying to work around a script Under Windows.Form and I'm a little bit stuck.
I'd like to be able a specific list appears depending on the choice made from the first list, which means that at the start of the script, only one list has to appears and many other available depending of the choice made.
Here's the full script for reference
#Open a Window.
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form = New-Object Windows.Forms.Form
$form.text = "Contrôles"
$form.Size = New-Object System.Drawing.Size(1000,700)
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(75,150)
$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)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(150,150)
$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)
#Create the Data table (DataTable).
$table1 = New-Object system.Data.DataTable
$table2 = New-Object system.Data.DataTable
#Define the 2 column (Name, Type).
$colonne1 = New-Object system.Data.DataColumn Choice,([string])
$colonne2 = New-Object system.Data.DataColumn Choice,([string])
#Create columns in the data table.
$table1.columns.add($colonne1)
$table2.columns.add($colonne2)
#Add the data line by line in the data table.
$ligne = $table1.NewRow() #Creation of the new row.
$ligne.Choice = "Service" #In the column Choice we put the value we want.
$table1.Rows.Add($ligne) #Add a line in the data table.
$ligne = $table1.NewRow()
$ligne.Choice = "Software"
$table1.Rows.Add($ligne)
$ligne = $table1.NewRow()
$ligne.Choice = "Other"
$table1.Rows.Add($ligne)
#Add the data line by line in the data table.
$ligne = $table2.NewRow() #Creation of the new row.
$ligne.Choice = "Service Enable" #In the column Choice we put the value we want.
$table2.Rows.Add($ligne) #Add a line in the data table.
$ligne = $table2.NewRow()
$ligne.Choice = "Service Disable"
$table2.Rows.Add($ligne)
$ligne = $table2.NewRow()
$ligne.Choice = "Other"
$table2.Rows.Add($ligne)
#Create the View.
$vu1 = New-Object System.Data.DataView($table1)
$vu1.Sort="Choice ASC" #Tri la colonne "Extension" par ordre croissant.
$vu2 = New-Object System.Data.DataView($table2)
$vu2.Sort="Choice ASC"
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(650,50)
$label.Size = New-Object System.Drawing.Size(280,35)
$label.Text = 'Please enter the information in the space below:'
$form.Controls.Add($label)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(650,100)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($textBox)
#Create the Drop-down list (ComboBox).
$liste1 = New-Object System.Windows.Forms.Combobox
$liste1.Location = New-Object Drawing.Point 20,50
$liste1.Size = New-Object System.Drawing.Size(150, 50)
$liste1.DropDownStyle = "DropDownList"
$liste2 = New-Object System.Windows.Forms.Combobox
$liste2.Location = New-Object Drawing.Point 350,50
$liste2.Size = New-Object System.Drawing.Size(150, 50)
$liste2.DropDownStyle = "DropDownList"
#Associate the Data to the Drop-down list
#To do so, we create a "Binding Context".
$liste1.BindingContext = New-Object System.Windows.Forms.BindingContext
$liste1.DataSource = $vu1 #Assigne the view that contains the sorted Data.
$liste1.DisplayMember = "Choice" #Column that will be displayed (Choice).
$liste2.BindingContext = New-Object System.Windows.Forms.BindingContext
$liste2.DataSource = $vu2 #Assigne the view that contains the sorted Data.
$liste2.DisplayMember = "Choice" #Column that will be displayed (Choice).
#Attach the control to the window.
$form.controls.add($liste1)
$form.controls.add($liste2)
#Show everything.
$form.Add_Shown({$textBox.Select()})
$result = $form.ShowDialog()
#Work the code arround.
if ($liste1.DisplayMember= "Service Enable")
{set-service -name RemoteRegistry -ComputerName $textBox.Text -StartupType Automatic}
if ($liste1.DisplayMember = "Service Disable")
{set-service -name RemoteRegistry -ComputerName $textBox.Text -StartupType Automatic}
Write-Host "ComboBox = " $liste1.DisplayMember
Write-Host "ComboBox = " $liste2.selectedvalue
#Fin.
If anybody have an idea where I could look, it would be great.
Thanks you
Nad
1. You have no form / trigger events in your code.
2. You don't have the correct GUI objects in your code to hold a list /
record result.
A form is just a container to hold elements until you add the code behind to make it do something. You have to have a proper GUI object to send that result to.
I am not sure if you are doing this all by hand in the ISE or VSCode or Notepad or whatever, but this is a good first effort. However, what you show, seems to indicate you are not really up to speed on GUI development / general app dev work, as what you are doing is not really unique to PowerShell, but something required for any app development client or web.
So, really, spend some time studying / reviewing general WPF/Winforms development and that form event stuff will be covered.
As for your use case, you need:
Define the list GUI object (multiline, ListBox, ListView, datagrid) to hold the results (synch'ing combox boxes mean adding and removing elements on event actions)
Define what that list is (text files, db read etc)
On the click, change or other form event, read from that list and populate
the GUI list object
There are many examples of this on this site and all over the web.
Here a good video on GUI development with PowerShell:
powershell populate combobox basing on the selected item on another combobox
From the above discussion (not something to just add to your code without understanding the what's and the why's):
Use a ComboBox.SelectionChangeCommitted Event:
"Occurs when the user changes the selected item and that change is displayed in the ComboBox"
$combobox2_SelectionChangeCommitted={
$Mailboxes = Get-Mailbox -OrganizationalUnit $ClientSelected
foreach ($mailbox in $Mailboxes)
{
$CurrentMailbox = "{0} ({1})" -f $mailbox.Name, $mailbox.Alias
Load-ComboBox $combobox2 $CurrentMailbox -Append
}
}
Use a button:
$button1_Click={
$Mailboxes = Get-Mailbox -OrganizationalUnit $ClientSelected
foreach ($mailbox in $Mailboxes)
{
$CurrentMailbox = "{0} ({1})" -f $mailbox.Name, $mailbox.Alias
Load-ComboBox $combobox2 $CurrentMailbox -Append
}
}
Lastly, using this …
Write-Host "ComboBox = " $liste1.DisplayMember
Write-Host "ComboBox = " $liste2.selectedvalue
… is not something one would do, because the console is not opened to see these results and Write-Host should be avoided except for when using console only text colorizations of other console only formatting scenarios, it also empties the display buffer, so it cannot be sent to anything else. Also, you don't have a GUI object called 'ComboBox' anywhere on the form, so it's not serving any purpose for your use case.
After some times and research, I managed to find what I needed exactly.
This might help people who stumble upon the post so here a small part of what I found
function Service()
{if ($ListBox1.SelectedItem -eq 'Enable Services')
{
$form.Controls.Add($Label3)
$form.Controls.add($ListBox2)
$form.Controls.Add($Label4)
$form.Controls.Add($textBox)
$form.Controls.Add($Button2)
$form.Controls.Add($Button3)
}
I create firstly a Function with a name, in which will countain the condition of what I'd like to happen when a choice is made in my listbox "ComboBox"
$button1.add_Click({ Service })
Then I call that function from a button I Added, in which other Boxes will appear upon click on that button.
It is not very different from #Postanote's answer but that was the solution I'm more at ease with.

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.