PowerShell Genie
Learn GUI and Forms

Building GUI Forms with PowerShell

Not every tool is a console command. System.Windows.Forms turns a script into a window with labels, text boxes and buttons — a small app your team can click instead of learning your parameters.

Advanced 7 min read Windows only · Windows PowerShell 5.1, or PowerShell 7 with -STA

Documentation: Windows Forms documentation

What Windows Forms does

Windows Forms is the .NET way to build desktop windows, and PowerShell can drive it directly because PowerShell is .NET. Load the assembly, create a Form object, add controls to it, and display it. The script pauses at ShowDialog until the user closes the window, then continues with whatever the user entered.

A form is just objects: a Form with a Text title and a Size, Labels that display text, TextBoxes that collect input, Buttons that run code when clicked. Each control has a Location and a Size, measured in pixels from the form's top-left corner.

Two limits matter. Forms are Windows-only — the same script cannot show a window on macOS or Linux. And PowerShell 7 runs in multi-threaded apartment mode by default while Forms requires single-threaded apartment mode, so a Forms script in PowerShell 7 must be launched with pwsh -STA. Windows PowerShell 5.1 is STA by default and needs no flag.

See the objects move

Inspect this authored example with fictional data in the bounded browser model. The numbered stages show how its objects change.

Add-Type -AssemblyName System.Windows.Forms
  1. Stage 1Add-Type -AssemblyName System.Windows.FormsTry it: a two-control form

    Build a form

    Load the assembly, create the form, add a label, a text box and a button, then wire the button's click event:

    1PS> Add-Type -AssemblyName System.Windows.Forms
    2PS> $form = New-Object System.Windows.Forms.Form
    3PS> $form.Text = 'Greeting'; $form.Size = New-Object System.Drawing.Size(300, 200)
    4PS> $label = New-Object System.Windows.Forms.Label
    5PS> $label.Text = 'Name:'; $label.Location = New-Object System.Drawing.Point(10, 20)
    6PS> $box = New-Object System.Windows.Forms.TextBox
    7PS> $box.Location = New-Object System.Drawing.Point(60, 20); $box.Width = 200
    8PS> $button = New-Object System.Windows.Forms.Button
    9PS> $button.Text = 'Greet'; $button.Location = New-Object System.Drawing.Point(10, 60)
    10PS> $button.Add_Click({ [System.Windows.Forms.MessageBox]::Show("Hello, $($box.Text)!") })
    11PS> $form.Controls.AddRange(@($label, $box, $button))
    12PS> $form.ShowDialog(); $form.Dispose()

    Add_Click takes a script block that runs when the button is pressed. Inside it, $box.Text reads what the user typed. MessageBox shows the result. ShowDialog displays the form and waits; Dispose releases it afterwards.

    Layout is manual: every control needs a Location and usually a Size. For larger tools, set the form's StartPosition to CenterScreen, anchor controls so they resize with the window, and keep the click handlers short — long work inside a click freezes the window until it finishes.

    Try it: a two-control form

    The smallest useful form: one label, one button, one message. Paste it into a console and watch a window appear:

    1PS> Add-Type -AssemblyName System.Windows.Forms
    2PS> $form = New-Object System.Windows.Forms.Form
    3PS> $form.Text = 'Hello'; $form.Size = New-Object System.Drawing.Size(280, 150)
    4PS> $button = New-Object System.Windows.Forms.Button
    5PS> $button.Text = 'Say hello'; $button.Location = New-Object System.Drawing.Point(80, 40)
    6PS> $button.Add_Click({ [System.Windows.Forms.MessageBox]::Show('Hello from PowerShell!') })
    7PS> $form.Controls.Add($button)
    8PS> $form.ShowDialog(); $form.Dispose()

    Click the button and a message box appears. Close the message box, then close the form — the console prompt returns only after the form closes, because ShowDialog blocks until then.

    Common mistakes

    • Forgetting ShowDialog. A form that is built but never shown does nothing. The script ends and the window never appears.
    • Running Forms in PowerShell 7 without -STA. The form may fail or behave strangely. Launch with pwsh -STA, or run the script from Windows PowerShell 5.1.
    • Expecting it on macOS or Linux. System.Windows.Forms is Windows-only. The same script throws on other platforms.
    • Long work inside a click handler. The window freezes while the handler runs. Keep clicks short, or run heavy work as a job and update the form when it completes.
    • Skipping Dispose. Without it, window handles linger. Always Dispose the form after ShowDialog returns.

    Your turn

    On your own machine, extend the greeting form: add a second text box for a department, and make the button's message include both the name and the department. Then add a second button labeled Clear that empties both boxes.

    Prove it: write it alone

    Blank editor. No starter, no hints. Write a script that shows a form with a text box and a button, where clicking the button displays the typed text in a message box — then closes cleanly.

    Practise this

    Build a greeting form

    Build a form with a text box and a button that greets the name typed into it.

    Review the lesson

    Recap

    Windows Forms turns a script into a clickable window: load the assembly, build a Form, add controls with locations and sizes, wire events with Add_Click, and show it with ShowDialog. It is Windows-only, needs -STA in PowerShell 7, and for richer layouts the next step is WPF with XAML — same idea, declarative layout instead of pixel positions.

    Genie guidePowerShell GUI and Forms

    System.Windows.Forms turns a script into a clickable window. Build the form, add controls, wire a click event, and show it with ShowDialog — Windows only.

    Stuck? Re-read the section, then try the filter yourself.