Powershell new object system windows forms form

Одним из существенных недостатков скриптов PowerShell при использовании их простыми пользователями (не сисадминами или программистами) — его консольный интерфейс. Результат выполнения скрипта PoSh отображается также в командной строке и не всегда удобен для восприятия конечного пользователя. Однако Powershell это мощное и современное средство автоматизации для Windows, которое позволяет прозрачно использовать разнообразные объекты .NET Framework. К примеру, с помощью API. NET вы сможете легко создать простой графический интерфейс для ваших PowerShell скриптов.

Одним из существенных недостатков скриптов PowerShell при использовании их простыми пользователями (не сисадминами или программистами) — его консольный интерфейс. Результат выполнения скрипта PoSh отображается также в командной строке и не всегда удобен для восприятия конечного пользователя. Однако Powershell это мощное и современное средство автоматизации для Windows, которое позволяет прозрачно использовать разнообразные объекты .NET Framework. К примеру, с помощью API. NET вы сможете легко создать простой графический интерфейс для ваших PowerShell скриптов.

В этом примере вы покажем, как с помощью PowerShell создать простую Windows форму и расположить на ней различные стандартные диалоговые элементы (кнопки, поля для ввода,  текстовые элементы, выпадающие списки). К примеру наша задача – написание простого GUI для определения времени последней смены пароля пользователя в Active Directory. Логика скрипта следующая — из AD в выпадающий список загружается список всех учетных записей в домене. Пользователь выбирает учетную запись, нажимает кнопку и в текстовом поле отображается данные о последнем времени смены пароля пользователя.

В данном примере мы используем PowerShell 3.0+ (я буду писать скрипт в PowerShell ISE в Windows 10).

Для использования функционала .NET по созданию форм мы воспользуемся классом System.Windows.Forms. Загрузить данный класс в сессию PowerShell можно так:

Add-Type -assembly System.Windows.Forms

Теперь создадим графическую форму (окно):

$window_form = New-Object System.Windows.Forms.Form

Установим заголовок и размеры окна формы (в пикселях):

$window_form.Text ='Пример графического интерфейса для скрипта PowerShell'
$window_form.Width = 500
$window_form.Height = 200

Чтобы форма автоматически растягивалась, если элементы расположенные на форме выйдут за границы, нужно включить атрибут AutoSize:

$window_form.AutoSize = $true

Теперь можно отобразить форму на экране.

$window_form.ShowDialog()

вывод графической формы в скрипте powershell

Как вы видите, на экране появилась пустая форма указанных размеров. Чтобы добавить не нее различные графические диалоговые элементы, перед строкой $window_form.ShowDialog() добавим следующие строки.

Создадим на форме надпись:

$FormLabel1 = New-Object System.Windows.Forms.Label
$FormLabel1.Text = "Выберите пользователя домена AD"
$FormLabel1.Location = New-Object System.Drawing.Point(0,10)
$FormLabel1.AutoSize = $true
$window_form.Controls.Add($FormLabel1)

Создадим выпадающий список и заполним его списком учетных записей из домена, полученных с помощью командлета Get-ADuser (входит в модуль ActiveDirectory для PowerShell).

$FormComboBox = New-Object System.Windows.Forms.ComboBox
$FormComboBox.Width = 250
$Users = get-aduser -filter * -Properties SamAccountName
Foreach ($User in $Users)
{
$FormComboBox.Items.Add($User.SamAccountName);
}
$FormComboBox.Location = New-Object System.Drawing.Point(60,10)
$window_form.Controls.Add($FormComboBox)

Отобразим еще две надписи. Во второй будет отображаться время последней смены пароля выбранного пользователя.

$FormLabel2 = New-Object System.Windows.Forms.Label
$FormLabel2.Text = "Последняя смена пароля:"
$FormLabel2.Location = New-Object System.Drawing.Point(0,40)
$FormLabel2.AutoSize = $true
$window_form.Controls.Add($FormLabel2)
$FormLabel3 = New-Object System.Windows.Forms.Label
$FormLabel3.Text = ""
$FormLabel3.Location = New-Object System.Drawing.Point(140,60)
$FormLabel3.AutoSize = $true
$window_form.Controls.Add($FormLabel3)

Теперь поместим на форму кнопку действия с надписью «Проверить»:

$FormButton = New-Object System.Windows.Forms.Button
$FormButton.Location = New-Object System.Drawing.Size(400,10)
$FormButton.Size = New-Object System.Drawing.Size(100,20)
$FormButton.Text = "Проверить"
$window_form.Controls.Add($FormButton)

Теперь к созданной кнопке привяжем скрипт проверки, которые должен вызываться при щелчке на кнопке (событие Add_Click). Для преобразования даты из формата TimeStamp в нормальный вид воспользуемся функцией [datetime]::FromFileTime.

$FormButton.Add_Click(
{
$FormLabel3.Text = [datetime]::FromFileTime((Get-ADUser -identity $FormComboBox.selectedItem -Properties pwdLastSet).pwdLastSet).ToString('dd mm yy : hh ss')
}
)

Запустите PowerShell скрипт. Как вы видите, он заполняет выпадающий список именами учётных записей из AD. Если вы выберите нужную учетную запись пользователя и нажмите на кнопку, в поле отобразится время последней смены пароля данного пользователя в Active Directory.

простой GUI для скрипт powershell

Аналогичным образом вы можете создать следующие графические элементы на форме:

  • CheckBox
  • RadioButton
  • TextBox
  • ChekedListBox
  • GroupBox
  • ListBox
  • TabControl
  • ListView
  • TreeView
  • DateTimePicker
  • TrackBar
  • PictureBox
  • ProgressBar
  • HScrollBar
  • VScrollBar
  • ContextMenu
  • Menu

Для вывода диалогового окна с уведомлением пользователю можно испоьзоват следующий код:

[System.Windows.Forms.MessageBox]::Show("Запущен процесс расчета","Предупреждение",0)

Для более удобного и быстрого создания графических элементов для PowerShell форм вы можете воспользоваться онлайн редактор для создания GUI формы для Powershell : https://poshgui.com/Editor.

