Introduction

We’re all familiar with programs that add icons to the
system tray - Windows NT/2000’s Task Manager, the Microsoft offline files
synchronisation manager, and many other third-party apps.

Quite frequently, these icons open a dialog of some sort
when double-clicked. The normal procedure is to simply hide or show the window.
It would be nice if the window displayed the same animation used when
minimizing or maximizing a window to and from the task bar. This article shows
how simple it is to do just that.

Drawing the Animation

It is actually very simple to duplicate the effect of the
minimizing and maximizing animation - just one function call will do the trick.
DrawAnimatedRects is used to draw and animate a rectangle. Here’s the
prototype:

When animating the caption, the hwnd argument is the handle
to the window to be animated, rather than the window to use for clipping and it
cannot be NULL, as suggested in the documentation. The lprcFrom and lprcToRECTs describe the start and the end positions of the animation.

The interesting argument is idAni, which sets the type of
animation to display. If this is set to IDANI_CAPTION, the window caption will
animate from the lprcFrom to lprcTo, in the same way as windows are minimized
and maximized.

(As a brief aside, the include files that come with Visual
C++ 6 include definitions for not only IDANI_CAPTION, but also IDANI_OPEN and
IDANI_CLOSE. There is no documentation as to what IDANI_OPEN and IDANI_CLOSE
do, and when plugged into the example code, they fail to produce any animation
at all. Perhaps it was intended that Windows display an animation when opening
and closing a window in the same way as the Mac does. Whatever was intended, it
doesn’t appear to have been implemented.

Another thing to note is that the headers for the current
Platform SDK - April 2000 - only define IDANI_OPEN, so it is necessary to
either use the Visual C++ 6 headers, or manually define the missing values,
which is what the example code does)

Determining the Animation Parameters

It’s not enough to know how to display the animation; we
also need to know the start and end position. When minimizing, the “from” value
is simple, you can use the RECT of the window. This gives us the correct width
of the window, but the height is going to be larger than the caption bar. This
is not a problem, however. We have told Windows that we are animating the
caption, so Windows only uses the caption’s height.

The “to” value is slightly trickier. We need to get the RECT
of the system tray. Unfortunately, there is no documented way to do this, and
each method we can use has its downside.

The most accurate way of getting the system tray dimensions
is to actually get the system tray’s window. Through judicious use of the very
handy Spy++ utility, we can see that the system tray is a window of class
“TrayNotifyWnd”, which is a child of the top-level window of class
“Shell_TrayWnd”. We can easily get these windows using FindWindow, and then
simply call GetWindowRect to get the dimensions of the system tray. As this
gives the best results, this is the preferable method, but it must be
remembered that this is undocumented and utilises window class names and
hierarchies that might not exist in future versions of the shell, so mustn’t be
relied upon. If this method fails, we drop through to the next method.

The nearest thing we can get to a documented method is to use the
SHAppBarMessage function with the ABM_GETTASKBARPOS message.
This will return us the bounding rectangle and edge position of the task bar.
From this we can tell where the system tray is. If the edge position is the left
or the right of the screen, the tray is at the bottom of the task bar. If the edge
position is the top or the bottom of the screen, the tray is to the right of
the screen. The only problem with this method is that it doesn’t give us the
actual coordinates of the system tray, just the rough position - we have to
give a default size for the system tray.

There is still the possibility that this will fail. This is
really only likely if explorer has been replaced by a third party shell. Many
of these shells now support a system tray (Shell_NotifyIcon sends a WM_COPYDATA
message to a top level window of class “Shell_TrayWnd”, which is the task bar)
If we find this window, we can get some dimensions for the system tray.

If all else fails, we just use a default value in the bottom
right of the screen.

Obviously enough, when maximizing, the “to” and “from”
values are the opposite to when minimizing.

One Last Nicety

Windows allows the animation to be disabled (using a registry setting, or a
tool such as the infamous TweakUI), so we must check for that before calling
DrawAnimatedRects. This is a simple call to SystemParametersInfo,
with the SPI_GETANIMATION value. If the animation has been disabled,
the window is simply hidden; otherwise, we call DrawAnimatedRects.

The Sample Code

There are two main files in the example project included
with this article. MinimizeToTray.cpp contains the functions to minimize and restore
the window, and MinimizeDemo.cpp contains a simple dialog app that demonstrates
how to use these functions.

The MinimizeWndToTray function displays the minimize
animation and hides the window passed in as a parameter. The RestoreWndFromTray
function displays the restore animation and shows the passed in window. Neither
of these functions adds or removes a system tray icon - that is left to the
caller.

One thing to note is that if Shell_NotifyIcon is called before
DrawAnimatedRects, the entire taskbar is erased, and not redrawn until
the animation is complete. As long as the shell icon is removed after the animation,
the taskbar is drawn properly. So call Shell_NotifyIcon after calling
MinimizeWndToTray or RestoreWndFromTray.

