PowerShell GUI - Как начать использовать
За последние пару лет я создал много скриптов PowerShell. Все они были созданы с единственной целью - автоматизировать мою работу в ИТ насколько это возможно. Но на этой неделе мне нужно было создать скрипт, который могли бы запускать сами пользователи. А пользователи и командная строка - не лучшее сочетание, поэтому давайте рассмотрим использование графического интерфейса PowerShell.
Мы все любим PowerShell за простоту и эффективность в написании скриптов, но для обычных пользователей работа с интерфейсом командной строки не является чем-то привычным. В этой статье я объясню, как вы можете создать простой графический интерфейс для вашего скрипта и помочь вам преодолеть основные препятствия.
Пример проекта
В этой статье я использую один из своих проектов в качестве примера, поэтому сначала позвольте мне немного рассказать о проблеме, которую мне нужно было решить.
Я работаю в строительной компании, и наши строительные площадки соединены через 4G IPVPN сеть с дата-центром. Из-за двойного NAT мы не можем развернуть принтеры с сервером печати.
Наши пользователи могут самостоятельно установить принтер, но поскольку они не указаны на сервере печати, им будет легко найти принтер. Им нужно будет создать порт TCP/IP, узнать IP-адрес принтера, выбрать драйвер и задать имя принтера.
Я создал скрипт PowerShell, который мог бы запускаться удаленно, чтобы сделать это в фоновом режиме, но это потребовало бы, чтобы они позвонили мне, чтобы я мог запустить скрипт в фоновом режиме. Сейчас я мог бы просто установить все принтеры, но это только запутало бы ситуацию и привело бы к длинному списку принтеров.
Итак, я подумал, что скрипту PowerShell нужны только модель принтера, IP-адрес и имя. Мы можем найти IP-адрес, так что если пользователь сможет выбрать модель и ввести имя, мы закончили. Это сэкономит мне два звонка в неделю.
Основы графического интерфейса PowerShell
Прежде чем мы начнем создавать форму, важно знать, что скрипт PowerShell выполняется последовательно. Вы определяете свою форму и отображаете ее. Но любой код после того, как вы отобразили форму, не будет выполнен, пока вы не закроете форму.
Это то, что потребовало у меня 30 минут, чтобы разобраться… Я просто думал, что могу показать форму и обрабатывать ввод ниже неё в PowerShell.
Создание формы
Мы начинаем с пустой формы. Сначала добавляем .Net Windows. Forms. Также нам нужно определить размер формы (ширина, высота), заголовок и цвет фона. Просто скопируйте и вставьте код ниже в PowerShell ISE и нажмите «Запуск».
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()Вы увидите простую форму, как эта ниже:
Добавление элементов в вашу форму
На нашей форме мы можем добавлять элементы. Они могут использоваться для отображения информации и сбора пользовательских данных. Место ввода основывается на точках/пикселях от левой и верхней стороны. Таким образом, положение 20,50 означает 20 пикселей от левой стороны и 50 точек от верхней стороны.
У нас есть следующие элементы, которые мы можем использовать в наших формах:
- Текстовое поле (для получения ввода от пользователя)
- Ярлык
- Кнопка
- PictureBox
- CheckBox
- Комбинированный список (выпадающий список)
- ListView
- ListBox
- RadioButton
- Панель
- Групповая рамка (Для объединения элементов)
- ProgressBar
- DataGridView
Итак, давайте создадим несколько элементов на нашей форме. Добавьте код ниже в ваш скрипт. Убедитесь, что ShowDialog находится в конце вашего скрипта.
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()
Что вы здесь видите, так это то, что каждый элемент создан. Все это простые текстовые или меточные элементы. Вы можете определить ширину и высоту каждого элемента, но если ваш контент длиннее, чем элемент, он будет отображаться только частично. Поэтому, установив Autosize в значение true, вы обеспечиваете того, что пользователь сможет прочитать всю метку.
Каждому элементу присваивается позиция: первая цифра - это количество пикселей от левой стороны, вторая - количество пикселей от верхней.
Результат будет выглядеть так:
Используя выпадающий список
Нашему пользователю нужно выбрать производителя принтера, мы используем только две марки, так что радиокнопка тоже сработает. Мы используем универсальные драйверы печати, поэтому мне не нужно знать конкретную модель принтера.
$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'Код выше, я думаю, довольно понятен, мы создаем выпадающий список и добавляем элементы с помощью одного цикла foreach в список. Снова мы также определяем позицию, и я установил минимальную ширину для элемента.
Вы можете установить значение по умолчанию для вашего выпадающего списка, выбрав индекс. Убедитесь, что вы добавили переменную элемента в $LocalPrinterForm.controls.AddRange, в противном случае она не будет отображаться.
Добавление кнопок
Мы также собираемся добавить несколько кнопок в нашу форму. Кнопка может иметь стандартное действие, такое как (
ОК, Отмена, Прерывание, Повтор, Игнорировать, Да или Нет), или вы можете назначить ей пользовательскую функцию.
Кнопки, которые мы собираемся добавить, это "Отмена" и "Добавить принтер". "Отмена" просто закроет форму и ничего больше не сделает, в то время как "Добавить принтер" запустит нашу логику для добавления принтера. Добавьте кнопки с помощью кода ниже, снова убедитесь, что вы добавили переменные кнопок в $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)Теперь многие примеры на других сайтах игнорируют результаты диалога с помощью [void]$form.ShowDialog(). Однако возвращаемое значение диалога следует использовать, чтобы узнать, как пользователь закрыл форму.
Итак, мы собираемся изменить команду ShowDialog.
$result = $LocalPrinterForm.ShowDialog()Таким образом, мы можем проверить, нажал ли пользователь кнопку "Отмена" или любую другую кнопку по умолчанию в форме.
if ($result -eq [System.Windows.Forms.DialogResult]::Cancel)
{
write-output 'User pressed cancel'
}Добавление пользовательской функции к кнопке
Когда пользователь нажимает Добавить принтер, должен быть выполнен пользовательский скрипт. Мы можем создать функцию с нашей логикой и назначить эту функцию кнопке с помощью следующей команды.
$AddPrinterBtn.Add_Click({ AddPrinter })И наша функция
function AddPrinter {
# ADDING PRINTER LOGIC GOES HERE
}Результат
Итак, после того как мы добавили все элементы, и я добавил несколько дополнительных меток, наша финальная форма GUI PowerShell выглядит так:
Теперь нам только нужно завершить наш сценарий, чтобы добавить принтер.
Добавление логики
Итак, с нашей формой на месте мы можем начать добавлять логику. Во время выполнения вашего скрипта вы, возможно, захотите изменить текст, показать или удалить поля или выполнить определенное действие. Давайте сначала начнем с описания нашего скрипта PowerShell.
#------------[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'
}В ваших функциях или разделе скрипта вы можете захотеть дать пользователю некоторую обратную связь или использовать данные из полей ввода.
Чтение и изменение элементов графического интерфейса PowerShell
Чтение данных из текстового поля довольно простое. В нашем примере мы хотим использовать имя, введенное пользователями, и проверить, существует ли уже имя принтера. Если имя существует, мы хотим показать ошибку.
if (Get-Printer -Name $printerName.text) {
$PrinterStatus.ForeColor = "#D0021B"
$PrinterStatus.Text = 'Printer name already exists.'
}В приведенном выше примере мы проверяем данное имя принтера, если оно существует, мы изменяем цвет шрифта метки "СтатусПринтера" на красный и изменяем текст метки, чтобы показать сообщение пользователям.
Нет необходимости обновлять форму или ввод, вы можете просто менять текст по мере необходимости.
Показ и скрытие полей
Перед загрузкой формы я проверяю, доступен ли принтер по ожидаемому сетевому адресу. Если нет, то пользователю не нужно продолжать. Поэтому, когда я инициализирую элементы формы, я скрываю некоторые элементы.
Если у нас есть соединение, то я показываю элементы или изменяю метку кнопок, например. Скрытие и отображение элементов в GUI PowerShell можно сделать с помощью следующего кода:
$PrinterNameLabel.Visible = $false # or $true of course 😉Онлайн редактор графического интерфейса PowerShell PoshGUI
Создание более крупной или сложной формы только из командной строки может быть немного сложным. Вам придется расположить все элементы в правильном месте, создать весь необходимый код. К счастью, существует онлайн-редактор GUI PowerShell PoshGUI.
Это отличный инструмент для создания вашего начального макета формы. Вы можете скачать файл PS1 и продолжить работу над вашей формой в вашем любимом редакторе.
Заключение
Я надеюсь, что эта статья поможет вам начать создание вашей первой формы PowerShell. Хотя PowerShell на самом деле не предназначен для создания форм, это может быть отличным способом сделать ваш скрипт более удобным для вашего пользователя или коллег.
Подпишитесь на рассылку, чтобы получать последние статьи о PowerShell или Office 365 на ваш почтовый ящик.
Полный код выглядит следующим образом:
#---------------------------------------------------------[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()Вы также можете ознакомиться со следующими статьями:
- Добавление принтеров и портов принтеров с помощью PowerShell
- Перенос домашних папок в OneDrive с помощью PowerShell
- Начало работы с Microsoft Flow



