Menu Formulário do PowerShell: Acesso Rápido aos Seus Scripts

É hora do projeto de fim de semana novamente e hoje você aprenderá como construir um menu leve de formulário PowerShell na bandeja do sistema, onde você pode iniciar rapidamente e facilmente seus scripts PowerShell mais desejados. Você pode ver abaixo o resultado final.

Launching PowerShell scripts via a system tray menu icon

Neste artigo, você aprenderá a construir seu próprio menu GUI do PowerShell dividindo o processo passo a passo.

Requisitos de Ambiente e Conhecimento

Antes de começar, certifique-se de atender aos seguintes requisitos mínimos:

Para este projeto, a boa notícia é que você realmente não precisará depender do Visual Studio, PoshGUI ou qualquer outra ferramenta de desenvolvimento de IU, pois os componentes principais em que este projeto dependerá são os seguintes:

  • NotifyIcon – Isso representará nosso ícone personalizável na bandeja do sistema para o usuário interagir.
  • Menu de Contexto – Recipiente para quando o usuário clica com o botão direito no ícone da bandeja.
  • Item do Menu – Objetos individuais para cada opção dentro do menu de clique com o botão direito.

Abra o seu editor de script PowerShell favorito e vamos começar!

Para este projeto, você vai construir três funções: duas funções para mostrar/ocultar o console e proporcionar uma experiência mais limpa ao usuário, e uma para adicionar itens ao menu da bandeja do sistema. Essas funções servirão como uma base para uso futuro, facilitando sua vida, como você aprenderá um pouco mais tarde neste artigo.

Mostrar/Ocultar Janela do Console

A menos que esteja oculto, quando você inicia um script PowerShell, a conhecida janela de console do PowerShell aparecerá. Como os itens de menu no formulário PowerShell que você criará irão iniciar scripts, você deve garantir que o console não apareça. Você só quer que ele execute.

Ao executar um script, você pode alternar a exibição da janela do console PowerShell ou não usando um pouco de .NET.

Primeiro adicione o tipo Window .NET na sessão atual. Para fazer isso, você usará algum C# como verá abaixo. Os dois métodos que você precisa carregar no contexto são GetConsoleWindow e ShowWindow. Ao carregar esses DLLs na memória, você está expondo certas partes da API, o que permite usá-las no contexto do seu script do PowerShell:

 #Carregar dlls no contexto da sessão do console atual
 Add-Type -Name Window -Namespace Console -MemberDefinition '
    [DllImport("Kernel32.dll")]
    public static extern IntPtr GetConsoleWindow();
 
    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);

Crie duas funções usando as DLLs carregadas acima usando o método GetConsoleWindow() e ShowWindow() como mostrado abaixo.

 function Start-ShowConsole {
    $PSConsole = [Console.Window]::GetConsoleWindow()
    [Console.Window]::ShowWindow($PSConsole, 5)
 }
 
 function Start-HideConsole {
    $PSConsole = [Console.Window]::GetConsoleWindow()
    [Console.Window]::ShowWindow($PSConsole, 0)
 }

Com essas duas funções, você criou uma maneira de mostrar ou ocultar a janela do console quando quiser.

Nota: Se você deseja ver a saída dos scripts executados via menu, pode usar transcrições do PowerShell ou outras características de registro baseadas em texto. Isso permite manter o controle em vez de apenas executar a sessão do PowerShell com o parâmetro WindowStyle para ocultar.

Agora comece a construir o código do script chamando Start-HideConsole. Quando o script do formulário do PowerShell executar com base no menu, isso garantirá que a janela do console do PowerShell não apareça.

<# 
	Inicialização de funções e carregamento de objetos na memória
	Exibir uma barra de carregamento baseada em texto ou Write-Progress no host
#>
 
Start-HideConsole
 
<# 
	Código para exibir seu ícone de formulário/systray
	Isto manterá o console aqui até ser fechado
 #>

Criar Opções de Menu

Agora é hora de criar as opções de menu. Garantindo que você possa facilmente criar novas opções posteriormente, crie outra função desta vez chamada New-MenuItem. Quando você chama esta função, ela criará um novo objeto MenuItem .NET que você pode adicionar ao menu posteriormente.

