4 minute read

I love PowerShell, and when prompting users for input I often prefer to use GUI controls rather than have them enter everything into the console, as some things like browsing for files or folders or entering multi-line text aren’t very pleasing to do directly in the PowerShell prompt window. So I thought I’d share some PowerShell code that I often use for these purposes. Below I give the code for creating each type of GUI control from a function, an example of calling the function, and a screen shot of what the resulting GUI control looks like.

Download a file containing the code for the functions and examples shown here

Show a message box

Function:

# Show message box popup and return the button clicked by the user.
function Read-MessageBoxDialog([string]$Message, [string]$WindowTitle, [System.Windows.Forms.MessageBoxButtons]$Buttons = [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]$Icon = [System.Windows.Forms.MessageBoxIcon]::None)
{
    Add-Type -AssemblyName System.Windows.Forms
    return [System.Windows.Forms.MessageBox]::Show($Message, $WindowTitle, $Buttons, $Icon)
}

Example:

$buttonClicked = Read-MessageBoxDialog -Message "Please press the OK button." -WindowTitle "Message Box Example" -Buttons OKCancel -Icon Exclamation
if ($buttonClicked -eq "OK") { Write-Host "Thanks for pressing OK" }
else { Write-Host "You clicked $buttonClicked" }

Message Box Example

Prompt for single-line user input

Function:

# Show input box popup and return the value entered by the user.
function Read-InputBoxDialog([string]$Message, [string]$WindowTitle, [string]$DefaultText)
{
    Add-Type -AssemblyName Microsoft.VisualBasic
    return [Microsoft.VisualBasic.Interaction]::InputBox($Message, $WindowTitle, $DefaultText)
}

Example:

$textEntered = Read-InputBoxDialog -Message "Please enter the word 'Banana'" -WindowTitle "Input Box Example" -DefaultText "Apple"
if ($textEntered -eq $null) { Write-Host "You clicked Cancel" }
elseif ($textEntered -eq "Banana") { Write-Host "Thanks for typing Banana" }
else { Write-Host "You entered $textEntered" }

Input Box Example

Prompt for a file

This is based on a post the Scripting Guy made.

Function:

# Show an Open File Dialog and return the file selected by the user.
function Read-OpenFileDialog([string]$WindowTitle, [string]$InitialDirectory, [string]$Filter = "All files (*.*)|*.*", [switch]$AllowMultiSelect)
{
    Add-Type -AssemblyName System.Windows.Forms
    $openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $openFileDialog.Title = $WindowTitle
    if (![string]::IsNullOrWhiteSpace($InitialDirectory)) { $openFileDialog.InitialDirectory = $InitialDirectory }
    $openFileDialog.Filter = $Filter
    if ($AllowMultiSelect) { $openFileDialog.MultiSelect = $true }
    $openFileDialog.ShowHelp = $true    # Without this line the ShowDialog() function may hang depending on system configuration and running from console vs. ISE.
    $openFileDialog.ShowDialog() > $null
    if ($AllowMultiSelect) { return $openFileDialog.Filenames } else { return $openFileDialog.Filename }
}

Example:

$filePath = Read-OpenFileDialog -WindowTitle "Select Text File Example" -InitialDirectory 'C:\' -Filter "Text files (*.txt)|*.txt"
if (![string]::IsNullOrEmpty($filePath)) { Write-Host "You selected the file: $filePath" }
else { "You did not select a file." }

Select Text File Example

Prompt for a directory

This is based on this post, as using System.Windows.Forms.FolderBrowserDialog may hang depending on system configuration and running from the console vs. PS ISE.

Function:

# Show an Open Folder Dialog and return the directory selected by the user.
function Read-FolderBrowserDialog([string]$Message, [string]$InitialDirectory, [switch]$NoNewFolderButton)
{
    $browseForFolderOptions = 0
    if ($NoNewFolderButton) { $browseForFolderOptions += 512 }

    $app = New-Object -ComObject Shell.Application
    $folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
    if ($folder) { $selectedDirectory = $folder.Self.Path } else { $selectedDirectory = '' }
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($app) > $null
    return $selectedDirectory
}

Example:

$directoryPath = Read-FolderBrowserDialog -Message "Please select a directory" -InitialDirectory 'C:\' -NoNewFolderButton
if (![string]::IsNullOrEmpty($directoryPath)) { Write-Host "You selected the directory: $directoryPath" }
else { "You did not select a directory." }

Browse For Folder

Prompt for multi-line user input

This is based on code shown in this TechNet article.

Function:

function Read-MultiLineInputBoxDialog([string]$Message, [string]$WindowTitle, [string]$DefaultText)
{
<#
    .SYNOPSIS
    Prompts the user with a multi-line input box and returns the text they enter, or null if they cancelled the prompt.

    .DESCRIPTION
    Prompts the user with a multi-line input box and returns the text they enter, or null if they cancelled the prompt.

    .PARAMETER Message
    The message to display to the user explaining what text we are asking them to enter.

    .PARAMETER WindowTitle
    The text to display on the prompt window's title.

    .PARAMETER DefaultText
    The default text to show in the input box.

    .EXAMPLE
    $userText = Read-MultiLineInputDialog "Input some text please:" "Get User's Input"

    Shows how to create a simple prompt to get mutli-line input from a user.

    .EXAMPLE
    # Setup the default multi-line address to fill the input box with.
    $defaultAddress = @'
    John Doe
    123 St.
    Some Town, SK, Canada
    A1B 2C3
    '@

    $address = Read-MultiLineInputDialog "Please enter your full address, including name, street, city, and postal code:" "Get User's Address" $defaultAddress
    if ($address -eq $null)
    {
        Write-Error "You pressed the Cancel button on the multi-line input box."
    }

    Prompts the user for their address and stores it in a variable, pre-filling the input box with a default multi-line address.
    If the user pressed the Cancel button an error is written to the console.

    .EXAMPLE
    $inputText = Read-MultiLineInputDialog -Message "If you have a really long message you can break it apart`nover two lines with the powershell newline character:" -WindowTitle "Window Title" -DefaultText "Default text for the input box."

    Shows how to break the second parameter (Message) up onto two lines using the powershell newline character (`n).
    If you break the message up into more than two lines the extra lines will be hidden behind or show ontop of the TextBox.

    .NOTES
    Name: Show-MultiLineInputDialog
    Author: Daniel Schroeder (originally based on the code shown at http://technet.microsoft.com/en-us/library/ff730941.aspx)
    Version: 1.0
#>
    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

    # 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
}

Example:

$multiLineText = Read-MultiLineInputBoxDialog -Message "Please enter some text. It can be multiple lines" -WindowTitle "Multi Line Example" -DefaultText "Enter some text here..."
if ($multiLineText -eq $null) { Write-Host "You clicked Cancel" }
else { Write-Host "You entered the following text: $multiLineText" }

Multi Line Example

All of these but the multi-line input box just use existing Windows Forms / Visual Basic controls.

I originally was using the Get verb to prefix the functions, then switched to the Show verb, but after reading through this page, I decided that the Read verb is probably the most appropriate (and it lines up with the Read-Host cmdlet).

Hopefully you find this useful.

Happy coding!

Comments

Richard

Hi,

Great script. One remark though. I am using this in a script of my own (or the intention is there). I get the input into a variable which I am going to use as the Body for an email i’m sending. However no carriage return are carried over so the message in the email is a single line. Any idea.

RvN

Jeremy

Thanks for an excellent file select function as well as the link back to the scripting guys post. I read their page, but your ready-to-go example worked better for me than theirs. Cheers

Derrick B.

@deadlydog

Sorry about that. I’m a newbbie by far. I’ve created a script to add and setup apps within Citrix, but I was looking for something like a popup box to add to the code to answer the questions in the script. Looking at “Prompt for simple user input” looks like it will do the trick. What don’t understand is how to add that to what I have to input the text in the right field. Can you show an example?

SeanD

Not sure how to insert this into my script. I have a script that looks at a text file (pc names), ping them and exports to a csv with the responses. Script work fine. I would like to prompt the user fot the text file instead of having the path hard coded. I tried pasting your sample variable $filepath in front of my code but it says i did not pick a file.

$filePath = Read-OpenFileDialog -WindowTitle “Select Text File Example” -InitialDirectory ‘C:' -Filter “Text files (.txt)|.txt” if (![string]::IsNullOrEmpty($filePath)) { Write-Host “You selected the file: $filePath” } else { “You did not select a file.” } get-content $filePath

$Output2 = “c:\PSexport\PingOldPCs5.csv”

Write-host ` “Pinging List of Dead Computers and Writing to c:\Psexport\PingPC.csv `n WAIT” -foregroundcolor “green” $filePath | ForEach-Object {GWMI win32pingstatus -filter (“address=’”+ $ +”’”) -computername .} | Select-Object ProtocolAddress,address,responsetime,statuscode | export-csv $Output2 -NoTypeInformation c:\PSexport\PingOldPCs5.csv

SeanD

OK. I think i am getting further. FYI - I am not a programmer but an admin learning PS.

My script pings the list of PCs that are in the text file. I am getting a blank excel sheet where normally if i hard code the text file location For-eachobject does its job and pings.

It seems like the function is just passing the name of the file

SeanD

Get-content was the trick. In case anyone wants to know - Here is the complete code that will allow you to pick a file and then read whats in it. Mine here reads it then pings the list

$filePath = Read-OpenFileDialog -WindowTitle “Select Text File Example” -InitialDirectory ‘C:' -Filter “Text files (.txt)|.txt” if (![string]::IsNullOrEmpty($filePath)) { Write-Host “You selected the file: $filePath” } else { “You did not select a file.” }

$Output2 = “c:\PSexport\PingOldPCs5.csv”

Show an Open File Dialog and return the file selected by the user.

function Read-OpenFileDialog([string]$WindowTitle, [string]$InitialDirectory, [string]$Filter = “All files (.)|.”, [switch]$AllowMultiSelect) { Add-Type -AssemblyName System.Windows.Forms $openFileDialog = New-Object System.Windows.Forms.OpenFileDialog $openFileDialog.Title = $WindowTitle if (![string]::IsNullOrWhiteSpace($InitialDirectory)) { $openFileDialog.InitialDirectory = $InitialDirectory } $openFileDialog.Filter = $Filter if ($AllowMultiSelect) { $openFileDialog.MultiSelect = $true } # openFileDialog.ShowHelp = $true # Without this line the ShowDialog() function may hang depending on system configuration and running from console vs. ISE. $openFileDialog.ShowDialog() > $null if ($AllowMultiSelect) { return $openFileDialog.Filenames } else { return $openFileDialog.Filename } } # End Function

$responses = @()

ForEach ($path in $filePath) {

ForEach ($computer in (Get-Content $path)) {

    $responses += GWMI win32_pingstatus -filter ("address='"+ $computer +"'") -computername .
}

}

$responses  
Select-Object ProtocolAddress,address,responsetime,statuscode export-csv $Output2 -NoTypeInformation
Harry

Nice work on the scripts bro! they fit nicely into the one I’m developing at work to automate admin tasks over lots of pc’s

DemoD

When I run this from Powershell ISE the windows that pop up always end up behind the ISE. Is there any way to code and always have them show up in the foreground?

deadlydog

@DemoD The only one that does not pop in the foreground for me is the Read-FolderBrowserDialog, and unfortunately I haven’t been able to find a way to force it. Luckily though it does create a new window icon in the start menu bar, so it’s at least a little obvious that a new popup was created and requires attention. If I do find a method that does it, I’ll update the post.

Aymen

When running the script I get an error message

Read-MessageBoxDialog: Unable to find type [System.Windows.Forms.MessageBoxButtons]. Make sure that the assembly that contains this type is loaded.

Any idea how to fix it?

deadlydog

@Aymen Move the line:

Add-Type -AssemblyName System.Windows.Forms

from within the Read-MessageBoxDialog function to just before it, so it looks like this:

Add-Type -AssemblyName System.Windows.Forms function Read-MessageBoxDialog([string]$Message, [string]$WindowTitle, [System.Windows.Forms.MessageBoxButtons]$Buttons = [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]$Icon = [System.Windows.Forms.MessageBoxIcon]::None) { return [System.Windows.Forms.MessageBox]::Show($Message, $WindowTitle, $Buttons, $Icon) }

I don’t have that problem on my local PC, so I’m guessing it’s a PowerShell 2.0 problem maybe?

Aymen

With this change it worked.

I have PowerShell 4.0 installed.

By the way: did you mean in the comments that the help button in the file selection dialog box is obligatory although it does nothing here?

Eric

I am trying to use the Prompt for a directory script. It worked a few days then now I get the following error when I run the script: “The term ‘Read-FolderBrowserDialog’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.”

I did a copy and paste directory from the code listed on this site so not sure what else is needed.

deadlydog

@Eric You will need to make sure the Read-FolderBrowserDialog function is defined in your script before you try and call it; so the function should be at the top of your script, and the line that calls is should be below that.

Eric

This is what I have :

#Opens file directory search $directoryPath = Read-FolderBrowserDialog -Message “Please select a directory” -InitialDirectory ‘S:\Projects\UCG\Production' -NoNewFolderButton if (![string]::IsNullOrEmpty($directoryPath)) { Write-Host “You selected the directory: $directoryPath” } else { “You did not select a directory.” }

#Search files within directory REST OF SCRIPT……..

Somehow this worked for a few days and when I closed saved the script and closed powershell the next time I tried to run it I got the error

deadlydog

@Eric Yeah, you had probably already ran the code that defined the Read-FolderBrowserDialog function, so the function was defined in your powershell session, even if you removed the actual code from your script. Then once you closed your powershell window then your session ended. All you need to do is put the function code at the top of your script.

e.g.

Show an Open Folder Dialog and return the directory selected by the user.

function Read-FolderBrowserDialog([string]$Message, [string]$InitialDirectory, [switch]$NoNewFolderButton) { $browseForFolderOptions = 0 if ($NoNewFolderButton) { $browseForFolderOptions += 512 }

$app = New-Object -ComObject Shell.Application
$folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
if ($folder) { $selectedDirectory = $folder.Self.Path } else { $selectedDirectory = '' }
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($app) > $null
return $selectedDirectory }

#Opens file directory search $directoryPath = Read-FolderBrowserDialog -Message “Please select a directory” -InitialDirectory ‘S:\Projects\UCG\Production\’ -NoNewFolderButton if (![string]::IsNullOrEmpty($directoryPath)) { Write-Host “You selected the directory: $directoryPath” } else { “You did not select a directory.” }

#Search files within directory REST OF SCRIPT……..

blaughw

I’ve used your single-line and file dialog snippets to make a “consumable” script for setting Exchange OOF messages for my org’s service desk. Very helpful, thank you!

Daniel

The multi-line user prompt is fantastic, but how do you run a ‘for each’ loop against each row entered into that prompt? It seems to take the input as one long string.

deadlydog

@Daniel Simply split the string on the newline character (rn) and then foreach over it. This stack overflow post shows how to do split a string on spaces; just swap the space out for the newline character, and the hard-coded string value for the string variable containing the user’s input. http://stackoverflow.com/a/11348779/602585

Graham J

First off thanks for this, been using it for loads of Service Desk scripts.

Mind if I pick your brains on something?

Is there anyway of getting -DEFAULTTEXT to spread onto more than one line when reading from a variable or text file?

My variable is $OOO

“I am out of the office from x to y.

In my absence please contact z.

Thanks”

As a work around I’ve exported $OOO to a text file with the correct formatting. When I set the -DEFAULTTEXT to : -DEFAULTTEXT ( Get-Content C:\OOO.txt ) It displays as with double spacing as shown below.

“I am out of the office from x to y. In my absence please contact z. Thanks”

Any help would be appreciated. Thanks

deadlydog

@Graham J I’m not certain about when reading from a file, but for a variable you can just use the regular rn to denote a new line (the string must be in double quotes though, not single quotes) or you can use the [Environment]::NewLine character.

For example, the following will have the default message show up over 3 lines:

$text = “Some text on line 1rnthis is on line 2 (multiple lines)” + [Environment]::NewLine + “and some more text on line 3” Read-MultiLineInputBoxDialog -Message ‘The message’ -WindowTitle ‘Window title’ -DefaultText $text

When reading the text in from a file, I suspect you may need to do a string replacement on the text that it read in, and replace whatever characters it sees for the new line character with rn.

devenir riche

An outstanding share! I’ve just forwarded this onto a co-worker who had been conducting a little research on this. And he actually ordered mme lunch simply because I discovered it for him…

lol. So let me reword this…. Thank YOU for the meal!! But yeah, thanx for spending the time to discuss this isue here on your web site.

Andres

I’m passing through a multiline $DefaultText that’s basically notes coming from VM’s. I’m seeing multilines if I Write-Host $DefaultText inside the function. But When the text box is opened the lines are all one single line. If I press ok and output the $multiLineText it looks correct. Is there something else I need to do in order to make the multilines show up in $DefaultText inside $texBox.Text ?

deadlydog

@Andres Hmmm, I’m not certain. I guess one question I would have is are the VMs Windows or Linux? As I know Linux just uses \n for a new line, but Windows uses \r\n. I’m not certain if this is the problem or not, as I haven’t done any testing; this is just off the top of my head.

farah magbutay

Hi Daniel, great article. Ive got some questions regarding this. I have got quite similar code on opening a dialogbox. Ive got some dilemna though which I cant find an answer on. I am using powershell to test a form (Application form) via a webpage. However I have one mandatory field that needs a file to be uploaded hence a pop up box to choose a file to upload comes up. I get to a point where in it opens the dialog box and the cursor is on the filename field but how can I get the filename into the text box. HELP =) farah

Enzo Lopez

How can I create and array from the word in the $textBox.Text If I create an input box and the user enters “warning”, I then want an array that equals $warning for me to use later in the script. I’m able to get it type out, but how do I reference an array where the name of the array is unknown

$test = “`$” + $textBox.Text $magic = (,$test)

PS C:\Users> $magic $warning

my goal is the have something like this in the script $magic.ToString() = Get-Service and $warning should return whatever is in get-service

Enzo Lopez

@Enzo Lopez I figured it out

New-Variable -Name $textBox.Text Set-Variable -Name $textBox.Text -Value (get-service) -Scope global $ArrayName = “`$” + $textBox2.Text Write-Host “The Array name is $ArrayName”

Hoang

Hi Daniel, Great and very helpful article. I need your help with Prompt for single-line user input. Me scenario is prompting user to enter computername then it will check if that computername exist in the path, if not it’ll prompt again asking user to reenter the correct computername. However, after user entered correct computername, inputbox still comes up. Here is what I have:

Show input box popup and return the value entered by the user.

function Get-InputBoxDialog([string]$Message, [string]$WindowTitle) { Add-Type -AssemblyName Microsoft.VisualBasic return [Microsoft.VisualBasic.Interaction]::InputBox($Message, $WindowTitle) }

$OSDComputerName = Get-InputBoxDialog -Message “Enter OLD ComputerName” -WindowTitle “Source PC” $SrcPath = “\Server\Migfolder$OSDComputerName” do { $OSDComputerName = Get-InputBoxDialog -Message “OLD ComputerName not found. Re-Enter OLD ComputerName” -WindowTitle “Source PC” } until (Test-Path $SrcPath)

deadlydog

@Hoang Changing your code to the following should fix up your issue. In the future, please consider asking these types of questions in StackOverflow.com or a similar site rather than here. Thanks.

Show input box popup and return the value entered by the user.

function Get-InputBoxDialog([string]$Message, [string]$WindowTitle) { Add-Type -AssemblyName Microsoft.VisualBasic return [Microsoft.VisualBasic.Interaction]::InputBox($Message, $WindowTitle) }

do { $OSDComputerName = Get-InputBoxDialog -Message “Enter OLD ComputerName” -WindowTitle “Source PC” $SrcPath = “\Server\Migfolder$OSDComputerName” } until (Test-Path $SrcPath)

The problem was you were not rebuilding $SrcPath after getting the new $OSDComputerName value.

Leave a Comment

Your email address will not be published. Required fields are marked *

Loading...