poshgui - онлайн генератор редактор графических форм для powershell

С помощью него вы сможете создать красивую форму с необходимыми диалоговыми элементами. И получить готовый код графической формы с разными элементами и кнопками для ваших PowerShell скриптов.

RedCreator37

PowerShell is an advanced shell with integration of .NET objects. It’s more than just a replacement for the older cmd.exe. It can work with .NET assemblies, process large datasets and even interact with web services.

Because of the .NET assemblies support, it can work with WinForms (or even WPF), making it possible to create scripts with GUIs.

Requirements

This has been tested to work with Windows PowerShell verion 5.1. It’s likely going to work with older versions as well, but it’s not going to work with the new cross-platform PowerShell (there are no WinForms on Linux/macOS). You can check the version with

Get-Host | Select-Object version

Enter fullscreen mode

Exit fullscreen mode

Setting up the environment

Before we can start, let’s check a few things.

The first one is the script execution policy. It controls which scripts can be run. By default, Windows blocks execution of all scripts (more on that here). We have to allow it to run local scripts that are not digitally signed. It’s possible to do this this either by going through Windows Settings > Updates & Security > For developers, checking the Change execution policy… checkbox and clicking Apply, or just executing

Set-ExecutionPolicy RemoteSigned

Enter fullscreen mode

Exit fullscreen mode

from administrator PowerShell.

Another (less important) thing is the code editor. Even though we could just write the entire script directly in PowerShell, it’s easier to use a full-featured editor with error checking and syntax highlighting. Windows already comes with PowerShell ISE (Integrated Scripting Environment), but you can use Visual Studio Code with the PowerShell extension.

Writing our script

Let’s start!

Importing the assemblies

We have to import both System.Windows.Forms and System.Drawing assemblies. It’s possible to only include the first one but we also need the 2nd to specify control sizes.

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

Enter fullscreen mode

Exit fullscreen mode

You can test it by creating a blank form:

$form = New-Object System.Windows.Forms.Form
$form.ShowDialog()

Enter fullscreen mode

Exit fullscreen mode

A blank form

Adding controls

Let’s make a «Hello World» form. First, we create a top-level Form object:

$form = New-Object System.Windows.Forms.Form
$form.Text = "Some form"
$form.Size = New-Object System.Drawing.Size(150, 145)
$form.AutoSize = $true

Enter fullscreen mode

Exit fullscreen mode

In PowerShell, objects are created using New-Object. You can also pass parameters to constructors, similar to the new keyword in C#. Values are assigned to properties directly. Another difference is using $true instead of just true.

Let’s add a label and a button:

$lbl1 = New-Object System.Windows.Forms.Label
$lbl1.Text = "Hello World!"
$lbl1.Location = New-Object System.Drawing.Point(30, 20);

$btn = New-Object System.Windows.Forms.Button
$btn.Text = "Close"
$btn.location = New-Object System.Drawing.Point(30, 60);
$btn.DialogResult = [System.Windows.Forms.DialogResult]::OK

Enter fullscreen mode

Exit fullscreen mode

The $btn.DialogResult line tells the form what to return when the button is clicked. You can use this to figure out whether the user clicked OK or Cancel. We also make $btn the default button and lay controls onto the form:

$form.AcceptButton = $btn
$form.controls.Add($lbl1)
$form.controls.Add($btn)

Enter fullscreen mode

Exit fullscreen mode

All that’s left is showing the form itself:

$form.ShowDialog()

Enter fullscreen mode

Exit fullscreen mode

A blank form

Event handlers

In our form, $btn is the default OK button which just terminates the form. But we can use non-terminating event handlers as well. For example, let’s make it possible to click on the label:

$lbl1.Add_Click({
    [System.Windows.Forms.MessageBox]::Show("Hey!")
})

Enter fullscreen mode

Exit fullscreen mode

You can call functions from event handlers as you normally would.

Visual Styles

Something I’ve noticed with these GUI scripts is that different control styles are used when the script is run from the PowerShell console instead of VSCode. The console uses legacy rendering which falls back to using Windows 95-style controls. We need to enable Visual Styles to fix that:

[System.Windows.Forms.Application]::EnableVisualStyles()

Enter fullscreen mode

Exit fullscreen mode


From here on, you can add more controls and event handlers. If you’ve used C#/VB.NET before you can reuse large parts of that knowledge to create nicer and more powerful scripts.

That’s it for now 😉


The full script:

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

[System.Windows.Forms.Application]::EnableVisualStyles()

$form = New-Object System.Windows.Forms.Form
$form.Text = "Some form"
$form.Size = New-Object System.Drawing.Size(150, 145)
$form.AutoSize = $true

$lbl1 = New-Object System.Windows.Forms.Label
$lbl1.Text = "Hello World!"
$lbl1.Location = New-Object System.Drawing.Point(30, 20);

$lbl1.Add_Click({
    [System.Windows.Forms.MessageBox]::Show("Hey!")
})

$btn = New-Object System.Windows.Forms.Button
$btn.Text = "Close"
$btn.location = New-Object System.Drawing.Point(30, 60);
$btn.DialogResult = [System.Windows.Forms.DialogResult]::OK

$form.AcceptButton = $btn
$form.controls.Add($lbl1)
$form.controls.Add($btn)

$Null = $form.ShowDialog()

Enter fullscreen mode

Exit fullscreen mode

For those of you familiar with Scripting languages you are probably used to using alternate applications like Visual Studio when you want to create GUIs for your scripts.

There are a handful of other utilities for PowerShell too, which are a little cheaper to buy and still have the benefit of speeding up things like creating a GUI for your PowerShell script and Forms etcetera.

Rather than spending money and having to learn how to use an additional tool, if you do not mind a little extra finger work, you can code your own GUI and Forms in PowerShell, and if you want you can even make use of Extensible Application Markup Language (XAML) language, and a handful of similar templates to help speed things up.

