Powershell может использовать классы .NET. Один из таких классов, NotifyIcon, может выводить привычный баннер в нижней части экрана. В этой статье будет рассмотрен способ создания такого баннера с вашим текстом, а так же разные варианты его использования.
В итоге этой статьи можно будет создать баннер с похожим уведомлением:
Оно так же будет оставаться в панели уведомлений.
Где можно использовать
Обычно такие баннеры можно видеть при обновлении, освобождении места, почтовые уведомления и т.д. Учитывая, что Powershell хорошо интегрирован с серверными ролями - такой баннер может отлично подойти под разные мониторинги сервисов. Например вы можете проверять заблокированных пользователей и выводить их если это произошло.
Сложность, с которой я столкнулся, связана с удаленным выполнением такого скрипта средствами Powershell (WinRM). Вывод каких либо окон у удаленного пользователя, используя Powershell и .NET, задача не такая простая.
Самый простой и интересный способ выводить такие окна массово - использование REST. В предыдущих статьях был пример создания такого сервиса на Pode.
Если баннер у вас не выводится - возможно у вас отключены уведомления. В моем случае причиной оказалось следующая настройка, но она не единственная которая может повлиять на вывод:
Создание баннера
Первым делом мы должны импортировать класс "System.Windows.Forms" через Add-Type. Без этой команды у вас может появиться ошибка. Далее мы создаем объект (копию/инстанс) из класса 'NotifyIcon':
Add-Type -AssemblyName System.Windows.Forms
$banner = New-Object System.Windows.Forms.NotifyIcon
После создания объекта баннера - нам нужно указать у него 4 свойства:
- Icon - иконка, которая будет отображаться вместе с баннером;
- BalloonTipIcon - уровень уведомлений: ошибка (Error), предупреждение (Warning), уведомление (Info) или None;
- BalloonTipText - текст, который будет выводиться в баннере;
- BalloonTipTitle - заголовок в баннере;
- Visible - отображение;
Иконку можно указать 2-мя способами: указав прямой путь до файла с расширением '*.ico' или скопировать ее у одного из приложений:
# импортируем класс для иконки
Add-Type -AssemblyName System.Drawing
# 1) Указываем путь до существующей картинки формата '.ico'
$ico = 'G:\banner.ico'
$banner.Icon = New-Object System.Drawing.Icon($ico)
# 2) или вытаскиваем иконку с существующего приложения
# (в данном примере Powershell)
$ico = "$env:WINDIR\system32\WindowsPowerShell\v1.0\powershell.exe"
$banner.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($ico)
Так же объявим остальные свойства описанные выше:
# Уровень сообщения
$banner.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]'Info'
$banner.BalloonTipTitle = 'Заголовок'
$banner.BalloonTipText = 'Наше сообщение'
# будет ли выводится баннер
$banner.Visible = $true
Для вызова баннера используется метод "ShowBalloonTip", в котором нужно указать длительность отображения баннера. В примере ниже стоит 200 миллисекунд. По истечении этого времени баннер исчезнет:
$banner.ShowBalloonTip(200)
Скрытие иконки
Одна из проблем кода выше заключается в иконке, которая выводятся вместе с баннером. Она не исчезает после вывода баннера. Ее так же нельзя скрыть по нажатию. Единственный способ закрыть их - завершить сессию Powershell в которой она была создана (закрыв окно).
Правильным способом скрытием иконки будет вызов метода 'dispose', который предназначен именно для этого. Этот метод будет вызываться при событии (тригере) в виде двойного нажатия на иконку в трейе.
Событие создается с помощью 'Register-ObjectEvent'. В нем мы должны указать объект (NotifyIcon), событие на которое необходима реакция (двойное нажатие мышки) и название. При нажатии мы выполняем блок прописанный в 'Action':
Add-Type -AssemblyName System.Windows.Forms
if (-NOT $banner) {
$banner = New-Object System.Windows.Forms.NotifyIcon
[void](Register-ObjectEvent -InputObject $banner -EventName MouseDoubleClick -SourceIdentifier BannerClick -Action {
# При двойном клике удаляем все созданные объекты
$banner.dispose()
Unregister-Event -SourceIdentifier BannerClick
Remove-Job -Name BannerClick
Remove-Variable -Name banner
})
}
# ...
# Остальной код
# ...
Альтернативно можно закрыть баннер вызвав метод "$banner.dispose()" в конце скрипта, но в этом случае иконка вместе с сообщением пропадет полностью после указанного времени.
Готовая команда
Целый скрипт находится на GitHub. Его можно использовать разными способами. Вы можете поместить его в папку с модулями или импортировать:
Import-Module "F:\powershell\banner\Invoke-Banner.ps1"
Варианты использования:
# информационное сообщение
Invoke-Banner -Title 'Заголовок' -Text 'Простое информационное сообщение'
# вывод ошибки
Invoke-Banner -Title 'Заголовок' -Text 'Ошибка' -Type Error
# указываем длительность работы баннера и свою иконку
Invoke-Banner -Title 'Заголовок' -Text 'Простое информационное сообщение' -Type Error -Duration 700 -Icon "G:\banner.ico"
Дополнительные возможности
Сам класс 'NotifyIcon' предназначен не только для вывода баннера. Основная работа класса связана с взаимодействием с иконкой. К этой иконке можно привязать контекстное меню и вызвать какие-то другие функции. Для примера возьмем команды, которые выведут список процессов в GUI:
Get-Process | Out-GridView
Мы можем создать контекстное меню с вызовом этой команды. Для этого добавим следующий код к предыдущему скрипту:
$banner.BalloonTipText = $Text
# --- предыдущий код ---
# создаем объект с меню
$menu = New-Object System.Windows.Forms.ContextMenuStrip
# создаем кнопку с названием
$exit = $menu.Items.Add('Вывод процессов')
# привязываем событие, которое сработает при нажатии на кнопку
$exit.add_Click({
Get-Process | Out-GridView
# Закрытие работы приложения
$appContext.ExitThread()
})
# привязываем меню к кнопке
$banner.ContextMenuStrip = $menu
$banner.Visible = $true
$banner.ShowBalloonTip($Duration)
# запускаем как приложение
$appContext = New-Object System.Windows.Forms.ApplicationContext
[void][System.Windows.Forms.Application]::Run($appContext)
}
Можно установить другие события скрывающие иконку или выполняющие определенные действия. Все из них можно увидеть следующим образом:
Add-Type -AssemblyName System.Windows.Forms
$banner = New-Object System.Windows.Forms.NotifyIcon
$banner | Get-Member
NotifyAction это класс .NET и вы можете почитать документацию для поиска других применений.
Использование удаленно
Вызывать подобный скрипт удаленно, используя WinRM (PSRemoting), не получится. Это связано с разными сеансами, в которых работает сам пользователи и Powershell. Если вы ищете простой вариант вывода сообщений удаленно - вам может больше подойти статься по программе msg и ее использование в Powershell, которая работает в этом случае исправно.
Можно использовать программу 'psexec' от SysInternals, но это так же достаточно сложный процесс. В первую очередь мы должны узнать номер сессии в которой работает пользователь. Самый простой способ сделать это - использовать 'quser':
# локально
quser
# удалено
quser /server:localhost
О quser и другие способах подключения описывалось в предыдущих статьях. Удаленный вариант, с quser, сработает только в том случае, если вы являетесь привилегированным пользователем для подключения к удаленному компьютеру. Использовать эту команду с WinRM, скорее всего, приведет к проблемам с кодировками. Из результата работы команды вам нужно значение с ID.
Имея этот идентификатор и скаченную утилиту 'psexec', вы сможете запустить командлет следующим образом:
$module_path = "C:\banner.ps1"
$command = "Invoke-Banner -Title Hello -Text World"
$session_id = 2
.\PsExec64.exe -s -i $session_id \\192.168.2.111 -u domain\администратор -p пароль /accepteula cmd /c "powershell -command Import-Module $module_path; $command"
Пример, который я сделал для видео:
...
Подписывайтесь на наш Telegram канал
Теги: #powershell