У меня есть рабочий код, который изменит иконку в трее в зависимости от наличия процесса в блокноте.
Set-StrictMode -Version Latest
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function Test-Notepad {
[bool](Get-Process -Name 'notepad' -ErrorAction SilentlyContinue)
}
function menu1clickEvent ($label) {
# Build Form object
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "My Form"
$Form.Size = New-Object System.Drawing.Size(200,200)
$Form.StartPosition = "CenterScreen"
$Form.Topmost = $True
$Form.Controls.Add($Label) # Add label to form
$form.ShowDialog()| Out-Null # Show the Form
}
function menu4clickEvent ($icon) {
$icon.Visible = $false
[System.Windows.Forms.Application]::Exit()
}
function create_taskbar_menu($label){
# Create menu item
$MenuItem1 = New-Object System.Windows.Forms.MenuItem
$MenuItem1.Text = "Menu Item 1"
# Create menu item
$MenuItem2 = New-Object System.Windows.Forms.MenuItem
$MenuItem2.Text = "Exit"
# Add menu items to context menu
$contextmenu = New-Object System.Windows.Forms.ContextMenu
$Main_Tool_Icon.ContextMenu = $contextmenu
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem1)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem2)
# Add click events
$MenuItem1.add_Click({menu1clickEvent $label})
$MenuItem2.add_Click({menu4clickEvent $Main_Tool_Icon})
}
# Add assemblies for WPF and Mahapps
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('WindowsFormsIntegration') | out-null
# Choose an icon to display in the systray
$onlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("C:/WINDOWS/system32/notepad.exe")
# use this icon when notepad is not running
$offlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("C:/WINDOWS/system32/resmon.exe")
# create tray icon
$Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
$Main_Tool_Icon.Text = "Icon Text"
$Main_Tool_Icon.Icon = if (Test-Notepad) { $onlineIcon } else { $offlineIcon }
$Main_Tool_Icon.Visible = $true
# Build Label object
$Label = New-Object System.Windows.Forms.Label
$Label.Name = "labelName"
$Label.AutoSize = $True
# Initialize the timer
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.Add_Tick({
if ($Label){
$Label.Text, $Main_Tool_Icon.Icon = if (Test-Notepad) {
"Notepad is running", $onlineIcon
} else {
"Notepad is NOT running", $offlineIcon
}
}
})
$timer.Start()
create_taskbar_menu $label
# ERROR:
# create_taskbar_menu2 $label $Main_Tool_Icon
# Make PowerShell Disappear - Thanks Chrissy
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)
# Use a Garbage colection to reduce Memory RAM
# https://dmitrysotnikov.wordpress.com/2012/02/24/freeing-up-memory-in-powershell-using-garbage-collector/
# https://docs.microsoft.com/fr-fr/dotnet/api/system.gc.collect?view=netframework-4.7.2
[System.GC]::Collect()
# Create an application context for it to all run within - Thanks Chrissy
# This helps with responsiveness, especially when clicking Exit - Thanks Chrissy
$appContext = New-Object System.Windows.Forms.ApplicationContext
try
{
[System.Windows.Forms.Application]::Run($appContext)
}
finally
{
foreach ($component in $timer, $Main_Tool_Icon, $offlineIcon, $onlineIcon, $appContext)
{
# The following test returns $false if $component is
# $null, which is really what we're concerned about
if ($component -is [System.IDisposable])
{
$component.Dispose()
}
}
Stop-Process -Id $PID
}
Моя цель состоит в том, чтобы реорганизовать create_taskbar_menu
метод для использования переданных переменных, а не глобальной, вот мой код :
function create_taskbar_menu2($label, $trayIcon){
# Create menu item
$MenuItem1 = New-Object System.Windows.Forms.MenuItem
$MenuItem1.Text = "Menu Item 1"
# Create menu item
$MenuItem2 = New-Object System.Windows.Forms.MenuItem
$MenuItem2.Text = "Exit"
# Add menu items to context menu
$contextmenu = New-Object System.Windows.Forms.ContextMenu
# Set icton properties
$trayIcon.ContextMenu = $contextmenu
$trayIcon.contextMenu.MenuItems.AddRange($MenuItem1)
$trayIcon.contextMenu.MenuItems.AddRange($MenuItem2)
# Add click events
$MenuItem1.add_Click({menu1clickEvent $label})
$MenuItem2.add_Click({menu4clickEvent $trayIcon})
}
Я называю этот метод как create_taskbar_menu2 $label $Main_Tool_Icon
вместо create_taskbar_menu $label
. Проблема в том, что после этого рефактора мое приложение вылетает, когда я нажимаю кнопку выхода на панели задач. Появляется следующее сообщение:
Unhandled exception has occurred in your application. If you click Continue, the application will ignore this error and attempt to continue. If you click Quit, the application will close immediately.
The variable '$trayIcon' cannot be retrieved because it has not been set.
Я запускаю свой код с cmd.exe
как powershell -f script.ps1
И ничего не получаю в консоли. Что я делаю неправильно, и как я могу отладить такие. NET Исключения Framework?
PS: вот журнал, который я могу скопировать из диалогового окна сообщения об ошибке (он немного длиннее, поэтому я наклеил на pastebin )