Создание графических интерфейсов PowerShell WPF: функции, ввод и результаты

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

Not a reader? Watch this related video.

Установите проверку конечного пользователя при сбросе паролей на службе технической поддержки. Снизьте уязвимость к социальной инженерии с помощью Specops Secure Service Desk. Свяжитесь с нами для демонстрации!

PowerShell может использовать и раскрывать функциональность и особенности .NET. В результате можно создавать графические пользовательские интерфейсы для скриптов, которые вы создаете. Создание графических интерфейсов PowerShell может показаться сложным, особенно если вы новичок.

Но если у вас есть базовый опыт написания скриптов на PowerShell, то нет причин, почему бы вам не научиться и не применить практику создания графического интерфейса для ваших скриптов.

В этом посте вы узнаете, как создавать графический интерфейс PowerShell с использованием Windows Presentation Framework (WPF).

Предварительные требования

Прежде чем приступить, убедитесь, что у вас есть следующие требования:

  1. Visual Studio 2017 или более поздняя версия – Вы будете использовать это для создания графического пользовательского интерфейса с использованием WPF. Вы можете скачать бесплатную/сообщественную версию.
  2. A script editor – I use Visual Studio Code, but you can also use another text editor of your choice. Some other options are Notepad++ and the built-in PowerShell ISE
  3. A Windows 10 computer with Windows PowerShell 5.1.

Создание сценария

В этом посте вы создадите простой сценарий с именем Main.ps1. В сценарии вы напишете код, который будет извлекать информацию о диске с локальной или удаленной системы, выполняя запрос к классу WMI Win32_LogicalDisk.

Вам понадобится сценарий, чтобы обернуть его в GUI. Я выбрал сценарий, который позволяет вам указать имя компьютера и запрашивать информацию о диске. Это, конечно, не обязательно для создания GUI. Используйте техники, которые вы изучите в этом посте, чтобы адаптировать свои GUI к вашим собственным сценариям.

В качестве примера сценария я создам функцию, выполняющую следующие действия:

  1. Принять ввод имени компьютера для запроса
  2. Выполнить запрос к компьютеру и сохранить информацию о фиксированных дисках в переменную
  3. Вернуть результаты

Написание функции

Ниже приведена функция, которую вы будете использовать в этом проекте, названная Get-FixedDisk. Цель этого проекта – получить информацию о неразъемных или фиксированных дисках на целевой машине.

Хотя этот код может использоваться как есть, создание GUI будет полезным, если вы хотите выполнить быстрый запрос, не используя функцию dot source и вручную вводя команды каждый раз.

Function Get-FixedDisk {
    [CmdletBinding()]
    # Этот блок param() указывает начало объявления параметров
    param (
        <# 
            Этот параметр принимает имя целевого компьютера.
            Он также установлен как обязательный, чтобы функция не выполнялась без указания значения.
        #>
        [Parameter(Mandatory)]
        [string]$Computer
    )
    <#
        Команда запроса WMI, которая получает список всех логических дисков и сохраняет результаты в переменной с именем $DiskInfo
    #>
    $DiskInfo = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Filter 'DriveType=3'
   $DiskInfo
}

Вы видите, что я добавил блок param() в код. Это для того, чтобы указать функции принимать входные данные на основе указанного типа данных.

В этом примере я добавил параметр Computer, который принимает строковое значение. Также, добавив атрибут параметра Mandatory, это гарантирует, что функция не будет выполняться, если параметр Computer не указан во время выполнения.

Далее, строка 18 показывает фактическую команду запроса WMI, которая получает список всех логических дисков и сохраняет результаты в переменной $DiskInfo. Я также добавил фильтр, чтобы получить только диски с DriveType=3. Этот фильтр гарантирует, что отображается только информация о локальных фиксированных дисках.

Импортирование кода (Dot Sourcing)

На этом этапе у вас уже есть рабочий скрипт, и вы готовы протестировать его. Но прежде чем протестировать скрипт, вам необходимо импортировать код в сеанс PowerShell. Один из способов загрузить код в сеанс PowerShell – это загрузка через точку.

Чтобы загрузить скрипт через точку, наберите точку (.) и пробел перед путем к скрипту. Если скрипт находится в папке C:\PoshGUI-sample, вы можете загрузить его следующим образом.

PS C:\PoshGUI-sample> . .\Main.ps1

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

PS C:>. C:\PoshGUI-sample\Main.ps1

Теперь, когда мы импортировали код в память, мы можем продолжить с тестированием созданной нами функции. В приведенном ниже примере показано, что функция Get-FixedDisk используется для запроса компьютера poshLabExc.

PS51> Get-FixedDisk -Computer poshLabExc

DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 53037772800
Size         : 135838822400
VolumeName   : Windows

DeviceID     : D:
DriveType    : 3
ProviderName :
FreeSpace    : 14872641536
Size         : 17178750976
VolumeName   : Temporary Storage

