powershellwinformsclickexitsystray

How to exit a powershell script that runs in the systray?


I would like to exit this systray program by clicking with the left mouse on the text "Quit.". When I hover, the mouse shows a rotating blue icon and clicking does nothing. What's the problem with the script?

# a systray program, that should be exited (but it doesn't)
# 2023-03-18


$iconPath = "H:\Dropbox\400 - Scriptprogrammierung\Powershell\Taskleiste mit Wochentag\icons\ico\Logo.ico" # icon path
Write-Host -ForegroundColor Yellow $iconPath
$tooltip = "This is a text."

# NotifyIcon-object
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
$notifyIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($iconPath)
$notifyIcon.Text = $tooltip

########################
# Here seems to be the problem...
$contextMenu = New-Object System.Windows.Forms.ContextMenuStrip
$menuItemExit = New-Object System.Windows.Forms.ToolStripMenuItem
$menuItemExit.Text = "Quit."
$contextMenu.Items.Add($menuItemExit)
$notifyIcon.ContextMenuStrip = $contextMenu
$menuItemExit.add_Click({ $notifyIcon.Dispose(); exit })
########################

# Show icon in systray
$notifyIcon.Visible = $true

# Loop
while ($true) {
    $notifyIcon.Text = $tooltip
    Start-Sleep -Seconds 60 # wait 60 seconds
}



Solution

  • The crucial changes required are:

    # Load the WinForms assembly.
    Add-Type -AssemblyName System.Windows.Forms
    
    # Use PowerShell's icon in this example; be sure to use a full path.
    $iconPath = (Get-Process -Id $PID).Path
    $tooltip = "This is a text."
    
    # Construct the NotifyIcon object.
    $notifyIcon = [System.Windows.Forms.NotifyIcon]::new()
    $notifyIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($iconPath)
    $notifyIcon.Text = $tooltip
    
    # Define a script-level variable that indicates whether the
    # script should be exited, to be set to $true from the .add_Click() event handler.
    $done = $false
    
    $contextMenu = [System.Windows.Forms.ContextMenuStrip]::new()
    $menuItemExit = [System.Windows.Forms.ToolStripMenuItem]::new()
    $menuItemExit.Text = "Quit."
    $null = $contextMenu.Items.Add($menuItemExit)
    $notifyIcon.ContextMenuStrip = $contextMenu
    # Set the script-level $done variable to $true when the menu item is clicked.
    $menuItemExit.add_Click({ $script:done = $true })
    
    # Show icon in systray (notification area)
    $notifyIcon.Visible = $true
    
    Write-Verbose -Verbose @"
    Adding a PowerShell icon to notification area (system tray).
    Use the icon's context menu to quit this script, 
    or press Ctrl-C in the console window.
    "@
    
    # Loop
    try {
      while (-not $done) {
          # IMPORTANT: Make WinForms process its events.
          [System.Windows.Forms.Application]::DoEvents()
          # Sleep just a little, to keep the UI responsive.
          # Note:
          #   In theory, you could perform other tasks here,
          #   as long as they complete quickly so as to still
          #   allow frequent enough ::DoEvents() calls.
          Start-Sleep -MilliSeconds 100
      }
    }
    finally {
      # Dispose of the notify icon, which also removes it from display.
      $notifyIcon.Dispose()
      Write-Verbose -Verbose 'Exiting.'
    }