You may have noticed that the notification area applications in Windows 7 (Volume/Power/Network/Action Centre) appear centred above their icon. I wanted Keiki to do the same; the current version is hardcoded to sit in the bottom right of the screen, which causes a few problems:

The taskbar position is not taken into account; the window will be in the bottom right even if the taskbar is at the top of the screen.

The window appears on top of the new Windows 7 fly-out interface for hiding notify icons if the Keiki icon is kept there.

In this post, I will demonstrate how to retrieve the location of a System.Windows.Forms.NotifyIcon with a function new to shell32.dll in Windows 7: Shell_NotifyIconGetRect. Windows Vista unfortunately lacks this function: I will cover the approach I use in Vista in a later post.

The Shell_NotifyIconGetRect function takes two parameters: a NOTIFYICONIDENTIFIER (a structure that identifies the icon) and a RECT (a structure which will receive the coordinates of the icon) and returns an HRESULT indicating whether the method succeeds or not. So, how can we use this function in managed code?

PInvoke

When it comes to using unmanaged APIs within managed code, PInvoke.net is probably the best place to start. Many PInvoke signatures for the Win32 API can be found here. Regrettably, as Shell_NotifyIconGetRect is a new function, it doesn’t yet have an entry, so we have to work some stuff out for ourselves.

System.Windows.Forms.NotifyIcon

As there is currently no wrapper for Shell_NotifyIcon included with WPF, Keiki uses the System.Windows.Forms.NotifyIcon class to create its notification area icon. This hasn’t been an issue: I need only basic functionality, and there is the additional benefit (for me) of avoiding WPF menus, which imitate the style of native menus, but are in fact slightly different, thus sticking out (at least to my pedant eyes). The WinForms wrapper has its own downsides: it doesn’t support custom icons (introduced in XP SP2 but not important for my project) and it would be nice not to have to reference that namespace at all in a WPF program.

There are numerous high quality 3rd-party tray icon implementations for WPF: the best I’ve seen is WPF NotifyIcon by Philipp Sumi. However, I’ve decided to stick with the standard WinForms NotifyIcon for this project, mostly for simplicity’s sake. So, how can we use this class with the Shell_NotifyIconGetRect function that we just got working in managed code?

The icon can be identified to Shell_NotifyIconGetRect through this structure in two ways:

guidItem alone (recommended)

hWnd plus uID

If guidItem is used, hWnd and uID are ignored.

Using a GUID to identify a notify icon is an approach that is new to Windows 7 and one that isn’t used by the WinForms NotifyIcon class. Thus, we need to find the notify icon’s hWnd (window handle) and uID so that we may pass them to our Shell_NotifyIconGetRect function.

Finally…

After what seems like a lot of effort (though not a lot of code), we can at last call our Shell_NotifyIconGetRect function!

C#

1

2

3

4

5

6

7

8

9

RECT rect=newRECT();

NOTIFYICONIDENTIFIER nid=newNOTIFYICONIDENTIFIER()

{

hWnd=iconhandle,

uID=(uint)iconid

};

nid.cbSize=(uint)Marshal.SizeOf(nid);

intresult=Shell_NotifyIconGetRect(refnid,outrect);

If everything has happened as it should, ‘rect’ should now hold the coordinates of the notify icon. (Again, some error handling would be nice.)

In the next post in this series I will look at how to get the position of the taskbar so that we place the window correctly using the result from Shell_NotifyIconGetRect.

Interesting note: if the notify icon is inside the fly-out notification area box, Shell_NotifyIconGetRect will only return its position within that box if the box is currently open. If the box is closed, the result seems to point to the location of the notification area expansion arrow.