DeviceID     : E:
DriveType    : 3
ProviderName :
FreeSpace    : 488202240
Size         : 524283904
VolumeName   : System Reserved

Создание графического интерфейса PowerShell

На данном этапе вы создали файл скрипта с именем Main.ps1 и внутри скрипта создали функцию Get-FixedDisk. Вы также смогли протестировать и подтвердить, что функция работает.

Теперь, когда вы знаете, что скрипт работает, вы можете начать создавать графический интерфейс пользователя.

Проектирование формы графического интерфейса PowerShell

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

  • a text box where the computer name can be entered
  • a button to execute the function
  • a text box where we can display the results

Затем вы можете начать его создавать!

Чтобы начать создание графического интерфейса, откройте Visual Studio и создайте новый проект.

Как только открыта Visual Studio, нажмите на Файл (1) –> Новый (2) –> Проект (3).

Creating a new Visual Studio project

В окне Новый проект выберите Visual C# (1), выберите WPF App (.NET Framework) (2), измените имя на PoshGUI-sample (3) и нажмите ОК.

Choosing a Visual Studio project

После создания проекта будет представлена пустая форма с именем MainWindow.xaml.

Visual Studio MainWindow.xaml

Теперь вам нужно отформатировать эту форму под наши требования. Ниже приведены элементы управления и формат, которые вам нужно добавить.

  • Окно
    • Заголовок: Информация о диске
    • Высота: 326
    • Ширина: 403
  • Элементы управления (4)
    • Метка
      • Содержание: “Имя компьютера:”
      • Отступ: 10, 10, 0, 0
    • Текстовое поле
      • Имя: txtComputer
      • Текст: “”
      • Высота: 23
      • Ширина: 174
    • Кнопка
      • Имя: btnQuery
      • Содержание: Запрос
      • Отступ: 0, 13, 12, 0
    • Текстовое поле
      • Имя: txtResults
      • Текст: “”
      • Только чтение: True
      • Отступ: 10, 60, 0, 0
      • Высота: 225
      • Ширина: 373

Финальный вид формы должен быть похож на то, что показано на изображении ниже. Вы можете переставить компоненты вашего окна по-разному. Будьте креативны!

PowerShell GUI Template

Объединение скрипта и графического интерфейса PowerShell

Как только вы будете довольны своим дизайном, вы можете начать интеграцию с скриптом.

PowerShell не может отображать формы нативно. Чтобы отобразить форму, необходимо добавить строку кода в самый верх нашего скрипта для поддержки отображения формы WPF.

Add-Type -AssemblyName PresentationFramework

Затем добавьте код для выполнения следующих действий:

  1. Импорт и чтение кода XAML формы.
  2. Динамическое создание переменных, присвоенных каждому именованному элементу управления
  3. Отображение формы

Ниже приведен обновленный код внутри вашего скрипта.

Примечание: Убедитесь, что вы изменяете строку $xamlFile и указываете полный путь к файлу MainWindow.xaml.

Add-Type -AssemblyName PresentationFramework

Function Get-FixedDisk {
    [CmdletBinding()]
    # Этот блок param() указывает начало объявления параметров
    param (
        <# 
            Этот параметр принимает имя целевого компьютера.
            Он также установлен как обязательный, чтобы функция не выполнялась без указания значения.
        #>
        [Parameter(Mandatory)]
        [string]$Computer
    )
    <#
        Команда запроса WMI, которая получает список всех логических дисков и сохраняет результаты в переменной с именем $DiskInfo
    #>
    $DiskInfo = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Filter 'DriveType=3'
    $DiskInfo
}

# где находится файл XAML?
$xamlFile = "C:\PoshGUI-sample\MainWindow.xaml"

#создать окно
$inputXML = Get-Content $xamlFile -Raw
$inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window'
[XML]$XAML = $inputXML

#Прочитать XAML
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
try {
    $window = [Windows.Markup.XamlReader]::Load( $reader )
} catch {
    Write-Warning $_.Exception
    throw
}

# Создать переменные на основе имен элементов управления формы.
# Переменная будет названа как 'var_<имя элемента управления>'

$xaml.SelectNodes("//*[@Name]") | ForEach-Object {
    #"пытаюсь элемент $($_.Name)"
    try {
        Set-Variable -Name "var_$($_.Name)" -Value $window.FindName($_.Name) -ErrorAction Stop
    } catch {
        throw
    }
}
Get-Variable var_*

$Null = $window.ShowDialog()

Примечание: $Null = $window.ShowDialog() всегда должна быть последней строкой кода в вашем скрипте.

Когда вы запускаете этот код, выполнив скрипт Main.ps1, вы должны увидеть пример вывода ниже.

PowerShell GUI variable and field mappings

Как видите, трем именованным элементам управления были присвоены их переменные. Эти имена переменных будут ссылаться позже в скрипте, когда мы добавим код логики управления элементами.

  • var_btnQuery
  • var_btnComputer
  • var_txtResults

Помните, что на данный момент скрипт может только отображать форму, но элементы управления бесполезны, так как вы еще не добавили код.