Cada opção de menu iniciará outro script ou sairá do lançador. Para acomodar essa funcionalidade, a função New-MenuItem tem três parâmetros:

  • Texto – O rótulo que o usuário clicará
  • CaminhoDoMeuScript – O caminho para o script do PowerShell a ser executado
  • SairApenas – A opção de sair do lançador.

Adicione o trecho de função abaixo ao script do menu.

 function New-MenuItem{
     param(
         [string]
         $Text = "Placeholder Text",
 
         $MyScriptPath,
         
         [switch]
         $ExitOnly = $false
     )

Continuando a construir a função New-MenuItem, crie um objeto MenuItem atribuindo-o a uma variável.

 #Inicialização
 $MenuItem = New-Object System.Windows.Forms.MenuItem

Em seguida, atribua o rótulo de texto ao item de menu.

 # Aplicar o texto desejado
 if($Text) {
 	$MenuItem.Text = $Text
 }

Agora adicione uma propriedade personalizada ao MenuItem chamada CaminhoDoMeuScript. Este caminho será chamado quando o item for clicado no menu.

 #Aplicar lógica de evento de clique
 if($MyScriptPath -and !$ExitOnly){
 	$MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty

Adicione um evento de clique ao MenuItem que inicia o script desejado. Start-Process fornece uma maneira limpa de fazer isso dentro de um bloco try/catch para garantir que quaisquer erros ao iniciar o script (como o PowerShell não estar disponível ou o script não existir no caminho fornecido) caiam no bloco catch.

   $MenuItem.Add_Click({
        try{
            $MyScriptPath = $This.MyScriptPath #Usado para encontrar o caminho correto durante o evento de clique
            
            if(Test-Path $MyScriptPath){
                Start-Process -FilePath "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ArgumentList "-NoProfile -NoLogo -ExecutionPolicy Bypass -File `"$MyScriptPath`"" -ErrorAction Stop
            } else {
                throw "Could not find at path: $MyScriptPath"
            }
        } catch {
          $Text = $This.Text
          [System.Windows.Forms.MessageBox]::Show("Failed to launch $Text`n`n$_") > $null
        }
  })

Adicione a lógica restante para fornecer uma condição de saída para o iniciador, seguida pelo retorno do seu novo MenuItem para ser atribuído a outra variável em tempo de execução.

    #Fornecer uma maneira de sair do iniciador
    if($ExitOnly -and !$MyScriptPath){
        $MenuItem.Add_Click({
            $Form.Close()
    
            #Lidar com quaisquer processos pendurados
            Stop-Process $PID
        })
    }
 
 	 #Retornar nosso novo MenuItem
    $MenuItem
 }

Agora você deve ter a função New-MenuItem criada! A função final deve se parecer com isto:

 function New-MenuItem{
     param(
         [string]
         $Text = "Placeholder Text",
 
         $MyScriptPath,
         
         [switch]
         $ExitOnly = $false
     )
 
     #Inicialização
     $MenuItem = New-Object System.Windows.Forms.MenuItem
 
     #Aplicar texto desejado
     if($Text){
         $MenuItem.Text = $Text
     }
 
     #Aplicar lógica de evento de clique
     if($MyScriptPath -and !$ExitOnly){
         $MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty
     }
 
     $MenuItem.Add_Click({
             try{
                 $MyScriptPath = $This.MyScriptPath #Usado para encontrar o caminho adequado durante o evento de clique
             
                 if(Test-Path $MyScriptPath){
                     Start-Process -FilePath "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ArgumentList "-NoProfile -NoLogo -ExecutionPolicy Bypass -File `"$MyScriptPath`"" -ErrorAction Stop
                 } else {
                     throw "Could not find at path: $MyScriptPath"
                 }
             } catch {
                 $Text = $This.Text
                 [System.Windows.Forms.MessageBox]::Show("Failed to launch $Text`n`n$_") > $null
             }
         })
 
     #Fornecer um modo de sair do iniciador
     if($ExitOnly -and !$MyScriptPath){
         $MenuItem.Add_Click({
                 $Form.Close()
    
                 #Lidar com quaisquer processos pendurados
                 Stop-Process $PID
             })
     }
 
     #Retornar nosso novo MenuItem
     $MenuItem
 }

Teste a função New-MenuItem copiando e colando o código acima no seu console PowerShell e executando a função fornecendo alguns valores de parâmetros falsos. Você verá que um objeto MenuItem .NET é retornado.

 PS51> (New-MenuItem -Text "Test" -MyScriptPath "C:\test.ps1").GetType()
 
 IsPublic IsSerial Name                                     BaseType
 -------- -------- ----                                     --------
 True     False    MenuItem                                 System.Windows.Forms.Menu

Criando um Formulário de Inicialização

Quer mais dicas como esta? Confira meu blog pessoal PowerShell em: https://nkasco.com/FriendsOfATA

Agora que você pode criar facilmente novos itens de menu, é hora de criar um iniciador de bandeja do sistema que irá exibir o menu.

Crie um objeto de formulário básico para adicionar componentes. Isso não precisa ser nada sofisticado, pois será oculto para o usuário final e manterá o console em execução em segundo plano também.

 #Criar Formulário para servir como um contêiner para nossos componentes
 $Form = New-Object System.Windows.Forms.Form
 ​
 #Configurar nosso formulário para ficar oculto
 $Form.BackColor = "Magenta" #Combine esta cor com a propriedade TransparencyKey para transparência no seu formulário
 $Form.TransparencyKey = "Magenta"
 $Form.ShowInTaskbar = $false
 $Form.FormBorderStyle = "None"

A seguir, crie o ícone que aparecerá na bandeja do sistema. Abaixo, escolhi usar o ícone do PowerShell. Durante a execução, o código abaixo cria um ícone real na bandeja do sistema. Este ícone pode ser personalizado conforme desejado, configurando a variável SystrayIcon para o ícone desejado.

Consulte a documentação para a classe System.Drawing.Icon para ver outros métodos nos quais você pode carregar um ícone na memória.

 #Inicialize/configure os componentes necessários
 $SystrayLauncher = New-Object System.Windows.Forms.NotifyIcon
 $SystrayIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe")
 $SystrayLauncher.Icon = $SystrayIcon
 $SystrayLauncher.Text = "PowerShell Launcher"
 $SystrayLauncher.Visible = $true

Ao executar o script, você deverá ver um ícone do PowerShell aparecer na bandeja do sistema, como mostrado abaixo.

Agora, crie um contêiner para seus itens de menu com um novo objeto ContextMenu e crie todos os itens do menu. Para este exemplo, o menu terá dois scripts para executar e uma opção de saída.

 $ContextMenu = New-Object System.Windows.Forms.ContextMenu
 ​
 $LoggedOnUser = New-MenuItem -Text "Get Logged On User" -MyScriptPath "C:\scripts\GetLoggedOn.ps1"
 $RestartRemoteComputer = New-MenuItem -Text "Restart Remote PC" -MyScriptPath "C:\scripts\restartpc.ps1"
 $ExitLauncher = New-MenuItem -Text "Exit" -ExitOnly

Em seguida, adicione todos os itens de menu recém-criados ao menu de contexto. Isso garantirá que cada opção de menu apareça no menu de contexto do formulário.

 #Adicione itens de menu ao menu de contexto
 $ContextMenu.MenuItems.AddRange($LoggedOnUser)
 $ContextMenu.MenuItems.AddRange($RestartRemoteComputer)
 $ContextMenu.MenuItems.AddRange($ExitLauncher)#Adicione componentes ao nosso formulário
 $SystrayLauncher.ContextMenu = $ContextMenu

Mostre o Formulário do Launcher

Agora que o formulário está completo, a última coisa a fazer é mostrá-lo, garantindo que a janela do console do PowerShell não apareça. Faça isso usando Start-HideConsole, exibindo o formulário do lançador e mostrando novamente o console com Start-ShowConsole para evitar um processo powershell.exe travado.

#Lançamento
Start-HideConsole
$Form.ShowDialog() > $null
Start-ShowConsole

Quer mais dicas como esta? Confira o meu blog pessoal de PowerShell em: https://nkasco.com/FriendsOfATA

O código completo na sua totalidade pode ser encontrado aqui: https://github.com/nkasco/PSSystrayLauncher

Suas Conclusões

Parabéns, você terminou este projeto! Neste artigo, você aprendeu:

  1. Como expor componentes da API do Windows.
  2. Como trabalhar com menus de contexto via WinForms e adicionar itens de menu subsequentes.
  3. Como criar um ícone na bandeja do sistema em PowerShell.

Este projeto deve fornecer a você entendimento e experiência suficientes para criar seu próprio menu de bandeja do sistema para seus scripts do PowerShell!

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