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. :)