Добавление кода события нажатия кнопки

Теперь, когда вы успешно изменили скрипт для импорта и отображения GUI, начните добавлять код в элементы управления для получения и отображения данных о диске.

В этом проекте действие будет назначено только кнопке btnQuery. Другие элементы управления будут служить только в качестве элементов ввода и элементов управления выводом/отображением. Это означает, что нам нужно добавить код события click к btnQuery.

Чтобы добавить действие click к btnQuery, назначьте приведенный ниже код соответствующему имени переменной $var_btnQuery. Скопируйте приведенный ниже код и вставьте его между ссылками на код Get-Variable var_* и $Null = $window.ShowDialog() в скрипте.

$var_btnQuery.Add_Click( {
   #Очистить поле результатов
   $var_txtResults.Text = ""
       if ($result = Get-FixedDisk -Computer $var_txtComputer.Text) {
           foreach ($item in $result) {
               $var_txtResults.Text = $var_txtResults.Text + "DeviceID: $($item.DeviceID)`n"
               $var_txtResults.Text = $var_txtResults.Text + "VolumeName: $($item.VolumeName)`n"
               $var_txtResults.Text = $var_txtResults.Text + "FreeSpace: $($item.FreeSpace)`n"
               $var_txtResults.Text = $var_txtResults.Text + "Size: $($item.Size)`n`n"
           }
       }       
   })

$var_txtComputer.Text = $env:COMPUTERNAME

Тестирование завершенного интерфейса PowerShell GUI

Со всеми частями, вот завершенный код для нашего скрипта, который включает функцию и интерфейс PowerShell GUI, который мы разработали.

Add-Type -AssemblyName PresentationFramework

Function Get-FixedDisk {
    [CmdletBinding()]
    # Этот блок param() указывает на начало объявления параметров
    param (
        <# 
            Этот параметр принимает имя целевого компьютера.
            Он также установлен как обязательный, чтобы функция не выполнялась без указания значения.
        #>
        [Parameter(Mandatory)]
        [string]$Computer
    )
    <#
        Команда запроса WMI, которая получает список всех логических дисков и сохраняет результаты в переменную с именем $DiskInfo
    #>
    $DiskInfo = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Filter 'DriveType=3'
   $DiskInfo
}

#где находится файл XAML?
$xamlFile = "C:\Users\june\source\repos\PoshGUI-sample\PoshGUI-sample\MainWindow.xaml"

#создать окно
$inputXML = Get-Content $xamlFile -Raw
$inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window'
[xml]$XAML = $inputXML
#Читать XAML

$reader = (New-Object System.Xml.XmlNodeReader $xaml)
try {
    $window = [Windows.Markup.XamlReader]::Load( $reader )
}
catch {
    Write-Warning $_.Exception
    throw
}

#Создать переменные на основе имен элементов управления формой.
#Переменная будет названа 'var_<control name>'

$xaml.SelectNodes("//*[@Name]") | ForEach-Object {
    #"попытка элемента $($_.Name)";
    try {
        Set-Variable -Name "var_$($_.Name)" -Value $window.FindName($_.Name) -ErrorAction Stop
    } catch {
        throw
   }
}

Get-Variable var_*

$var_btnQuery.Add_Click( {
   #очистить поле результатов
   $var_txtResults.Text = ""
       if ($result = Get-FixedDisk -Computer $var_txtComputer.Text) {
           foreach ($item in $result) {
               $var_txtResults.Text = $var_txtResults.Text + "DeviceID: $($item.DeviceID)`n"
               $var_txtResults.Text = $var_txtResults.Text + "VolumeName: $($item.VolumeName)`n"
               $var_txtResults.Text = $var_txtResults.Text + "FreeSpace: $($item.FreeSpace)`n"
               $var_txtResults.Text = $var_txtResults.Text + "Size: $($item.Size)`n`n"
           }
       }       
   })

$var_txtComputer.Text = $env:COMPUTERNAME
$Null = $window.ShowDialog()

Как видно ниже, после вызова скрипта в PowerShell появляются окна PowerShell GUI. Затем можно ввести допустимое имя компьютера для проверки функциональности.

PowerShell GUI Example Result

Безопасная проверка вызывающих с помощью методов аутентификации, исключающих возможность подделки пользователей. Блокируйте хакеров службы поддержки с Specops Secure Service Desk. Попробуйте бесплатно!

Сводка

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

Это всего лишь комбинация базового сценария и графического интерфейса. Множество улучшений можно сделать, таких как:

  • Форматирование размера и свободного места для отображения в виде значений GB.
  • Изменение имени отображаемого свойства.
  • Использование GridView вместо TextBox для отображения результатов.
  • Добавление кнопки импорта для перебора списка серверов из файла CSV.

Это зависит от вас, как изменить и добавить функциональность в соответствии с вашими требованиями.

Дополнительное чтение

Source:
https://adamtheautomator.com/powershell-gui/