Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Properly displaying a tray balloon tooltip in PowerShell

Short version: I think I need help with properly using events in PowerShell that are invoked as a result of a Windows Message to get rid of a balloon tooltip's icon.

Long Version:

I have a long-running PowerShell command (a build) that I would like to be notified when it completes via a balloon tooltip in the system tray/notification area.

I was able to create a Write-BalloonTip script (below) that does roughly what I want. The only problem is that, as sometimes happens with tray icons, the tray icon doesn't disappear until I mouse over it. By re-using the same global variable to represent the NotifyIcon, I'm able to re-use this script and keep it so that only one system tray icon remains (until I mouse over it). This still feels like a hack. I tried to add an event handler so that it'd be notified on the BalloonTipClosed event and then dispose of it there. In the event handler, I tried all three techniques I've seen suggested for getting rid of the lingering icon to no avail.

The annoying part is that a simple .Dispose seems to work on subsequent calls of the script, leading me to think that the event script block isn't being called at all.

I've verified that BalloonTipClosed gets called after the tip fades away in a separate WinForms app.

Am I missing something basic? Any help is much appreciated. Thanks!

Here's the code for "Write-BalloonTip.ps1":

param
(
    $text,
    $title = "",
    $icon = "Info",
    $timeout=15000
)

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | out-null

if ($global:writeBalloonTipIcon)
{
    # This gets rid of the previous one
    $global:writeBalloonTipIcon.Dispose()
}

$global:writeBalloonTipIcon = new-object System.Windows.Forms.NotifyIcon 
$global:writeBalloonTipIcon.Icon = [System.Drawing.SystemIcons]::Information

# FIXME: This *should* cleanup the icon after it's done, but it doesn't seem to work
$global:writeBalloonTipIcon.add_BalloonTipClosed(
  {
    # this *should* work, but it's not. What am I missing?
    $global:writeBalloonTipIcon.Icon = $null; 
    $global:writeBalloonTipIcon.Visible = $false; 
    $global:writeBalloonTipIcon.Dispose();
  });

$global:writeBalloonTipIcon.Visible = $true;
$global:writeBalloonTipIcon.ShowBalloonTip($timeout, $title, $text, $icon);
like image 843
Jeff Moser Avatar asked Feb 01 '10 19:02

Jeff Moser


2 Answers

I think you need to execute this code in an STA thread. PowerShell (v2 shown here) executes in an MTA thread by default:

PS U:\> [System.Threading.Thread]::CurrentThread


ManagedThreadId    : 5
ExecutionContext   : System.Threading.ExecutionContext
Priority           : Normal
IsAlive            : True
IsThreadPoolThread : False
IsBackground       : False
ThreadState        : Running
ApartmentState     : MTA
CurrentUICulture   : en-US
CurrentCulture     : en-US
Name               : Pipeline Execution Thread
like image 183
Aaron Lerch Avatar answered Nov 04 '22 20:11

Aaron Lerch


I would recommend using the Register-ObjectEvent to subscribe to the BalloonTipClosed event. This came up recently in another SO post. Check it out.

like image 29
Keith Hill Avatar answered Nov 04 '22 19:11

Keith Hill