Menu di moduli di PowerShell: Accesso rapido ai tuoi script

È di nuovo tempo di progetto del fine settimana e oggi imparerai come costruire un menu di forma leggera del vassoio di sistema PowerShell dove puoi lanciare rapidamente e facilmente i tuoi script PowerShell più ambiti. Puoi vedere qui sotto il risultato finale.

Launching PowerShell scripts via a system tray menu icon

In questo articolo, imparerai come creare il tuo menu PowerShell GUI suddividendo il processo passo dopo passo.

Requisiti Ambientali e di Conoscenza

Prima di tuffarti, assicurati di soddisfare i seguenti requisiti minimi:

  • Windows 7 o successivo
  • Windows PowerShell 3 o successivo – La versione più recente di .NET Core 3.0 con anteprima di PowerShell 7 potrebbe funzionare su Windows grazie al supporto aggiunto di recente per WPF e WinForm, ma non è testato.
  • .NET Framework 4.5 o successivo
  • A familiarity with Windows Forms (WinForms)  You can, however, due this with WPF too though.

Per questo progetto, la buona notizia è che non avrai davvero bisogno di affidarti a Visual Studio, PoshGUI, o qualsiasi altro strumento di sviluppo UI in quanto i componenti principali su cui si baserà questo progetto saranno i seguenti:

  • NotifyIcon – Questo rappresenterà la nostra icona del vassoio di sistema personalizzabile per l’utente interagire.
  • ContextMenu – Contenitore per quando l’utente fa clic con il tasto destro sull’icona della barra delle applicazioni.
  • MenuItem – Singoli oggetti per ciascuna opzione nel menu a comparsa del clic destro.

Apri il tuo script editor PowerShell preferito e iniziamo!

Per questo progetto, dovrai creare tre funzioni: due funzioni per mostrare/nascondere la console al fine di fornire un’esperienza utente più pulita e una per aggiungere voci al menu della barra delle applicazioni. Queste funzioni serviranno come base per utilizzi successivi, rendendo la tua vita molto più facile, come vedrai più avanti in questo articolo.

Mostra/Nascondi Finestra Console

A meno che tu non la nasconda, quando avvii uno script PowerShell, comparirà la familiare console PowerShell. Poiché le voci di menu nel modulo PowerShell che creerai avvieranno degli script, assicurati che la console non si apra. Vuoi solo che venga eseguito.

Quando uno script viene eseguito, puoi alternare la visualizzazione della finestra della console PowerShell utilizzando un po’ di .NET.

Prima aggiungi il tipo Window di .NET nella sessione corrente. Per farlo, utilizzerai del C#, come mostrato di seguito. I due metodi che devi caricare nel contesto sono GetConsoleWindow e ShowWindow. Caricando queste DLL in memoria, stai esponendo determinate parti dell’API, consentendoti di utilizzarle nel contesto dello script di PowerShell:

 #Carica le DLL nel contesto della sessione corrente della console
 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);

Crea due funzioni utilizzando quelle caricate sopra, utilizzando il metodo GetConsoleWindow() e ShowWindow() come mostrato di seguito.

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

Con queste due funzioni, hai ora creato un modo per mostrare o nascondere la finestra della console a tuo piacimento.

Nota: Se desideri vedere l’output degli script eseguiti tramite il menu, puoi utilizzare transcrizioni di PowerShell o altre funzionalità di registrazione basate su testo. Questo ti consente di mantenere il controllo rispetto all’esecuzione della sessione PowerShell solo con il parametro WindowStyle impostato su nascosto.

Inizia ora a costruire il codice dello script chiamando Start-HideConsole. Quando lo script basato su menu di PowerShell viene eseguito, ciò assicurerà che la finestra della console di PowerShell non venga visualizzata.

<# 
	Inizializzazione delle funzioni e caricamento degli oggetti in memoria
	Mostra una barra di caricamento basata su testo o scrivi-Progress all'host
#>
 
Start-HideConsole
 
<# 
	Codice per visualizzare la tua icona di modulo/barra di sistema
	Questo manterrà la console qui fino a quando verrà chiuso
#>

Crea opzioni di menu

Ora è il momento di creare le opzioni del menu. Assicurati di poter creare facilmente nuove opzioni in seguito, crea un’altra funzione chiamata New-MenuItem. Quando chiami questa funzione, creerà un nuovo oggetto MenuItem .NET che potrai quindi aggiungere al menu successivamente.

Ogni opzione del menu avvierà uno script diverso o uscirà dal launcher. Per accogliere questa funzionalità, la funzione New-MenuItem ha tre parametri:

  • Text – L’etichetta su cui l’utente cliccherà
  • MyScriptPath – Il percorso dello script PowerShell da eseguire
  • ExitOnly – L’opzione per uscire dal launcher.

Aggiungi il frammento di funzione sottostante allo script del menu.

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

Continuando a costruire la funzione New-MenuItem, crea un oggetto MenuItem assegnandolo a una variabile.

 #Inizializzazione
 $MenuItem = New-Object System.Windows.Forms.MenuItem

In seguito, assegna l’etichetta di testo all’elemento di menu.

 # Applica il testo desiderato
 if($Text) {
 	$MenuItem.Text = $Text
 }

Aggiungi ora una proprietà personalizzata al MenuItem chiamata MyScriptPath. Questo percorso verrà richiamato quando si fa clic sull’elemento nel menu.

 #Applicare la logica dell'evento di clic
 if($MyScriptPath -and !$ExitOnly){
 	$MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty

Aggiungi un evento di clic al MenuItem che avvia lo script desiderato. Start-Process fornisce un modo pulito per farlo all’interno di un blocco try/catch in modo che tu possa assicurarti che eventuali errori nell’avvio dello script (come PowerShell non essendo disponibile o lo script che non esiste nel percorso fornito) vengano gestiti nel blocco catch.

   $MenuItem.Add_Click({
        try{
            $MyScriptPath = $This.MyScriptPath #Usato per trovare il percorso corretto durante l'evento di clic
            
            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
        }
  })

Aggiungi la logica rimanente per fornire una condizione di uscita per il launcher seguita dal ritorno del tuo nuovo MenuItem per essere assegnato a un’altra variabile durante l’esecuzione.

    #Fornire un modo per uscire dal launcher
    if($ExitOnly -and !$MyScriptPath){
        $MenuItem.Add_Click({
            $Form.Close()
    
            #Gestire eventuali processi in sospeso
            Stop-Process $PID
        })
    }
 
 	 #Restituire il nostro nuovo MenuItem
    $MenuItem
 }

Ora dovresti avere la funzione New-MenuItem creata!

 function New-MenuItem{
     param(
         [string]
         $Text = "Placeholder Text",
 
         $MyScriptPath,
         
         [switch]
         $ExitOnly = $false
     )
 
     #Inizializzazione
     $MenuItem = New-Object System.Windows.Forms.MenuItem
 
     #Applicare il testo desiderato
     if($Text){
         $MenuItem.Text = $Text
     }
 
     #Applicare la logica dell'evento di clic
     if($MyScriptPath -and !$ExitOnly){
         $MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty
     }
 
     $MenuItem.Add_Click({
             try{
                 $MyScriptPath = $This.MyScriptPath #Utilizzato per trovare il percorso adeguato durante l'evento di clic
             
                 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
             }
         })
 
     #Fornire un modo per uscire dal launcher
     if($ExitOnly -and !$MyScriptPath){
         $MenuItem.Add_Click({
                 $Form.Close()
    
                 #Gestire eventuali processi sospesi
                 Stop-Process $PID
             })
     }
 
     #Restituire il nostro nuovo MenuItem
     $MenuItem
 }

Testare la funzione New-MenuItem copiando e incollando il codice sopra nella console di PowerShell e eseguendo la funzione fornendo alcuni valori di parametro falsi. Vedrai che viene restituito un oggetto MenuItem di .NET.

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

Creazione di un Form del launcher

Vuoi più suggerimenti come questo? Dai un’occhiata al mio blog personale di PowerShell su: https://nkasco.com/FriendsOfATA

Ora che puoi facilmente creare nuovi elementi di menu, è il momento di creare un launcher della barra delle applicazioni che visualizzerà il menu.

Creare un oggetto di form base per aggiungere componenti. Non deve essere nulla di straordinario poiché sarà nascosto all’utente finale e manterrà anche la console in esecuzione in background.

 #Creare un Form per servire come contenitore per i nostri componenti
 $Form = New-Object System.Windows.Forms.Form
 ​
 #Configurare il nostro form per essere nascosto
 $Form.BackColor = "Magenta" #Abbinare questo colore alla proprietà TransparencyKey per rendere trasparente il tuo form
 $Form.TransparencyKey = "Magenta"
 $Form.ShowInTaskbar = $false
 $Form.FormBorderStyle = "None"

Successivo, crea l’icona che apparirà nella barra delle applicazioni del sistema. Qui di seguito ho scelto di utilizzare l’icona di PowerShell. Durante l’esecuzione, il codice seguente crea effettivamente un’icona nella barra delle applicazioni del sistema. Questa icona può essere personalizzata a tuo piacimento impostando la variabile SystrayIcon con l’icona desiderata.

Dai un’occhiata alla documentazione per la classe System.Drawing.Icon per vedere altri metodi con cui è possibile caricare un’icona in memoria.

 #Inizializza/configura i componenti necessari
 $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

Quando lo script viene eseguito, dovresti vedere un’icona di PowerShell comparire nella barra delle applicazioni del sistema come puoi vedere qui sotto.

Ora, crea un contenitore per i tuoi elementi di menu con un nuovo oggetto ContextMenu e crea tutti i tuoi elementi di menu. Per questo esempio, il menu avrà due script da eseguire e un’opzione di uscita.

 $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

Successivamente, aggiungi tutti gli elementi di menu appena creati al menu contestuale. Ciò garantirà che ogni opzione di menu appaia nel menu contestuale del modulo.

 #Aggiungi elementi di menu al menu contestuale
 $ContextMenu.MenuItems.AddRange($LoggedOnUser)
 $ContextMenu.MenuItems.AddRange($RestartRemoteComputer)
 $ContextMenu.MenuItems.AddRange($ExitLauncher)#Aggiungi componenti al nostro modulo
 $SystrayLauncher.ContextMenu = $ContextMenu

Mostra il modulo del lanciatore

Ora che il modulo è completo, l’ultima cosa da fare è mostrarlo garantendo che la finestra della console di PowerShell non venga visualizzata. Fai questo utilizzando il tuo Start-HideConsole, mostrando il modulo del lanciatore e quindi mostrando nuovamente la console con Start-ShowConsole per evitare un processo powershell.exe sospeso.

#Lancio
Start-HideConsole
$Form.ShowDialog() > $null
Start-ShowConsole

Vuoi più suggerimenti come questo? Dai un’occhiata al mio blog personale di PowerShell su: https://nkasco.com/FriendsOfATA

Il codice completo può essere trovato qui: https://github.com/nkasco/PSSystrayLauncher

I tuoi appunti

Congratulazioni, hai completato questo progetto! In questo articolo hai imparato:

  1. Come esporre componenti dell’API di Windows.
  2. Come lavorare con menu contestuali tramite WinForms e aggiungere voci di menu successive.
  3. Come creare un’icona nella barra delle applicazioni di sistema in PowerShell.

Questo progetto dovrebbe darti sufficiente comprensione ed esperienza per creare il tuo menu nella barra delle applicazioni di sistema per i tuoi script PowerShell!

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