Right now though, all that is more advanced, and beyond the focus of this introduction to creating a Form. Later, in other Tutorials I’ll cover these advanced techniques, but for now let’s get on with the basics.

As PowerShell is centered around the .Net Framework you can make use of the [System.Windows.Forms] assembly to create a new object System.Windows.Forms.Form.

As the [System.Windows.Forms] assembly is does not automatically load in PowerShell you need to add this assembly yourself by using the cmdlet Add-Type and the -AssemblyName parameter, then having done this create your new object.

Note: You can also load the assembly using the following command:

Code:

[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")


Let’s take a look at the simpler (and easier to remember) method combined with a basic script:

Code:

# Load the System.Windows.Forms assembly into PowerShell
Add-Type -AssemblyName System.Windows.Forms

# Create a new Form object and assign to the variable $Form
$Form = New-Object System.Windows.Forms.Form

#Initialize Form so it can be seen
$Form.showDialog()

If we open PowerShell or PowerShell ISE and run this basic script the following GUI form is generated:

PS59.png

Obviously, this form is of no practical use to us yet, but it is a platform we can use to start creating a more useful Form.

Take a look at the following code block:

Code:

# Add description to the Form's titlebar
$Form.Text = "The Titlebar - Form"

# Create a new Label object
$Label = New-Object System.Windows.Forms.Label

# Add some text to label
$Label.Text = "Some random text to display"

# Use AutoSize to guarantee label is correct size to contain text we add
$Label.AutoSize = $true

# Add Label to form
$Form.Controls.Add($Label)

We can add this to our original Script to add a description to our Form’s Title bar, and also add a new dimension to the form: the Label object, which we can use to add some text into the form.

PS60.png

  • By using the .Text method with out $Form variable we are able to add a description to the Form’s title bar.
  • We can create a new element in our form; the Label using the New-Object cmdlet to introduce the System.Windows.Forms.Label and this can be assigned to a variable.
  • Once we have assigned the Label to a variable we can then use the .Text method to add text to the label.
  • Given the text we add might be short or very long, its a good idea to make use of the AutoSize method to guarantee the label is large enough to hold whatever text we choose to enter for the label.
  • Now all you need to do is add the Label to our current Form using the Controls.add(<variable>).
  • The rest of the script is the initial building block you previously created.

Starting to be a more useful form now, but for the fact the default font size is not too great for those of us who need glasses to read it, and do you actually like the font face, what if you want something a little different; perhaps to make the form a little nicer, or perhaps because a certain font face is easier to read for you.

Not a problem, we can continue building on our code by adding some additional commands. For example:

Code:

# Choose a font face, font size, and style
# Other styles include Bold, Italic, Underline and Strikeout
# Note:  Fontface must be present on users computer
$FontFace = New-Object System.Drawing.Font(
  "Comic Sans MS",20,[System.Drawing.FontStyle]::Regular
)

# Initialize the forms font
$Form.Font = $FontFace

# AutoSize ensures the Form size can contain the text
$Form.AutoSize = $true

# Allow user to resize too vs "GrowAndShrink" - user cannot resize
$Form.AutoSizeMode = "GrowOnly"

Now if we add this to our building block we get:

PS61.png

  • This introduces a little more finger work!
  • First we need to create a new variable to store our Font information. You can call that variable whatever you like. Then you need to assign a new object to it which allows you to introduce System.Windows.Font(<Name>,<Size>,<Style>)
  • We then need to initialize our Forms font by assigning our new object to our $Form.Font(<object>)
  • As we are using a large font size, we include the AutoSize() for $Form to ensure the form automatically sizes to hold our text message in an easy to read format: $Form.AutoSize = $true
  • In addition I’ve added an additional option AutoSizeMode (which is not compulsory), but allows the Form to increase in size using an aspect ratio, e.g. Width x Depth: $Form.AutoSizeMode = «GrowOnly». You can control the Form size by adjusting its width only, by using «GrowAndShrink» instead.

    Using GrowAndShrink with above code would result in:

    PS62.png

Apart from controlling these aspects of the Form, one can also take control of the Caption (i.e. Minimize, Maximize & Close buttons), as well as hide or show the SizeGrip which is what allows a user to adjust the size of the active window by dragging the bottom right corner. You can also control the opacity of the Form (i.e. whether you can see other windows beneath it and to what degree), and also determine what position on the Screen the Form will open to when activated. You can also control whether or not the activated Form will have an icon appear on the taskbar.

To achieve all this we’ll need to continue building on our Script code by adding some additional blocks of code, like below:

Code:

# Set Form State by controlling Window settings
# Windows setting options: Minimized, Normal or Maximized
# $false disables a particular Caption choice for the user
$Form.MinimizeBox = $false
$Form.MaximizeBox = $false
$Form.WindowsState = "Normal"

# Set whether user can see Form icon in taskbar
# $false = not seen, $true = seen
$Form.ShowInTaskbar = $false

# Set whether user has access to the Size Grip for window
# Options include Auto, Hide or Show
$Form.SizeGripStyle = "Show"

# Set what position Form opens in on users screen
# Choices include:
# CenterScreen, Manual, WindowsDefaultLocation,
# WindowsDefaultBounds, CenterParent
$Form.StartPosition = "CenterParent"          

# Set the opacity level for window, i.e. how transparent it is
# Range is from 0.0 (invisible) to 1.0 (100% opaque)
$Form.Opacity = 0.6

When added to our script we get:

PS63.png

  • So in the above example by setting $Form.MinimizeBox and $Form.MaximizeBox to $false we are disabling them. If instead you assign $true to them the user will be able to use these buttons.
  • The $Form.WindowState can be assigned Minimized, Normal or Maximized which of course determines how the Form will open and display to the user.
  • In this example I set $Form.showInTaskbar to $false. This prevents the Forms icon appearing on the taskbar, but if you want that to appear there, assign $true instead.
  • The $Form.SizeGripStyle allows us to set one of three choices: Auto, Hide, or Show. By assigning Hide the grip icon normally located on the bottom right corner of our Form will not be viewed. The user can still adjust the size though, by hovering mouse to get the double-arrow.
  • The opacity can be set from 0.0 (invisible) to 1.0 which is 100% opaque (i.e. cannot be seen through)
  • The StartPosition can be one of several choices as illustrated in the code comments. In my case I used CenterParent which causes the Form to open in the top left corner, but if you wanted the Form to open in the actual center of the screen you’d go for $Form.StartPosition = «CenterScreen». Try the other choices, to determine which position you prefer. The position you settle on may in part be determined by your opacity setting.

Now for the Form background itself. We can control this too and customize its look to our person preferences. Take a look at the following code snippet:

Code:

# Add a background color to form
# You can use the Static color names found in System.Drawing.Color
# Static Color example: "Blue"
# Or you can use ARGB values (i.e. Alpha, Red, Green & Blue)
# by using 4 pairs of letters from A to F to represent level of
# Alpha, Red, Green & Blue in color
# ARGB example: "#FFFFABCD"
$Form5.BackColor = "#FFFFABCD"

I won’t be mean and ask you to guess the colour #FFFFABCD represents.

Hint:
think Sticky Notes.

Now add this to our script and we get:

PS64.png

I won’t go into it here, but suffice to say you could either convert this Script to an executable which you could place in your Startup folder, or create a new task to open the form at the desired time, and create your very own custom Sticky Notes.

Now you are not just stuck with pretty colours. Why not use a picture instead:

Code:

# Replace BackGround with an image instead
# Makes use of [System.Drawing.Image]
# This allows you to create an image Object from a file
# which can be used as a background to your form
$BackgroundPicture = [System.Drawing.Image]::FromFile(
  # Image obtained Royalty Free from https://www.pexels.com/
  "$env:UserProfilePicturescatface.jpg"
)

# Set the image to be used
$Form.BackgroundImage = $BackgroundPicture

# Set Layout for image
# Default = Tile which will tile the image
# inside form as many times as it will fit
# Choices include, None, Tile, Center, Stretch and Zoom
$Form.BackgroundImageLayout = "None"

# As we are using image, it is better to
# set the Form size manually to fit the image size
# Thus we do not use AutoSize (although you can if you want to)
# Instead we use Width and Height to ensure a tidy fit for image
# The cool thing is you can let the computer work that size out
$Form.Width = $BackgroundPicture.Width
$Form.Height = $BackgroundPicture.Height

As you can see in the comments of this code block, there are a number of ways to manipulate an image and the form itself to help improve the final look of a Form should you choose to use a background image.

  • Initially we create our own variable ($BackgroundPicture in my case but you can name it what you like) to store an image on our computer.
  • Next we make use of [System.Drawing.Image]::FromFile(<path to file><filename>) to set a path to the image.
  • You then need to assign your variable: $Form.BackgroundImage = $BackgroundPicture
  • Next you’ll want to determine the layout of your image. There are several choices as mentioned in the codes comments. I’ve chosen $Form.BackgroundImageLayout = «None» because I will be setting the Form to size itself to my image, and thus do not need to worry about tiling, stretching, or positioning the image.

    Note:
    If I did not choose any option here at all, the default behaviour would be for the image chosen to be tiled, hence given I do not want tiling, I had to choose an option!
  • You can set the Form’s width and height to suit your image if you know the images dimensions, but rather than having to work all that out yourself, just let the computer do the work for you by using the .Width and .Height methods, e.g. $Form.Width = $BackgroundImage.Width
  • As I’ve introduced a background image, I need to consider the appearance of my Label which contains the text you are reading in the Form. I do not want it interfering with the image, so I set it to be transparent, by including this snippet: $Label.BackColor = «Transparent»

So after adjusting our script to include these details:

PS65.png

And if we did not make that Label transparent we’d see:

PS66.png


Not so nice with a non transparent label!

Well that concludes the introduction to creating your own GUI Forms in PowerShell.

In Part 2 of this tutorial I’ll take a look at how we add buttons, interactive text fields, Lists and the ability to drag and drop items into our Form.

Till then happy PowerShelling !

Regards,

Regedit32

I’ve been getting questions about how to use PowerShell and windows forms for a while from different customers and friends, so I’ve decided to write a series of posts covering the basics and show what you can do with this.

So in this post I will show you the basics on how to create a form and add two buttons to it and some text. (Complete code at the end)

Part 1:

To start with, we need to load two assemblys;

  • System Windows Forms
  • System Drawing

We do this by adding these two lines

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

Next we need to create our main form, the window that will show when the script is run:

$MainForm = New-Object System.Windows.Forms.Form
$MainForm.Text = "First Form"
$MainForm.Size = New-Object System.Drawing.Size(800,600)
$MainForm.KeyPreview = $True
$MainForm.FormBorderStyle = "1"
$MainForm.MaximizeBox = $false
$MainForm.StartPosition = "CenterScreen"

Starting from the top in the code above, we create a new Windows Form called $MainForm and name the application “First Form”

The size of the windows should be 800*600 and we should accept input from the keyboard, such as “Enter” and “Tab”.

“BorderStyle” is a value between 1 and 7, I always use 1, but experiment to find what looks best in your eyes!

We also make sure that the user can’t maximize the window and that the application starts in the center of the screen.

Next we need to activate the form when we run the script. This is done by adding the following:

$MainForm.Add_Shown({$MainForm.Activate()})
[void] $MainForm.ShowDialog()

If we run the complete code that should look like the one below, this is the result we get:

MainForm

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

### Add Forms ###
$MainForm = New-Object System.Windows.Forms.Form
$MainForm.Text = "First Form"
$MainForm.Size = New-Object System.Drawing.Size(800,600)
$MainForm.KeyPreview = $True
$MainForm.FormBorderStyle = "1"
$MainForm.MaximizeBox = $false
$MainForm.StartPosition = "CenterScreen"

### Activate Form
$MainForm.Add_Shown({$MainForm.Activate()})
[void] $MainForm.ShowDialog()

Part 2:

The code above is a good start, but there’s not really anything we can do, more than showing a box. In this part we will resize the window, add a button and some text.

Let’s start with changing the size of the window to 400*200 by changing the line to:

$MainForm.Size = New-Object System.Drawing.Size(400,200)

Next, we need to add a button. To do this we add this code after we created to form, but before we activate it:

$Close = New-Object System.Windows.Forms.Button
$Close.Size = New-Object System.Drawing.Size(75,25)
$Close.Location = New-Object System.Drawing.Size(165,110)
$Close.Text = "Close"
$MainForm.Controls.Add($Close)
$Close.add_click({[void] $MainForm.Close()})

This will add a button called “Close” to our form with the size of 75*25 on the coordinates 165,110. When we press the button it will close the form by running:

[void] $MainForm.Close()

(You can also call a function by adding the name of the function between ({ <function> }))

Go ahead and run the script and try it out, the window should look like this:

Button

Part 3:

The last part in this post will show you how to add some text as well, a window with a button isn’t that helpful unless you know what it does.

After the code that contains the button, we add this:

$Text = New-Object System.Windows.Forms.Label
$Text.Size = New-Object System.Drawing.Size(300,50)
$Text.Location = New-Object System.Drawing.Size(70,50)
$Text.Text = "Press the button to close this window"
$MainForm.Controls.Add($Text)

Now when you run the script, it will look like the following:

Text
Stay tuned for more posts on how to use Windows Forms with PowerShell!

The complete code:

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

### Add Forms ###
$MainForm = New-Object System.Windows.Forms.Form
$MainForm.Text = "First Form"
$MainForm.Size = New-Object System.Drawing.Size(400,200)
$MainForm.KeyPreview = $True
$MainForm.FormBorderStyle = "1"
$MainForm.MaximizeBox = $false
$MainForm.StartPosition = "CenterScreen"

### Add Buttons ###
$Close = New-Object System.Windows.Forms.Button
$Close.Size = New-Object System.Drawing.Size(75,25)
$Close.Location = New-Object System.Drawing.Size(165,110)
$Close.Text = "Close"
$MainForm.Controls.Add($Close)
$Close.add_click({[void] $MainForm.Close()})

### Add Lables ###
$Text = New-Object System.Windows.Forms.Label
$Text.Size = New-Object System.Drawing.Size(300,50)
$Text.Location = New-Object System.Drawing.Size(70,50)
$Text.Text = "Press the button to close this window"
$MainForm.Controls.Add($Text)

### Activate Form
$MainForm.Add_Shown({$MainForm.Activate()})
[void] $MainForm.ShowDialog()

I have created many PowerShell scripts for the last couple of years. All with a single purpose to automate my IT work as much as possible. But this week I needed to create a script that could be run by the users themselves. And users and command line isn’t the best combination, so let’s take a look at using the PowerShell GUI.

We all love PowerShell for the simplicity and efficientness when it comes to scripting, but for normal users working with a CLI isn’t something they are used to. In this article, I will explain how you can create a simple GUI for your script and help you through the basic obstacles.

The example project

In this article I will use one of my own projects as an example, so let me first tell a bit about the problem I needed to solve.

I work for a construction company and our construction sites are connected over a 4G IPVPN network with the datacenter. Because of double natting, we can’t deploy printers with a print server.

Our users can install a printer themselves, but because they are not listed on the print server they can easily search for the printer. They will need to create a TCP/IP port, find the IP Address of the printer, select the driver and give a printer name.

I created a PowerShell script that I could run remotely to do this in the background, but that would require them to call me, so I could run the script in the background. Now I could just install all the printers, but that would only be confusing and resulting in a long list of printers.

So I figured, the PowerShell script only needs the printer model, Ip Address and a name. We can look up the Ip Address so if the user can select a model and fill in a name we are done. Saving me 2 calls a week.

The basics of the PowerShell GUI

Before we start creating a form is it important to know that the PowerShell script is run sequentially. So you define your form and show it. But any code after you displayed the form won’t be executed until you close the form.

This is something that took me 30 minutes to figure out… I simply thought I could show the form and handle the input below it in PowerShell.

Creating the form

We start with a blank form. First, we add the .Net Windows. Forms. Also, we need to define a size for the form (width, height), title and background color. Just copy-paste the code below in the PowerShell ISE and click on run

# Init PowerShell Gui
Add-Type -AssemblyName System.Windows.Forms

# Create a new form
$LocalPrinterForm                    = New-Object system.Windows.Forms.Form

# Define the size, title and background color
$LocalPrinterForm.ClientSize         = '500,300'
$LocalPrinterForm.text               = "LazyAdmin - PowerShell GUI Example"
$LocalPrinterForm.BackColor          = "#ffffff"

# Display the form
[void]$LocalPrinterForm.ShowDialog()

You will see a simple form just like this one below:

PowerShell form

Adding elements to your form

On our form, we can add elements. These can be used to display information and gather user input. The place of the input is based on points/pixels from the left side and the top side. So the location 20,50 is 20 pixels from the left side and 50 points from the top side.

We have the following elements that we can use on our forms:

  • TextBox (to get user input)
  • Label
  • Button
  • PictureBox
  • CheckBox
  • ComboBox (Dropdown list)
  • ListView
  • ListBox
  • RadioButton
  • Panel
  • Groupbox (To group elements together)
  • ProgressBar
  • DataGridView

So let’s create some elements on our form. Add the code below to your script. Make sure that ShowDialog is at the end of your script.

# Create a Title for our form. We will use a label for it.
$Titel                           = New-Object system.Windows.Forms.Label

# The content of the label
$Titel.text                      = "Adding new printer"

# Make sure the label is sized the height and length of the content
$Titel.AutoSize                  = $true

# Define the minial width and height (not nessary with autosize true)
$Titel.width                     = 25
$Titel.height                    = 10

# Position the element
$Titel.location                  = New-Object System.Drawing.Point(20,20)

# Define the font type and size
$Titel.Font                      = 'Microsoft Sans Serif,13'

# Other elemtents
$Description                     = New-Object system.Windows.Forms.Label
$Description.text                = "Add a new construction site printer to your computer. Make sure you are connected to the network of the construction site."
$Description.AutoSize            = $false
$Description.width               = 450
$Description.height              = 50
$Description.location            = New-Object System.Drawing.Point(20,50)
$Description.Font                = 'Microsoft Sans Serif,10'

$PrinterStatus                   = New-Object system.Windows.Forms.Label
$PrinterStatus.text              = "Status:"
$PrinterStatus.AutoSize          = $true
$PrinterStatus.location          = New-Object System.Drawing.Point(20,115)
$PrinterStatus.Font              = 'Microsoft Sans Serif,10,style=Bold'

$PrinterFound                    = New-Object system.Windows.Forms.Label
$PrinterFound.text               = "Searching for printer..."
$PrinterFound.AutoSize           = $true
$PrinterFound.location           = New-Object System.Drawing.Point(75,115)
$PrinterFound.Font               = 'Microsoft Sans Serif,10'

# ADD OTHER ELEMENTS ABOVE THIS LINE

# Add the elements to the form
$LocalPrinterForm.controls.AddRange(@($Titel,$Description,$PrinterStatus,$PrinterFound))

# THIS SHOULD BE AT THE END OF YOUR SCRIPT FOR NOW
# Display the form
[void]$LocalPrinterForm.ShowDialog()

What you see here is that every element is created. These are all simple text or labels elements. You can define the width and height of each element, but if your content is longer then the element it will only be partially displayed. So by setting the Autosize to true, you are assured that the user can read the whole label.

Every element is given a location, the first digit is the number of pixels from the left side, the second the number of pixels from the top.

The result will look like this:

Adding elements to PowerShell GUI Form

Using a dropdown list

Our user needs to select the printer manufacturer, we only use two brands, so I radio button could work too. We use generic print drivers so I don’t need to know the specific model of the printer.

$PrinterType                     = New-Object system.Windows.Forms.ComboBox
$PrinterType.text                = ""
$PrinterType.width               = 170
$printerType.autosize            = $true

# Add the items in the dropdown list
@('Canon','Hp') | ForEach-Object {[void] $PrinterType.Items.Add($_)}

# Select the default value
$PrinterType.SelectedIndex       = 0
$PrinterType.location            = New-Object System.Drawing.Point(20,210)
$PrinterType.Font                = 'Microsoft Sans Serif,10'

The code above is pretty clear I think, we create the combo box and add the items with a single line foreach loop to the list. Again we also define the position and I have set a minimum width for the element.

You can set a default value for your dropdown list by selecting the index. Make sure you add the variable of the element to the $LocalPrinterForm.controls.AddRange, otherwise it won’t be displayed.

Adding buttons

We are also going to add some buttons to our form. A button can have a standard action, like (
OK, Cancel, Abort, Retry, Ignore, Yes, or No) or you can assign a custom function to it.

The buttons we are going to add are Cancel and Add Printer. Cancel will just close the form and does nothing else, while AddPrinter will run our logic to add the printer. Add the buttons with the code below, again make sure you add the variables of the buttons to the $LocalPrinterForm.controls.AddRange

$AddPrinterBtn                   = New-Object system.Windows.Forms.Button
$AddPrinterBtn.BackColor         = "#a4ba67"
$AddPrinterBtn.text              = "Add Printer"
$AddPrinterBtn.width             = 90
$AddPrinterBtn.height            = 30
$AddPrinterBtn.location          = New-Object System.Drawing.Point(370,250)
$AddPrinterBtn.Font              = 'Microsoft Sans Serif,10'
$AddPrinterBtn.ForeColor         = "#ffffff"

$cancelBtn                       = New-Object system.Windows.Forms.Button
$cancelBtn.BackColor             = "#ffffff"
$cancelBtn.text                  = "Cancel"
$cancelBtn.width                 = 90
$cancelBtn.height                = 30
$cancelBtn.location              = New-Object System.Drawing.Point(260,250)
$cancelBtn.Font                  = 'Microsoft Sans Serif,10'
$cancelBtn.ForeColor             = "#000"
$cancelBtn.DialogResult          = [System.Windows.Forms.DialogResult]::Cancel
$LocalPrinterForm.CancelButton   = $cancelBtn
$LocalPrinterForm.Controls.Add($cancelBtn)

Now a lot of examples on other websites discard the results of the dialog by using [void]$form.ShowDialog(). But the return value of the dialog should be used to tell how the user has closed the form.

So we are going to modify the ShowDialog cmd.

$result = $LocalPrinterForm.ShowDialog()

This way we can check if the user has pressed Cancel or any other default button in the form

if ($result –eq [System.Windows.Forms.DialogResult]::Cancel)
{
    write-output 'User pressed cancel'
}

Adding a custom function to a button

When the user clicks Add Printer a custom script should be executed. We can create a function with our logic and assign the function to the button with the following cmd

$AddPrinterBtn.Add_Click({ AddPrinter })

And our function

function AddPrinter { 
	# ADDING PRINTER LOGIC GOES HERE
}

Result

So after we added all elements, and I added some extra-label, our final PowerShell GUI form looks like this:

PowerShell GUI Example

Now we only have to finish our script so a printer is added.

Adding the logic

So with our form in place, we can start by adding the logic. During the execution of your script, you might want to change text, show or remove fields or perform a specific action. Lets first start with outlining our PowerShell script.

#------------[Initialisations]------------

# Init PowerShell Gui
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing


#---------------[Form]-------------------

# Our form goes here

[System.Windows.Forms.Application]::EnableVisualStyles()

$LocalPrinterForm                    = New-Object system.Windows.Forms.Form
$LocalPrinterForm.ClientSize         = '480,300'
$LocalPrinterForm.text               = "LazyAdmin - PowerShell GUI Example"
$LocalPrinterForm.BackColor          = "#ffffff"
$LocalPrinterForm.TopMost            = $false
$Icon                                = New-Object system.drawing.icon ("./form.ico")
$LocalPrinterForm.Icon               = $Icon

# THE REST OF THE FORM

$LocalPrinterForm.controls.AddRange(@( "<ALL FORM ELEMENTS>" ))

#------------[Functions]------------

function AddPrinter { 
    # Function that is triggered with the add printer button
}

#------------[Script]------------

$AddPrinterBtn.Add_Click({ AddPrinter })

# REST OF YOUR SCRIPT


#------------[Show form]------------

# Show the form
$result = $LocalPrinterForm.ShowDialog()

# Catch any output of the form
if ($result –eq [System.Windows.Forms.DialogResult]::Cancel)
{
    write-output 'User pressed cancel'
}

In your functions or script section, you might want to give some feedback to the user or use the data from the input fields.

Reading and changing PowerShell GUI Elements

Reading data from the textbox is pretty straight forward. In our example, we want to use the name the users have entered and check if the printer name already exists. If the name exists we want to show an error.

if (Get-Printer -Name $printerName.text) {
	$PrinterStatus.ForeColor = "#D0021B"
	$PrinterStatus.Text = 'Printer name already exists.'
}

In the example above we check the given printer name, if it exists we change the font color of the label “PrinterStatus” to read and change the text of the label to show a message to the users.

There is no need to refresh the form or input, you can just change the text as you go.

Showing and hiding fields

Before the form is loaded I check if there is a printer available on the expected network address. If not then there is no need for the user to continue. So when I initialize the form elements I have hidden some elements.

If we have a connection, then I show the elements or change the label of the buttons for example. Hiding and show elements in PowerShell GUI can be done with the following code:

$PrinterNameLabel.Visible = $false # or $true of course 😉

Online PowerShell GUI Editor PoshGUI

Creating a bigger or complex form from the command line only can be a bit challenging. You will have to position all the elements in the correct place, create all the necessary code. Luckily there is an online PowerShell GUI editor PoshGUI.

Online PowerShell Editor PoshGUI

This is a great tool to create your initial form layout. You can download the PS1 file and continue working on your form in your favorite editor.

Conclusion

So I hope this article helpt you to get started creating your first PowerShell form. While PowerShell isn’t really meant for creating forms, it can be a great way to make a script more usable to your user or colleagues.

Subscribe to the newsletter to receive the latest article about PowerShell or Office 365 in your mailbox.

The complete code looks as follows:

#---------------------------------------------------------[Initialisations]--------------------------------------------------------
# Init PowerShell Gui
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing


#---------------------------------------------------------[Form]--------------------------------------------------------

[System.Windows.Forms.Application]::EnableVisualStyles()

$LocalPrinterForm                    = New-Object system.Windows.Forms.Form
$LocalPrinterForm.ClientSize         = '480,300'
$LocalPrinterForm.text               = "Printers"
$LocalPrinterForm.BackColor          = "#ffffff"
$LocalPrinterForm.TopMost            = $false
$Icon                                = New-Object system.drawing.icon ("//thunnissen.local/netlogon/printer.ico")
$LocalPrinterForm.Icon               = $Icon

$Titel                           = New-Object system.Windows.Forms.Label
$Titel.text                      = "Add new printer"
$Titel.AutoSize                  = $true
$Titel.width                     = 25
$Titel.height                    = 10
$Titel.location                  = New-Object System.Drawing.Point(20,20)
$Titel.Font                      = 'Microsoft Sans Serif,13'

$Description                     = New-Object system.Windows.Forms.Label
$Description.text                = "To add a printer, make sure you are connected to the same network as the printer.."
$Description.AutoSize            = $false
$Description.width               = 450
$Description.height              = 50
$Description.location            = New-Object System.Drawing.Point(20,50)
$Description.Font                = 'Microsoft Sans Serif,10'

$PrinterStatus                   = New-Object system.Windows.Forms.Label
$PrinterStatus.text              = "Status:"
$PrinterStatus.AutoSize          = $true
$PrinterStatus.width             = 25
$PrinterStatus.height            = 10
$PrinterStatus.location          = New-Object System.Drawing.Point(20,115)
$PrinterStatus.Font              = 'Microsoft Sans Serif,10,style=Bold'

$PrinterFound                    = New-Object system.Windows.Forms.Label
$PrinterFound.text               = "Searching for printer..."
$PrinterFound.AutoSize           = $true
$PrinterFound.width              = 25
$PrinterFound.height             = 10
$PrinterFound.location           = New-Object System.Drawing.Point(100,115)
$PrinterFound.Font               = 'Microsoft Sans Serif,10'

$PrinterDetails                  = New-Object system.Windows.Forms.Label
$PrinterDetails.text             = "Printer details"
$PrinterDetails.AutoSize         = $true
$PrinterDetails.width            = 25
$PrinterDetails.height           = 10
$PrinterDetails.location         = New-Object System.Drawing.Point(20,150)
$PrinterDetails.Font             = 'Microsoft Sans Serif,12'
$PrinterDetails.Visible          = $false

$PrinterNameLabel                = New-Object system.Windows.Forms.Label
$PrinterNameLabel.text           = "Name:"
$PrinterNameLabel.AutoSize       = $true
$PrinterNameLabel.width          = 25
$PrinterNameLabel.height         = 20
$PrinterNameLabel.location       = New-Object System.Drawing.Point(20,180)
$PrinterNameLabel.Font           = 'Microsoft Sans Serif,10,style=Bold'
$PrinterNameLabel.Visible        = $false

$PrinterName                     = New-Object system.Windows.Forms.TextBox
$PrinterName.multiline           = $false
$PrinterName.width               = 314
$PrinterName.height              = 20
$PrinterName.location            = New-Object System.Drawing.Point(100,180)
$PrinterName.Font                = 'Microsoft Sans Serif,10'
$PrinterName.Visible             = $false

$PrinterTypeLabel                = New-Object system.Windows.Forms.Label
$PrinterTypeLabel.text           = "Brand:"
$PrinterTypeLabel.AutoSize       = $true
$PrinterTypeLabel.width          = 25
$PrinterTypeLabel.height         = 20
$PrinterTypeLabel.location       = New-Object System.Drawing.Point(20,210)
$PrinterTypeLabel.Font           = 'Microsoft Sans Serif,10,style=Bold'
$PrinterTypeLabel.Visible        = $false

$PrinterType                     = New-Object system.Windows.Forms.ComboBox
$PrinterType.text                = ""
$PrinterType.width               = 170
$PrinterType.height              = 20
@('Canon','Hp') | ForEach-Object {[void] $PrinterType.Items.Add($_)}
$PrinterType.SelectedIndex       = 0
$PrinterType.location            = New-Object System.Drawing.Point(100,210)
$PrinterType.Font                = 'Microsoft Sans Serif,10'
$PrinterType.Visible             = $false

$AddPrinterBtn                   = New-Object system.Windows.Forms.Button
$AddPrinterBtn.BackColor         = "#ff7b00"
$AddPrinterBtn.text              = "Add"
$AddPrinterBtn.width             = 90
$AddPrinterBtn.height            = 30
$AddPrinterBtn.location          = New-Object System.Drawing.Point(370,250)
$AddPrinterBtn.Font              = 'Microsoft Sans Serif,10'
$AddPrinterBtn.ForeColor         = "#ffffff"
$AddPrinterBtn.Visible           = $false

$cancelBtn                       = New-Object system.Windows.Forms.Button
$cancelBtn.BackColor             = "#ffffff"
$cancelBtn.text                  = "Cancel"
$cancelBtn.width                 = 90
$cancelBtn.height                = 30
$cancelBtn.location              = New-Object System.Drawing.Point(260,250)
$cancelBtn.Font                  = 'Microsoft Sans Serif,10'
$cancelBtn.ForeColor             = "#000"
$cancelBtn.DialogResult          = [System.Windows.Forms.DialogResult]::Cancel
$LocalPrinterForm.CancelButton   = $cancelBtn
$LocalPrinterForm.Controls.Add($cancelBtn)

$LocalPrinterForm.controls.AddRange(@($Titel,$Description,$PrinterStatus,$PrinterFound,$PrinterName,$PrinterNameLabel,$PrinterType,$AddPrinterBtn,$cancelBtn,$PrinterTypeLabel,$PrinterDetails))

#-----------------------------------------------------------[Functions]------------------------------------------------------------

function AddPrinter { 
	$PrinterFound.ForeColor = "#000000"
	$PrinterFound.Text = 'Adding printer...'
	# Check printer port
	$portName = "TCPPort:"+$printerIp
	$portExists = Get-Printerport -Name $portname -ErrorAction SilentlyContinue

	# Create port if it not exists
	if (-not $portExists) {
		$PrinterFound.Text = 'Creating printer port...'
		Add-PrinterPort -name $portName -PrinterHostAddress $printerIp
	}

	# Select the correct driver
	if ($PrinterType.SelectedItem -eq 'Canon') {
		$printerDriverName = "Canon Generic Plus PCL6"
	}else{
		$printerDriverName = "HP LaserJet M227-M231 PCL-6"
	}

	# Check if printer driver exists
	$printDriverExists = Get-PrinterDriver -name $printerDriverName -ErrorAction SilentlyContinue

	# Install printer or printer driver and printer
	if ($printDriverExists) {
		$PrinterFound.Text = 'Installing printer...'
		Add-Printer -Name $printerName.text -PortName $portName -DriverName $printerDriverName 
	}else{
		$PrinterFound.Text = 'Installing printer driver...'
		Add-PrinterDriver -name $printerDriverName

		$PrinterFound.Text = 'Installing printer...'
		Add-Printer -Name $printerName.text -PortName $portName -DriverName $printerDriverName
	}

	if (Get-Printer -Name $printerName.text) {
		$PrinterFound.ForeColor = "#7ed321"
		$PrinterFound.Text = 'The printer is installed'
	}
	else {
		$PrinterFound.ForeColor = "#D0021B"
		$PrinterFound.Text = 'Installation failed'
	}
	$PrinterNameLabel.Visible = $false
	$PrinterName.Visible = $false
	$PrinterType.Visible = $false
	$AddPrinterBtn.Visible = $false
	$PrinterDetails.Visible = $false
	$PrinterTypeLabel.Visible = $false
	$cancelBtn.text = "Close"
}

#---------------------------------------------------------[Script]--------------------------------------------------------
# Get printers IP Address
$clientIP = (
    Get-NetIPConfiguration |
    Where-Object {
        $_.IPv4DefaultGateway -ne $null -and
        $_.NetAdapter.Status -ne "Disconnected"
    }
).IPv4Address.IPAddress

$networkAddress = $clientIP.Split('.')
$networkAddress = $networkAddress[0]+"."+$networkAddress[1]+"."+$networkAddress[2]

# Check if printer is online
$printerIp =  $networkAddress + ".31" 
$testConnection = Test-Connection $printerIp -count 1 -Quiet

If ($testConnection) {
	$PrinterFound.text = "Printer found"
	$PrinterFound.ForeColor = "#7ed321"
	$PrinterNameLabel.Visible = $true
	$PrinterName.Visible = $true
	$PrinterType.Visible = $true
	$AddPrinterBtn.Visible = $true
	$PrinterDetails.Visible = $true
	$PrinterTypeLabel.Visible = $true
}else{
	$PrinterFound.text = "No printers found"
	$PrinterFound.ForeColor = "#D0021B"
	$cancelBtn.text = "Sluiten"
}

$AddPrinterBtn.Add_Click({ AddPrinter })

[void]$LocalPrinterForm.ShowDialog()

You can also check the following articles:

  • Adding printers and printer ports with PowerShell
  • Migrating home folders to OneDrive with PowerShell
  • Getting started with Microsoft Flow

Понравилась статья? Поделить с друзьями:

Вот еще несколько интересных статей:

  • Powershell ise скачать для windows 10
  • Powershell export csv encoding windows 1251
  • Powershell exe windows powershell что это
  • Powershell exe windows powershell 32 бита
  • Powershell active directory module windows server 2008

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии