Menu de formulaire PowerShell : Accès rapide à vos scripts

C’est encore le moment du projet du week-end et aujourd’hui, vous apprendrez à construire un menu de formulaire PowerShell léger dans la zone de notification du système où vous pourrez lancer rapidement et facilement vos scripts PowerShell les plus convoités. Vous pouvez voir ci-dessous le résultat final.

Launching PowerShell scripts via a system tray menu icon

Dans cet article, vous apprendrez à construire votre propre interface utilisateur (GUI) de menu PowerShell en décomposant le processus étape par étape.

Exigences en matière d’environnement et de connaissances

Avant de vous plonger, assurez-vous de répondre aux exigences minimales suivantes :

  • Windows 7 ou version ultérieure
  • Windows PowerShell 3 ou version ultérieure – La dernière version de .NET Core 3.0 avec l’aperçu de PowerShell 7 peut fonctionner sur Windows en raison du support récemment ajouté pour WPF et WinForm , mais cela n’a pas été testé.
  • .NET Framework 4.5 ou version ultérieure
  • A familiarity with Windows Forms (WinForms)  You can, however, due this with WPF too though.

Pour ce projet, la bonne nouvelle est que vous n’aurez pas vraiment besoin de compter sur Visual Studio , PoshGUI , ou tout autre outil de développement d’interface utilisateur, car les composants principaux sur lesquels ce projet s’appuiera sont les suivants :

  • NotifyIcon – Cela représentera notre icône de zone de notification système personnalisable avec laquelle l’utilisateur peut interagir.
  • MenuContextuel – Conteneur lorsque l’utilisateur clique avec le bouton droit de la souris sur l’icône de la barre d’état système.
  • ÉlémentDeMenu – Objets individuels pour chaque option dans le menu contextuel.

Ouvrez votre éditeur de script PowerShell préféré et commençons!

Pour ce projet, vous allez créer trois fonctions : deux fonctions pour afficher/masquer la console afin de fournir une expérience utilisateur plus propre et une pour ajouter des éléments à votre menu de la barre d’état système. Ces fonctions serviront de base pour une utilisation ultérieure afin de rendre votre vie beaucoup plus facile, comme vous l’apprendrez un peu plus tard dans cet article.

Afficher/Masquer la fenêtre de la console

À moins qu’elle ne soit cachée, lorsque vous lancez un script PowerShell, la console PowerShell familière apparaîtra. Étant donné que les éléments de menu dans le formulaire PowerShell que vous allez créer lanceront des scripts, vous devez vous assurer que la console ne s’affiche pas. Vous voulez juste qu’elle s’exécute.

Lorsqu’un script est exécuté, vous pouvez basculer l’affichage de la fenêtre de la console PowerShell en utilisant un peu de .NET.

Tout d’abord, ajoutez le type Fenêtre .NET dans la session actuelle. Pour ce faire, vous utiliserez un peu de C# comme vous le verrez ci-dessous. Les deux méthodes que vous devez charger dans le contexte sont GetConsoleWindow et ShowWindow. En chargeant ces DLL en mémoire, vous exposez certaines parties de l’API, ce qui vous permet de les utiliser dans le contexte de votre script PowerShell :

 # Charger les DLL dans le contexte de la session console actuelle
 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);

Créez deux fonctions en utilisant les charges ci-dessus en utilisant la méthode GetConsoleWindow() et ShowWindow() comme indiqué ci-dessous.

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

Avec ces deux fonctions, vous avez maintenant créé un moyen de montrer ou de cacher la fenêtre de la console à volonté.

Remarque : Si vous souhaitez voir la sortie des scripts exécutés via le menu, vous pouvez utiliser des transcriptions PowerShell ou d’autres fonctionnalités de journalisation basées sur du texte. Cela vous permet de maintenir le contrôle plutôt que d’exécuter uniquement la session PowerShell avec le paramètre WindowStyle pour la cacher.

Commencez maintenant à construire le code du script en appelant Start-HideConsole. Lorsque le script menu-driven PowerShell s’exécute, cela garantira que la fenêtre de la console PowerShell ne s’affiche pas.

<#
	Initialisation des fonctions et chargement des objets en mémoire
	Afficher une barre de chargement basée sur du texte ou Write-Progress vers l'hôte
#>
 
Start-HideConsole
 
<# 
	Code pour afficher votre icône de formulaire/tray
	Cela maintiendra la console ici jusqu'à sa fermeture
#>

Créer des options de menu

Il est maintenant temps de créer les options de menu. En veillant à pouvoir facilement créer de nouvelles options ultérieurement, créez une autre fonction cette fois appelée New-MenuItem. Lorsque vous appelez cette fonction, elle créera un nouvel objet MenuItem .NET que vous pourrez ensuite ajouter au menu plus tard.

Chaque option de menu lancera un autre script ou quittera le lanceur. Pour prendre en compte cette fonctionnalité, la fonction New-MenuItem a trois paramètres:

  • Texte – Le libellé sur lequel l’utilisateur cliquera
  • CheminDeMonScript – Le chemin vers le script PowerShell à exécuter
  • SortieSeule – L’option pour quitter le lanceur.

Ajoutez le fragment de fonction ci-dessous au script du menu.

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

Poursuivez la construction de la fonction New-MenuItem, créez un objet MenuItem en l’assignant à une variable.

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

Ensuite, attribuez le libellé texte à l’élément de menu.

 # Appliquer le texte désiré
 if($Text) {
 	$MenuItem.Text = $Text
 }

Ajoutez maintenant une propriété personnalisée à MenuItem appelée CheminDeMonScript. Ce chemin sera utilisé lorsque l’élément est cliqué dans le menu.

 # Appliquer la logique de l'événement de clic
 if($MyScriptPath -and !$ExitOnly){
 	$MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty

Ajoutez un événement de clic à l’élément de menu qui lance le script désiré. Start-Process fournit une manière propre de le faire dans un bloc try/catch afin de vous assurer que toutes les erreurs de lancement du script (telles que PowerShell n’étant pas disponible ou le script n’existant pas au chemin fourni) tombent dans votre bloc catch.

   $MenuItem.Add_Click({
        try{
            $MyScriptPath = $This.MyScriptPath # Utilisé pour trouver le chemin approprié pendant l'événement de 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
        }
  })

Ajoutez la logique restante pour fournir une condition de sortie pour le lanceur suivi du renvoi de votre nouvel élément de menu créé pour être attribué à une autre variable à l’exécution.

    # Fournir un moyen de quitter le lanceur
    if($ExitOnly -and !$MyScriptPath){
        $MenuItem.Add_Click({
            $Form.Close()
    
            # Gérer tous les processus suspendus
            Stop-Process $PID
        })
    }
 
 	 # Renvoyer notre nouvel élément de menu
    $MenuItem
 }

Vous devriez maintenant avoir la fonction New-MenuItem créée ! La fonction finale devrait ressembler à ceci:

 function New-MenuItem{
     param(
         [string]
         $Text = "Placeholder Text",
 
         $MyScriptPath,
         
         [switch]
         $ExitOnly = $false
     )
 
     #Initialisation
     $MenuItem = New-Object System.Windows.Forms.MenuItem
 
     #Appliquer le texte désiré
     if($Text){
         $MenuItem.Text = $Text
     }
 
     #Appliquer la logique de l'événement de clic
     if($MyScriptPath -and !$ExitOnly){
         $MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty
     }
 
     $MenuItem.Add_Click({
             try{
                 $MyScriptPath = $This.MyScriptPath #Utilisé pour trouver le chemin approprié lors de l'événement de 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
             }
         })
 
     #Fournir un moyen de quitter le lanceur
     if($ExitOnly -and !$MyScriptPath){
         $MenuItem.Add_Click({
                 $Form.Close()
    
                 #Gérer tous les processus en cours d'exécution
                 Stop-Process $PID
             })
     }
 
     #Renvoyer notre nouveau MenuItem
     $MenuItem
 }

Testez la fonction New-MenuItem en copiant et collant le code ci-dessus dans votre console PowerShell et en exécutant la fonction en fournissant de fausses valeurs de paramètres. Vous verrez qu’un objet MenuItem .NET est retourné.

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

Création d’un formulaire de lancement

Envie de plus de conseils comme celui-ci ? Consultez mon blog personnel PowerShell à l’adresse : https://nkasco.com/FriendsOfATA

Maintenant que vous pouvez facilement créer de nouveaux éléments de menu, il est temps de créer un lanceur de zone de notification système qui affichera le menu.

Créez un objet de formulaire de base pour y ajouter des composants. Cela n’a pas besoin d’être compliqué car il restera caché pour l’utilisateur final et maintiendra également la console en cours d’exécution en arrière-plan.

 #Créer un formulaire pour servir de conteneur à nos composants
 $Form = New-Object System.Windows.Forms.Form
 ​
 #Configurer notre formulaire pour qu'il soit caché
 $Form.BackColor = "Magenta" #Associez cette couleur à la propriété TransparencyKey pour rendre votre formulaire transparent
 $Form.TransparencyKey = "Magenta"
 $Form.ShowInTaskbar = $false
 $Form.FormBorderStyle = "None"

Ensuite, créez l’icône qui apparaîtra dans la zone de notification système. Ci-dessous, j’ai choisi d’utiliser l’icône PowerShell. À l’exécution, le code ci-dessous crée une véritable icône de zone de notification système. Cette icône peut être personnalisée selon vos préférences en définissant la variable SystrayIcon sur l’icône souhaitée.

Consultez la documentation de la classe System.Drawing.Icon pour voir d’autres méthodes permettant de charger une icône en mémoire.

 #Initialisez/configurez les composants nécessaires
 $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

Lorsque le script est exécuté, vous devriez alors voir une icône PowerShell apparaître dans votre zone de notification système, comme indiqué ci-dessous.

Maintenant, créez un conteneur pour vos éléments de menu avec un nouvel objet ContextMenu et créez tous vos éléments de menu. Pour cet exemple, le menu comportera deux scripts à exécuter et une option de sortie.

 $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

Ensuite, ajoutez tous les éléments de menu créés précédemment au menu contextuel. Cela garantira que chaque option de menu s’affiche dans le menu contextuel du formulaire.

 #Ajoutez des éléments de menu au menu contextuel
 $ContextMenu.MenuItems.AddRange($LoggedOnUser)
 $ContextMenu.MenuItems.AddRange($RestartRemoteComputer)
 $ContextMenu.MenuItems.AddRange($ExitLauncher)#Ajoutez des composants à notre formulaire
 $SystrayLauncher.ContextMenu = $ContextMenu

Affichez le formulaire de lancement

Maintenant que le formulaire est complet, la dernière chose à faire est de l’afficher tout en veillant à ce que la fenêtre de console PowerShell ne s’affiche pas. Faites cela en utilisant votre Start-HideConsole, en affichant le formulaire de lancement, puis en affichant à nouveau la console avec Start-ShowConsole pour éviter qu’un processus powershell.exe ne reste bloqué.

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

Vous voulez plus de conseils comme celui-ci? Consultez mon blog personnel PowerShell sur : https://nkasco.com/FriendsOfATA

Le code complet dans son intégralité est disponible ici : https://github.com/nkasco/PSSystrayLauncher

Vos conclusions

Félicitations, vous avez terminé ce projet! Dans cet article, vous avez appris :

  1. Comment exposer des composants de l’API Windows.
  2. Comment travailler avec les menus contextuels via WinForms et ajouter des éléments de menu ultérieurs.
  3. Comment créer une icône de zone de notification dans PowerShell.

Ce projet devrait vous donner une compréhension et une expérience suffisantes pour créer votre propre menu de zone de notification pour vos scripts PowerShell!

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