Annotating Icons

Placing icon in tray is usually quite straightforward. You usually just take application icon and assign it to NotifyIcon. But what if you want to add a small annotation to tray icon (e.g. small shield)?

Well, you can do something like this. Just get application icon and draw another image on it. Yes, it does require a bit of calculation to get it into the bottom right corner but nothing that a little math cannot handle.

private static Icon GetAnnotatedIcon(Bitmap annotation) {
var icon = GetApplicationIcon();

if (icon != null) {
var image = icon.ToBitmap();
if (icon != null) {
using (var g = Graphics.FromImage(image)) {
g.DrawImage(annotation, (int)g.VisibleClipBounds.Width - annotation.Width - 2, (int)g.VisibleClipBounds.Height - annotation.Height - 2);
g.Flush();
}
}
return Icon.FromHandle(image.GetHicon());
}
return null;
}

There is only one magic moment above - how to get application icon. Fortunately, a bit of P/Invoke goes a long way.

private static Icon GetApplicationIcon() {
var hLibrary = NativeMethods.LoadLibrary(Assembly.GetEntryAssembly().Location);
if (!hLibrary.Equals(IntPtr.Zero)) {
var hIcon = NativeMethods.LoadImage(hLibrary, "#32512", NativeMethods.IMAGE_ICON, 20, 20, 0);
if (!hIcon.Equals(System.IntPtr.Zero)) {
var icon = Icon.FromHandle(hIcon);
if (icon != null) { return icon; }
}
}
return null;
}

private static class NativeMethods {
public const UInt32 IMAGE_ICON = 1;

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern internal IntPtr LoadImage(IntPtr hInstance, String lpIconName, UInt32 uType, Int32 cxDesired, Int32 cyDesired, UInt32 fuLoad);

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
static extern internal IntPtr LoadLibrary(string lpFileName);
}

And yes, this code doesn't take DPI into account so your high-definition icon might suffer - let's leave that for some other post. :)

Leave a Reply

Your email address will not be published. Required fields are marked *