MinimizeDemo.cpp shows how to use the two functions in an actual app, and even
shows a simple use of the Shell_NotifyIcon function. The dialog is
minimized to the tray in response to the WM_CLOSE message and to the
SC_MINIMIZE WM_SYSCOMMAND message. The dialog is restored when the icon is
double-clicked.

Conclusion

This code is very easy to use. Simply drop the
MinimizeToTray.cpp file into your project, and add two function calls to your
app. There are plenty of comments in the source files, and the MinimizeDemo.cpp
sample is very easy to follow.

This code is public domain. Feel free to use and abuse it in
any way you want. If you do use it though, I’d appreciate it if you dropped me
a quick note. Consider it emailware.

Coda

The main purpose of this article was to explain how to use
DrawAnimatedRect, and provide as near to foolproof method as
possible of finding the position of the system tray. I didn't intend to describe
how to use Shell_NotifyIcon. However, it is a reasonable place to
pass along a couple of tips on common problems:

The most prevalent problem with notification icons is that once you display a
menu for the icon, clicking outside of the menu doesn't dismiss it. Apparently,
"this behaviour is by design" and is documented in Microsoft's Knowledge Base
article Q135788.

Another problem frequently encountered is that the notification icon stays
in the system tray, and disappears when the mouse moves over it. This is simply
that the program hasn't removed the icon before quitting - a call to
Shell_NotifyIcon with NIM_DELETE before exiting will
sort this out.

Finally, we have a somewhat tricky one. When double-clicking on one icon,
it is sometimes possible to also activate another. This happens if the first
icon is removed in response to the double-click message. As this message is
sent in response to the mouse button being pressed, released and then pressed
again, Windows still has a button up event left to send. And because the icons
have moved in response to the first icon being removed, this message is sent
somewhere it wasn't intended for. That icon may then innocently enough take
action in response to this message.

This is a situation where no one can take blame. However, it is one we can
work around. Instead of removing the icon once we action on the double-click
event, we remove it in response to the second button up, everything works
perfectly. The easiest way to do this is to set a flag when we receive a
double-click event, and remove the icon when the flag is set in the button up
handler. The example code included shows how to do this.

History

1.0 28 June 2000 Initial version

1.1 19 October 2000 Added useful tips on using Shell_NotifyIcon. Thanks to
Darren Schroeder for reporting this potential problem!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Hi,I am developing a s/w which uses the SHAppBarMessage API with ABM_GETTASKBARPOS as first parameter.But I found that it does not work on a Windows NT platform(works fine with Windows 9x).i.e the uEdge property always gives value "0" irrispective of taskbar position.Could anyone please guide me ?Thanking in appriciation,

Does any body have any idea about how to invoke a context menu on rightclick of Network Neighborhood computer. Say u want to display two menuitems like connect and disconnect of a n/w neighborhood computer usingShellExtensions.

Any idea or suggestions highly appreciated..

Pls. provide how to register the component , i mean under what hive to beregistered.

Is there any way of removing the VSS stuff in the workspace file before it is posted for download? I'm using version 5 of VC++ but along with VSS and when I open the workspace, it prompts for trying to reconnect the project up to some project of my own. It is really only an annoyance thing as I click through three dialogs to finally get the workspace open. Oddly I can't get it to save this 'state', cause even the next time I open the workspace, I have to repeat the process. If nobody else is experiencing this, then perhaps it is just my own environment causing this.

i see a problem with this program that i have seen in many other programs. when i double click the icon in the tray it activates the program as it should however it also activates the one next to it on the left also. i've experienced this many many times and it doesn't happen on all tray programs. so i have to conclude it's something in the source as opposed to a microsoft tray bug. anyone else experienced this or had a similar problem

Ah. Good one! It's not exactly my fault, and it's not exactly Microsoft's It's kind of a side of effect of Windows...

Most tray programs respond to WM_LBUTTONDBLCLK. The only problem is that after sending the WM_LBUTTONDBLCLK to the system tray, Windows then sends WM_LBUTTONUP. And of course, if the icon has been removed, the shell will send the message to the icon that is now in that position, and it's up to the controlling app how that is processed - activating or showing a menu or whatever. (I've actually got this problem in another app, where I hide a window in response to a double click message, and the window beneath it gets the final button up message). The only way around it would be to remove the icon in response to the (second!) WM_LBUTTONUP message. I don't really like this method - it feels too much like a kludge, but it works.

I'll try and get the article updated with this info. Thanks for pointing this out

This would probably solve the problem 9 times out of 10, but if, for some reason the system delays sending the button up message (paging, heavy load, or whatever) you're going to be back to square one. The best way to do this is to only remove the icon in response to the second button up message. I've submitted an update to the article which addresses this, hopefully it'll be updated soo