Fixed Issue 269.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/GUI/NotifyIconAdv.cs Sat Jul 14 19:24:04 2012 +0000
1.3 @@ -0,0 +1,502 @@
1.4 +/*
1.5 +
1.6 + This Source Code Form is subject to the terms of the Mozilla Public
1.7 + License, v. 2.0. If a copy of the MPL was not distributed with this
1.8 + file, You can obtain one at http://mozilla.org/MPL/2.0/.
1.9 +
1.10 + Copyright (C) 2012 Michael Möller <mmoeller@openhardwaremonitor.org>
1.11 +
1.12 +*/
1.13 +
1.14 +using System;
1.15 +using System.ComponentModel;
1.16 +using System.Drawing;
1.17 +using System.Runtime.InteropServices;
1.18 +using System.Reflection;
1.19 +using System.Windows.Forms;
1.20 +
1.21 +namespace OpenHardwareMonitor.GUI {
1.22 + public class NotifyIconAdv : Component {
1.23 +
1.24 + private static int nextId = 0;
1.25 +
1.26 + private object syncObj = new object();
1.27 + private Icon icon;
1.28 + private string text = "";
1.29 + private int id;
1.30 + private bool created;
1.31 + private NotifyIconNativeWindow window;
1.32 + private bool doubleClickDown;
1.33 + private bool visible;
1.34 + private MethodInfo commandDispatch;
1.35 +
1.36 + public event EventHandler BalloonTipClicked;
1.37 + public event EventHandler BalloonTipClosed;
1.38 + public event EventHandler BalloonTipShown;
1.39 + public event EventHandler Click;
1.40 + public event EventHandler DoubleClick;
1.41 + public event MouseEventHandler MouseClick;
1.42 + public event MouseEventHandler MouseDoubleClick;
1.43 + public event MouseEventHandler MouseDown;
1.44 + public event MouseEventHandler MouseMove;
1.45 + public event MouseEventHandler MouseUp;
1.46 +
1.47 + public string BalloonTipText { get; set; }
1.48 + public ToolTipIcon BalloonTipIcon { get; set; }
1.49 + public string BalloonTipTitle { get; set; }
1.50 + public ContextMenu ContextMenu { get; set; }
1.51 + public ContextMenuStrip ContextMenuStrip { get; set; }
1.52 + public object Tag { get; set; }
1.53 +
1.54 + public Icon Icon {
1.55 + get {
1.56 + return icon;
1.57 + }
1.58 + set {
1.59 + if (icon != value) {
1.60 + icon = value;
1.61 + UpdateNotifyIcon(visible);
1.62 + }
1.63 + }
1.64 + }
1.65 +
1.66 + public string Text {
1.67 + get {
1.68 + return text;
1.69 + }
1.70 + set {
1.71 + if (value == null)
1.72 + value = "";
1.73 +
1.74 + if (value.Length > 63)
1.75 + throw new ArgumentOutOfRangeException();
1.76 +
1.77 + if (!value.Equals(text)) {
1.78 + text = value;
1.79 +
1.80 + if (visible)
1.81 + UpdateNotifyIcon(visible);
1.82 + }
1.83 + }
1.84 + }
1.85 +
1.86 + public bool Visible {
1.87 + get {
1.88 + return visible;
1.89 + }
1.90 + set {
1.91 + if (visible != value) {
1.92 + visible = value;
1.93 + UpdateNotifyIcon(visible);
1.94 + }
1.95 + }
1.96 + }
1.97 +
1.98 + public NotifyIconAdv() {
1.99 + BalloonTipText = "";
1.100 + BalloonTipTitle = "";
1.101 +
1.102 + commandDispatch = typeof(Form).Assembly.
1.103 + GetType("System.Windows.Forms.Command").GetMethod("DispatchID",
1.104 + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
1.105 + null, new Type[] { typeof(int) }, null);
1.106 +
1.107 + id = ++NotifyIconAdv.nextId;
1.108 + window = new NotifyIconNativeWindow(this);
1.109 + UpdateNotifyIcon(visible);
1.110 + }
1.111 +
1.112 + protected override void Dispose(bool disposing) {
1.113 + if (disposing) {
1.114 + if (window != null) {
1.115 + icon = null;
1.116 + text = "";
1.117 + UpdateNotifyIcon(false);
1.118 + window.DestroyHandle();
1.119 + window = null;
1.120 + ContextMenu = null;
1.121 + ContextMenuStrip = null;
1.122 + }
1.123 + } else {
1.124 + if (window != null && window.Handle != IntPtr.Zero) {
1.125 + NativeMethods.PostMessage(
1.126 + new HandleRef(window, window.Handle), WM_CLOSE, 0, 0);
1.127 + window.ReleaseHandle();
1.128 + }
1.129 + }
1.130 + base.Dispose(disposing);
1.131 + }
1.132 +
1.133 + public void ShowBalloonTip(int timeout) {
1.134 + ShowBalloonTip(timeout, BalloonTipTitle, BalloonTipText, BalloonTipIcon);
1.135 + }
1.136 +
1.137 + public void ShowBalloonTip(int timeout, string tipTitle, string tipText,
1.138 + ToolTipIcon tipIcon)
1.139 + {
1.140 + if (timeout < 0)
1.141 + throw new ArgumentOutOfRangeException("timeout");
1.142 +
1.143 + if (string.IsNullOrEmpty(tipText))
1.144 + throw new ArgumentException("tipText");
1.145 +
1.146 + if (DesignMode)
1.147 + return;
1.148 +
1.149 + if (created) {
1.150 + NativeMethods.NotifyIconData data = new NativeMethods.NotifyIconData();
1.151 + if (window.Handle == IntPtr.Zero)
1.152 + window.CreateHandle(new CreateParams());
1.153 +
1.154 + data.Window = window.Handle;
1.155 + data.ID = id;
1.156 + data.Flags = NativeMethods.NotifyIconDataFlags.Info;
1.157 + data.TimeoutOrVersion = timeout;
1.158 + data.InfoTitle = tipTitle;
1.159 + data.Info = tipText;
1.160 + data.InfoFlags = (int)tipIcon;
1.161 +
1.162 + NativeMethods.Shell_NotifyIcon(
1.163 + NativeMethods.NotifyIconMessage.Modify, data);
1.164 + }
1.165 + }
1.166 +
1.167 + private void ShowContextMenu() {
1.168 + if (ContextMenu == null && ContextMenuStrip == null)
1.169 + return;
1.170 +
1.171 + NativeMethods.Point p = new NativeMethods.Point();
1.172 + NativeMethods.GetCursorPos(ref p);
1.173 + NativeMethods.SetForegroundWindow(
1.174 + new HandleRef(window, window.Handle));
1.175 +
1.176 + if (ContextMenu != null) {
1.177 + ContextMenu.GetType().InvokeMember("OnPopup",
1.178 + BindingFlags.NonPublic | BindingFlags.InvokeMethod |
1.179 + BindingFlags.Instance, null, ContextMenu,
1.180 + new Object[] { System.EventArgs.Empty });
1.181 +
1.182 + NativeMethods.TrackPopupMenuEx(
1.183 + new HandleRef(ContextMenu, ContextMenu.Handle), 72,
1.184 + p.x, p.y, new HandleRef(window, window.Handle),
1.185 + IntPtr.Zero);
1.186 +
1.187 + NativeMethods.PostMessage(
1.188 + new HandleRef(window, window.Handle), WM_NULL, 0, 0);
1.189 + return;
1.190 + }
1.191 +
1.192 + if (ContextMenuStrip != null)
1.193 + ContextMenuStrip.GetType().InvokeMember("ShowInTaskbar",
1.194 + BindingFlags.NonPublic | BindingFlags.InvokeMethod |
1.195 + BindingFlags.Instance, null, ContextMenuStrip,
1.196 + new Object[] { p.x, p.y });
1.197 + }
1.198 +
1.199 + private void UpdateNotifyIcon(bool showNotifyIcon) {
1.200 + if (DesignMode)
1.201 + return;
1.202 +
1.203 + lock (syncObj) {
1.204 + window.LockReference(showNotifyIcon);
1.205 +
1.206 + NativeMethods.NotifyIconData data = new NativeMethods.NotifyIconData();
1.207 + data.CallbackMessage = WM_TRAYMOUSEMESSAGE;
1.208 + data.Flags = NativeMethods.NotifyIconDataFlags.Message;
1.209 +
1.210 + if (showNotifyIcon && window.Handle == IntPtr.Zero)
1.211 + window.CreateHandle(new CreateParams());
1.212 +
1.213 + data.Window = window.Handle;
1.214 + data.ID = id;
1.215 +
1.216 + if (icon != null) {
1.217 + data.Flags |= NativeMethods.NotifyIconDataFlags.Icon;
1.218 + data.Icon = icon.Handle;
1.219 + }
1.220 +
1.221 + data.Flags |= NativeMethods.NotifyIconDataFlags.Tip;
1.222 + data.Tip = text;
1.223 +
1.224 + if (showNotifyIcon && icon != null) {
1.225 + if (!created) {
1.226 + int i = 0;
1.227 + do {
1.228 + created = NativeMethods.Shell_NotifyIcon(
1.229 + NativeMethods.NotifyIconMessage.Add, data);
1.230 + if (!created) {
1.231 + System.Threading.Thread.Sleep(200);
1.232 + i++;
1.233 + }
1.234 + } while (!created && i < 40);
1.235 + } else {
1.236 + NativeMethods.Shell_NotifyIcon(
1.237 + NativeMethods.NotifyIconMessage.Modify, data);
1.238 + }
1.239 + } else {
1.240 + if (created) {
1.241 + int i = 0;
1.242 + bool deleted = false;
1.243 + do {
1.244 + deleted = NativeMethods.Shell_NotifyIcon(
1.245 + NativeMethods.NotifyIconMessage.Delete, data);
1.246 + if (!deleted) {
1.247 + System.Threading.Thread.Sleep(200);
1.248 + i++;
1.249 + }
1.250 + } while (!deleted && i < 40);
1.251 + created = false;
1.252 + }
1.253 + }
1.254 + }
1.255 + }
1.256 +
1.257 + private void ProcessMouseDown(ref Message message, MouseButtons button,
1.258 + bool doubleClick)
1.259 + {
1.260 + if (doubleClick) {
1.261 + if (DoubleClick != null)
1.262 + DoubleClick(this, new MouseEventArgs(button, 2, 0, 0, 0));
1.263 +
1.264 + if (MouseDoubleClick != null)
1.265 + MouseDoubleClick(this, new MouseEventArgs(button, 2, 0, 0, 0));
1.266 +
1.267 + doubleClickDown = true;
1.268 + }
1.269 +
1.270 + if (MouseDown != null)
1.271 + MouseDown(this,
1.272 + new MouseEventArgs(button, doubleClick ? 2 : 1, 0, 0, 0));
1.273 + }
1.274 +
1.275 + private void ProcessMouseUp(ref Message message, MouseButtons button) {
1.276 + if (MouseUp != null)
1.277 + MouseUp(this, new MouseEventArgs(button, 0, 0, 0, 0));
1.278 +
1.279 + if (!doubleClickDown) {
1.280 + if (Click != null)
1.281 + Click(this, new MouseEventArgs(button, 0, 0, 0, 0));
1.282 +
1.283 + if (MouseClick != null)
1.284 + MouseClick(this, new MouseEventArgs(button, 0, 0, 0, 0));
1.285 + }
1.286 + doubleClickDown = false;
1.287 + }
1.288 +
1.289 + private void ProcessInitMenuPopup(ref Message message) {
1.290 + if (ContextMenu != null &&
1.291 + (bool)ContextMenu.GetType().InvokeMember("ProcessInitMenuPopup",
1.292 + BindingFlags.NonPublic | BindingFlags.InvokeMethod |
1.293 + BindingFlags.Instance, null, ContextMenu,
1.294 + new Object[] { message.WParam })) {
1.295 + return;
1.296 + }
1.297 + window.DefWndProc(ref message);
1.298 + }
1.299 +
1.300 + private void WndProc(ref Message message) {
1.301 + switch (message.Msg) {
1.302 + case WM_DESTROY:
1.303 + UpdateNotifyIcon(false);
1.304 + return;
1.305 + case WM_COMMAND:
1.306 + if (message.LParam != IntPtr.Zero) {
1.307 + window.DefWndProc(ref message);
1.308 + return;
1.309 + }
1.310 + commandDispatch.Invoke(null, new object[] {
1.311 + message.WParam.ToInt32() & 0xFFFF });
1.312 + return;
1.313 + case WM_INITMENUPOPUP:
1.314 + ProcessInitMenuPopup(ref message);
1.315 + return;
1.316 + case WM_TRAYMOUSEMESSAGE:
1.317 + switch ((int)message.LParam) {
1.318 + case WM_MOUSEMOVE:
1.319 + if (MouseMove != null)
1.320 + MouseMove(this,
1.321 + new MouseEventArgs(Control.MouseButtons, 0, 0, 0, 0));
1.322 + return;
1.323 + case WM_LBUTTONDOWN:
1.324 + ProcessMouseDown(ref message, MouseButtons.Left, false);
1.325 + return;
1.326 + case WM_LBUTTONUP:
1.327 + ProcessMouseUp(ref message, MouseButtons.Left);
1.328 + return;
1.329 + case WM_LBUTTONDBLCLK:
1.330 + ProcessMouseDown(ref message, MouseButtons.Left, true);
1.331 + return;
1.332 + case WM_RBUTTONDOWN:
1.333 + ProcessMouseDown(ref message, MouseButtons.Right, false);
1.334 + return;
1.335 + case WM_RBUTTONUP:
1.336 + if (ContextMenu != null || ContextMenuStrip != null)
1.337 + ShowContextMenu();
1.338 + ProcessMouseUp(ref message, MouseButtons.Right);
1.339 + return;
1.340 + case WM_RBUTTONDBLCLK:
1.341 + ProcessMouseDown(ref message, MouseButtons.Right, true);
1.342 + return;
1.343 + case WM_MBUTTONDOWN:
1.344 + ProcessMouseDown(ref message, MouseButtons.Middle, false);
1.345 + return;
1.346 + case WM_MBUTTONUP:
1.347 + ProcessMouseUp(ref message, MouseButtons.Middle);
1.348 + return;
1.349 + case WM_MBUTTONDBLCLK:
1.350 + ProcessMouseDown(ref message, MouseButtons.Middle, true);
1.351 + return;
1.352 + case NIN_BALLOONSHOW:
1.353 + if (BalloonTipShown != null)
1.354 + BalloonTipShown(this, EventArgs.Empty);
1.355 + return;
1.356 + case NIN_BALLOONHIDE:
1.357 + case NIN_BALLOONTIMEOUT:
1.358 + if (BalloonTipClosed != null)
1.359 + BalloonTipClosed(this, EventArgs.Empty);
1.360 + return;
1.361 + case NIN_BALLOONUSERCLICK:
1.362 + if (BalloonTipClicked != null)
1.363 + BalloonTipClicked(this, EventArgs.Empty);
1.364 + return;
1.365 + default:
1.366 + return;
1.367 + }
1.368 + }
1.369 +
1.370 + if (message.Msg == NotifyIconAdv.WM_TASKBARCREATED) {
1.371 + lock (syncObj) {
1.372 + created = false;
1.373 + }
1.374 + UpdateNotifyIcon(visible);
1.375 + }
1.376 +
1.377 + window.DefWndProc(ref message);
1.378 + }
1.379 +
1.380 + private class NotifyIconNativeWindow : NativeWindow {
1.381 + private NotifyIconAdv reference;
1.382 + private GCHandle referenceHandle;
1.383 +
1.384 + internal NotifyIconNativeWindow(NotifyIconAdv component) {
1.385 + this.reference = component;
1.386 + }
1.387 +
1.388 + ~NotifyIconNativeWindow() {
1.389 + if (base.Handle != IntPtr.Zero)
1.390 + NativeMethods.PostMessage(
1.391 + new HandleRef(this, base.Handle), WM_CLOSE, 0, 0);
1.392 + }
1.393 +
1.394 + public void LockReference(bool locked) {
1.395 + if (locked) {
1.396 + if (!referenceHandle.IsAllocated) {
1.397 + referenceHandle = GCHandle.Alloc(reference, GCHandleType.Normal);
1.398 + return;
1.399 + }
1.400 + } else {
1.401 + if (referenceHandle.IsAllocated)
1.402 + referenceHandle.Free();
1.403 + }
1.404 + }
1.405 +
1.406 + protected override void OnThreadException(Exception e) {
1.407 + Application.OnThreadException(e);
1.408 + }
1.409 +
1.410 + protected override void WndProc(ref Message m) {
1.411 + reference.WndProc(ref m);
1.412 + }
1.413 + }
1.414 +
1.415 + private const int WM_NULL = 0x00;
1.416 + private const int WM_DESTROY = 0x02;
1.417 + private const int WM_CLOSE = 0x10;
1.418 + private const int WM_COMMAND = 0x111;
1.419 + private const int WM_INITMENUPOPUP = 0x117;
1.420 + private const int WM_MOUSEMOVE = 0x200;
1.421 + private const int WM_LBUTTONDOWN = 0x201;
1.422 + private const int WM_LBUTTONUP = 0x202;
1.423 + private const int WM_LBUTTONDBLCLK = 0x203;
1.424 + private const int WM_RBUTTONDOWN = 0x204;
1.425 + private const int WM_RBUTTONUP = 0x205;
1.426 + private const int WM_RBUTTONDBLCLK = 0x206;
1.427 + private const int WM_MBUTTONDOWN = 0x207;
1.428 + private const int WM_MBUTTONUP = 0x208;
1.429 + private const int WM_MBUTTONDBLCLK = 0x209;
1.430 + private const int WM_TRAYMOUSEMESSAGE = 0x800;
1.431 +
1.432 + private const int NIN_BALLOONSHOW = 0x402;
1.433 + private const int NIN_BALLOONHIDE = 0x403;
1.434 + private const int NIN_BALLOONTIMEOUT = 0x404;
1.435 + private const int NIN_BALLOONUSERCLICK = 0x405;
1.436 +
1.437 + private static int WM_TASKBARCREATED =
1.438 + NativeMethods.RegisterWindowMessage("TaskbarCreated");
1.439 +
1.440 + private static class NativeMethods {
1.441 + [DllImport("user32.dll", CharSet = CharSet.Auto)]
1.442 + public static extern IntPtr PostMessage(HandleRef hwnd, int msg,
1.443 + int wparam, int lparam);
1.444 +
1.445 + [DllImport("user32.dll", CharSet = CharSet.Auto)]
1.446 + public static extern int RegisterWindowMessage(string msg);
1.447 +
1.448 + [Flags]
1.449 + public enum NotifyIconDataFlags : int {
1.450 + Message = 0x1,
1.451 + Icon = 0x2,
1.452 + Tip = 0x4,
1.453 + State = 0x8,
1.454 + Info = 0x10
1.455 + }
1.456 +
1.457 + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
1.458 + public class NotifyIconData {
1.459 + private int Size = Marshal.SizeOf(typeof(NotifyIconData));
1.460 + public IntPtr Window;
1.461 + public int ID;
1.462 + public NotifyIconDataFlags Flags;
1.463 + public int CallbackMessage;
1.464 + public IntPtr Icon;
1.465 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
1.466 + public string Tip;
1.467 + public int State;
1.468 + public int StateMask;
1.469 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
1.470 + public string Info;
1.471 + public int TimeoutOrVersion;
1.472 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
1.473 + public string InfoTitle;
1.474 + public int InfoFlags;
1.475 + }
1.476 +
1.477 + public enum NotifyIconMessage : int {
1.478 + Add = 0x0,
1.479 + Modify = 0x1,
1.480 + Delete = 0x2
1.481 + }
1.482 +
1.483 + [DllImport("shell32.dll", CharSet = CharSet.Auto)]
1.484 + [return: MarshalAs(UnmanagedType.Bool)]
1.485 + public static extern bool Shell_NotifyIcon(NotifyIconMessage message,
1.486 + NotifyIconData pnid);
1.487 +
1.488 + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
1.489 + public static extern bool TrackPopupMenuEx(HandleRef hmenu, int fuFlags,
1.490 + int x, int y, HandleRef hwnd, IntPtr tpm);
1.491 +
1.492 + [StructLayout(LayoutKind.Sequential)]
1.493 + public struct Point {
1.494 + public int x;
1.495 + public int y;
1.496 + }
1.497 +
1.498 + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
1.499 + public static extern bool GetCursorPos(ref Point point);
1.500 +
1.501 + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
1.502 + public static extern bool SetForegroundWindow(HandleRef hWnd);
1.503 + }
1.504 + }
1.505 +}
2.1 --- a/GUI/SensorNotifyIcon.cs Thu Jul 12 10:17:18 2012 +0000
2.2 +++ b/GUI/SensorNotifyIcon.cs Sat Jul 14 19:24:04 2012 +0000
2.3 @@ -24,7 +24,7 @@
2.4 private UnitManager unitManager;
2.5
2.6 private ISensor sensor;
2.7 - private NotifyIcon notifyIcon;
2.8 + private NotifyIconAdv notifyIcon;
2.9 private Bitmap bitmap;
2.10 private Graphics graphics;
2.11 private Color color;
2.12 @@ -40,7 +40,7 @@
2.13 {
2.14 this.unitManager = unitManager;
2.15 this.sensor = sensor;
2.16 - this.notifyIcon = new NotifyIcon();
2.17 + this.notifyIcon = new NotifyIconAdv();
2.18
2.19 Color defaultColor = Color.Black;
2.20 if (sensor.SensorType == SensorType.Load ||
3.1 --- a/GUI/SystemTray.cs Thu Jul 12 10:17:18 2012 +0000
3.2 +++ b/GUI/SystemTray.cs Sat Jul 14 19:24:04 2012 +0000
3.3 @@ -23,7 +23,7 @@
3.4 private UnitManager unitManager;
3.5 private List<SensorNotifyIcon> list = new List<SensorNotifyIcon>();
3.6 private bool mainIconEnabled = false;
3.7 - private NotifyIcon mainIcon;
3.8 + private NotifyIconAdv mainIcon;
3.9
3.10 public SystemTray(IComputer computer, PersistentSettings settings,
3.11 UnitManager unitManager)
3.12 @@ -34,7 +34,7 @@
3.13 computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
3.14 computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);
3.15
3.16 - this.mainIcon = new NotifyIcon();
3.17 + this.mainIcon = new NotifyIconAdv();
3.18
3.19 ContextMenu contextMenu = new ContextMenu();
3.20 MenuItem hideShowItem = new MenuItem("Hide/Show");
4.1 --- a/OpenHardwareMonitor.csproj Thu Jul 12 10:17:18 2012 +0000
4.2 +++ b/OpenHardwareMonitor.csproj Sat Jul 14 19:24:04 2012 +0000
4.3 @@ -73,6 +73,9 @@
4.4 <Compile Include="GUI\GadgetWindow.cs" />
4.5 <Compile Include="GUI\Gadget.cs" />
4.6 <Compile Include="GUI\HardwareTypeImage.cs" />
4.7 + <Compile Include="GUI\NotifyIconAdv.cs">
4.8 + <SubType>Component</SubType>
4.9 + </Compile>
4.10 <Compile Include="GUI\PlotPanel.cs">
4.11 <SubType>UserControl</SubType>
4.12 </Compile>