After many years of writing scripts for the Windows PowerShell console, I’m now learning the next level of automation — writing GUI applications that run Windows PowerShell commands. PowerShell Studio forms provide a gentle and well-guided introduction to GUI applications, but it’s still taken me some time to break old habits.
I constantly need to remind myself that things that “just work” in the console don’t make any sense in a GUI. Writing output is one of them.
Writing Output to the Console
When you generate output in the Windows PowerShell console, the output appears as text strings in the console window.
PS C:> Get-Date Wednesday, December 3, 2014 7:16:38 AM
Behind the scenes, Windows PowerShell silently appends an Out-String command to the pipeline and sends the results to “standard output” (“stdout”). The process that hosts Windows PowerShell handles the standard output, almost always by writing the output to the console (host behavior might vary).
So, if you run a cmdlet that returns objects, such as the Get-Process cmdlet, (string representations of) the objects appear in the console.
If you format objects, such as by using Format-List or Format-Table, (string representations of) the formatted objects appear in the console.
If you run Write-Host or Write-Output, Out-Host or Out-String, the output strings appears in the console.
And even if you send the output to an alternate output stream by using Write-Warning, Write-Verbose, or Write-Error, by default, the output appears in the console.
But GUI application don’t have a console. What happens to the output that would otherwise go to the console when there is no console? And, more importantly, how do I manage output without a console?
Where is my output?
GUI applications, like the cool ones that you write in PowerShell Studio, don’t have a console. There is no standard output. There is no auto-appended Out-String.
So, when you generate output and don’t save it to a file or a variable, the output is just lost.
Get-Process | ... <Nowhere> ...
It’s like a pitcher throwing a baseball when there’s no catcher on the field. The ball just falls to the ground and rolls to a stop .. ignored.
So, in a GUI application, you can’t just write output and hope for the best. If you want to display cmdlet output or save it, you need to do it explicitly.
Display output in a text box
A GUI application is a collection of controls. To display output, you need to assign the output to a control, such as text box, rich text box, or a data grid.
For example, to display the name of the local computer in the console, you run an $env:ComputerName command.
PS C:> $env:ComputerName JUNEB-TESTBOX
When I try the same thing in a GUI application where there is no console, I don’t see any result at all. In this example, I created a form with a button that, when clicked, runs the $env:ComputerName expression.
$buttonComputerName_Click = { <b>$env:ComputerName</b> } |
But, when I click the button, nothing happens, even if I have a console window open.
To display the computer name in a GUI application, I add a display control to my form and assign the output of the command to a method of the display control.
For example, I can add a Textbox control to the form and use the assignment operator (=) to assign the result of the expression to the Text property of the TextBox. Now, when you click the button, you see the output in the TextBox.
$buttonComputerName_Click = { $textboxResults.Text = $env:ComputerName } |
Here’s the result:
Display Output in a Different Program
You can also display the output of commands in objects that are not part of your program. GUI professionals think that this approach is sloppy and not a best practice, but you might find it useful for testing or debugging.
For example, you can pipe the output to the Out-GridView cmdlet.
$buttonComputerName_Click = { $env:ComputerName | Out-GridView } |
Here’s the result:
You can also write to a file and open the file. These sample commands create a temporary file, write to the file, and then use Notepad to display the file. Again, this makes real programmers cringe, but it works, at least for testing.
$buttonComputerName_Click = { $TmpFile = [System.IO.Path]::GetTempFileName $env:COMPUTERNAME | Set-Content -Path $TmpFile Notepad $TmpFile } |
Here’s the result:
Convert to Strings
Another task you need to manage in a GUI application is converting objects to text strings. Usually, this just means piping your output to an Out cmdlet, such as Out-String. Out cmdlets, including Out-GridView, convert objects to string data.
When you write output to the console, Windows PowerShell automatically appends an Out-String command to the pipeline. But, there is no console and no automatic Out-String in a GUI application.
When you assign non-string objects to a string object or to a control that takes string objects, such as a TextBox, Windows PowerShell calls the toString() method of the object. (In other languages, the assignment would just fail, but it’s trying to help.) With some objects, such as DateTime and ErrorRecord objects, toString() works just fine, but, in many cases, it produces odd-looking results.
For example, this command assigns the results of a Get-Process command to a text box. Windows PowerShell recognizes that you need text and calls the toString() method of Process objects, but the result is not useful.
$buttonGetProcess_Click = { $textboxResults.Text = Get-Process } |
To get a string representation of the default process table, pipe the output to the Out-String cmdlet and then assign it to the text box.
$buttonGetProcess_Click = { $textboxResults.Text = Get-Process | Out-String } |
TIP: To make the output easy to read, set the text box (or rich text box) to use a monospace font. In PowerShell Studio, right click the text box, click Apply Property Set, and then click Apply Console Font.
Don’t avoid toString() completely. There are a few objects where the toString() method produces a useful result. DateTime objects are the classic example. The toString() method of a DateTime object returns a “short-date/long-time” format string. Out-String returns a “long-date/long-time” format.
PS C:> (Get-Date).ToString() 12/6/2014 10:42:24 AM PS C:> Get-Date | Out-String Saturday, December 6, 2014 10:42:37 AM
#Uses toString() $buttonGetDate_Click = { $textboxDate.Text = Get-Date } |
#Uses toString() $buttonGetDate_Click = { $textboxDate.Text = Get-Date | Out-String } |
You can also use the Format and UFormat parameters of the Get-Date cmdlet to get a formatted date string in almost any configuration. For example, to get a date in RFC1123 format, use -Format R (or “r”). No need to pipe to Out-String when you use Format or UFormat, because these parameters force the cmdlet to return a string.
PS C:> Get-Date -Format R Tue, 09 Dec 2014 10:46:41 GMT
Converting Formatted Objects
In the console, Format cmdlets, such as Format-Table, are usually the last element in a pipeline. The output looks great in the console, because Windows PowerShell appends an Out-String command to the pipeline before sending it to the console.
PS C:> Get-HotFix | Format-Table -Property HotFixID, InstalledOn -AutoSize HotFixID InstalledOn -------- ----------- KB2693643 10/7/2014 12:00:00 AM KB2769299 9/5/2014 12:00:00 AM KB2883200 10/7/2013 12:00:00 AM KB2883684 9/5/2014 12:00:00 AM ...
But, in a GUI application, by default, you get the result of calling the toString() method of the format objects that the Format cmdlets return.
$buttonGetHotfix_Click = { $textboxHotFix.Text = Get-HotFix | Format-Table -Property HotFixID, InstalledOn -AutoSize } |
To get a more usable display in your GUI application, pipe the format objects to the Out-String cmdlet before assigning them to the Text property of the TextBox.
$buttonGetHotfix_Click = { $textboxHotFix.Text = Get-HotFix | Format-Table -Property HotFixID, InstalledOn -AutoSize | Out-String } |
That’s better!
After a while, the task of managing the output display in your GUI applications become as natural as typing a cmdlet name, but while you’re learning, be sure to test and make sure that all of your output is captured and displayed in a usable format.
New stuff from the SAPIEN blog: Displaying Output in a GUI Application http://t.co/pi8Rcw9n0x
Displaying Output in a GUI Application http://t.co/aIneo6RKxu #PowerShell
RT @JeffHicks: New stuff from the SAPIEN blog: Displaying Output in a GUI Application http://t.co/pi8Rcw9n0x
#PowerShell output is displayed automatically in the console. Not so in a GUI app! How to handle output in GUI app:
http://t.co/wTAoA3Jle6
RT @juneb_get_help: #PowerShell output is displayed automatically in the console. Not so in a GUI app! How to handle output in GUI app:
htt…
RT @juneb_get_help: #PowerShell output is displayed automatically in the console. Not so in a GUI app! How to handle output in GUI app:
htt…
RT @juneb_get_help: #PowerShell output is displayed automatically in the console. Not so in a GUI app! How to handle output in GUI app:
htt…
RT @juneb_get_help: #PowerShell output is displayed automatically in the console. Not so in a GUI app! How to handle output in GUI app:
htt…
Hey June I also want to share something on this, by using the appendtext property.
$txtbox.AppendText “June is Great!`r`n”
This lets me manual add text to a textbox, with an carriage return at the end. So each time I write to it, it goes to the next line. 🙂
Displaying Output in a GUI Application http://t.co/L4AXQntz6J via @SAPIENTech
. @AWSOMEDEVSIGNER @SAPIENTech I think the utility of datatemplates would be interesting to consider in these cases. (Xaml data binding)
@rrelyea @SAPIENTech Yepp 🙂 I think it could have been done from the GUI-First standpoint as well. Combing with this and you are good to go
It’s nice to see some of this stuff being talked about. I’ve been developing GUI based PowerShell scripts for a little while now and writing output to text or richtext boxes. I do have an issue that you might be able to help with though. If I’m iterating through an array and trying to append the textbox after each iteration, it doesn’t seem to work. What seems to happen is that the textbox doesn’t update until after the whole foreach block has completed. Only then is all of the output dumped into the textbox. Any ideas?
Thanks, Bill! Great tip!
Use the control’s Update method to force a redraw. I also recommend looking at our Creating Responsive Loop article: http://www.sapien.com/blog/2011/07/15/primalforms-2011-creating-responsive-loops/
We also have a ScriptingAnswers forum dedicated to GUI questions.
Thanks so much, David. Jon, just wanted to mention that assignments to a rich/textbox in a ForEach loop are processed one at a time; not cached until the loop completes. For help, post on the PowerShell GUI forum in Scripting Answers: http://sapien.com/forums/viewforum.php?f=21
Thanks David and June. I’ll take a look at both your suggestions.
Hello, I am fairly new to scripting as a whole and my small employer has tasked me with PowerShell. I found PowerShell Studio 2015, and if I am able to implement it well enough, my employer will be purchasing. My question is a very basic request (again, I am not a programmer, but dabbled in VBS and HTML): how do I call the information from a user input box as a variable and “use” it in a clickevent? Basically, a small GUI for delegating access to a users mailbox. Here is what I have:
$textbox1_TextChanged = {
$mailboxAccess
}
$textbox2_TextChanged = {
$mailboxUser
}
$buttonExecute_Click = {
#TODO: Add-MailboxPermission $mailboxAccess -User $mailboxUser -AccessRights FullAccess -InheritanceType All -AutoMapping $false
}
How do I make $mailboxAccess and $mailboxUser input be recognized?
Thanks, WJS
Hi WJS,
You will actually already have the text that you have typed into your textboxes. Once you populate a textbox, the text within it becomes a gettable and settable property.
They will be accessible to you as $textbox1.Text and $Textbox2.Text
You don’t REALLY need the “$textbox1_TextChanged” and “$textbox2_TextChanged” functions.
So in your “$buttonExecute_Click” function, you can either directly access them with something like…
Add-MailboxPermission $Textbox1.Text -User $Textbox2.Text -AccessRights FullAccess -InheritanceType All -AutoMapping $false
OR you could first define them as variables and set the values before using them in the Add-MailboxPermission cmdlet
So something like …
#create the variables
$mailboxAccess = $Textbox1.Text
$mailboxUser = $Textbox2.Text
#run the cmdlet
Add-MailboxPermission $mailboxAccess -User $mailboxUser -AccessRights FullAccess -InheritanceType All -AutoMapping $false
Just a tip, for when you get a bit more confident with your scripting, rather than having textboxes (which give the opportunity for human error in typing in the values that you want), you could\should use a get-mailbox command to read all (or a filtered subset) of your mailboxes into an Array – as you load your PowerShell application. Be careful with this – it could get slow if you have a huge Exchange infrastructure, thus appropriate filtering).
A basic example would be
$MailboxArray = @(Get-Mailbox -Database LondonMDB09)
Each object in your array will have properties associated with it. You will want to make sure that you use the “Identity” property for your subsequent scripting.
You could then use the entries in this array to populate a combobox. There are helper functions in PowerShell Studio that will assist you with this.
But you could simple do something like…
foreach($Mailbox in $MailboxArray)
{
$combobox1.Items.Add($MAilbox.Identity)
}
This actually wouldn’t look too elegant, as the Identity of a mailbox doesn’t look too pretty but there are ways of getting around this – probably a bit beyond the scope of this comment.
Then when you access the comboboxes (rather than the textboxes), the selected entry will be the value that you use for your variables. For example..
$mailboxAccess = $combobox1.SelectedItem
So this would ensure that you never have your script because of typing errors.
Hope this makes sense and good luck with your scripting.
Jon,
Exactly what I was needing! Thank you so much for you suggestion, you made my Friday! Just some different tools/options to use…and I figured it out with logic.
WJS
Displaying output in a #PowerShell GUI app. Don’t forget Out-String. https://t.co/f51JRq1pXj
RT @juneb_get_help: Displaying output in a #PowerShell GUI app. Don’t forget Out-String. https://t.co/f51JRq1pXj
@juneb_get_help @SAPIENTech great tips!
RT @juneb_get_help: Displaying output in a #PowerShell GUI app. Don’t forget Out-String. https://t.co/f51JRq1pXj