From 4fc0a4960e4ecacc753a9fe164f462e1679bcd56 Mon Sep 17 00:00:00 2001 From: Robin Krom Date: Fri, 19 Jul 2019 14:05:13 +0200 Subject: [PATCH] Removing some of the "magic numbers" (#10) * Modified the WindowMessageSink to use an enum where possible. * I managed to find more of the windows messages, and it was worth it. I expect that there is a bug, while currently two events aren't handled: WM_CONTEXTMENU & NIN_SELECT. Why can be read here: https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw. Also added NIN_KEYSELECT * A small stability fix, although it most likely wouldn't happen that the MouseEventReceived isn't assigned it's done so in a different location and might hit us somewhere in the future. I also removed unneeded assignments. --- .../Interop/NotifyIconVersion.cs | 4 +- .../Interop/WindowMessageSink.cs | 82 ++++---- .../NotifyIconWpf/Interop/WindowsMessages.cs | 190 ++++++++++++++++++ 3 files changed, 239 insertions(+), 37 deletions(-) create mode 100644 Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/WindowsMessages.cs diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/NotifyIconVersion.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/NotifyIconVersion.cs index 5e8a436..66fb482 100644 --- a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/NotifyIconVersion.cs +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/NotifyIconVersion.cs @@ -19,8 +19,8 @@ Win2000 = 0x3, /// - /// Extended tooltip support, which is available - /// for Vista and later. + /// Extended tooltip support, which is available for Vista and later. + /// Detailed information about what the different versions do, can be found here /// Vista = 0x4 } diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/WindowMessageSink.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/WindowMessageSink.cs index 121a256..19b5eac 100644 --- a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/WindowMessageSink.cs +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/WindowMessageSink.cs @@ -225,76 +225,89 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop { if (msg != CallbackMessageId) return; - switch (lParam.ToInt32()) + var message = (WindowsMessages) lParam.ToInt32(); + Debug.WriteLine("Got message " + message); + switch (message) { - case 0x200: - MouseEventReceived(MouseEvent.MouseMove); + case WindowsMessages.WM_CONTEXTMENU: + // TODO: Handle WM_CONTEXTMENU, see https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw + Debug.WriteLine("Unhandled WM_CONTEXTMENU"); break; - case 0x201: - MouseEventReceived(MouseEvent.IconLeftMouseDown); + case WindowsMessages.WM_MOUSEMOVE: + MouseEventReceived?.Invoke(MouseEvent.MouseMove); break; - case 0x202: + case WindowsMessages.WM_LBUTTONDOWN: + MouseEventReceived?.Invoke(MouseEvent.IconLeftMouseDown); + break; + + case WindowsMessages.WM_LBUTTONUP: if (!isDoubleClick) { - MouseEventReceived(MouseEvent.IconLeftMouseUp); + MouseEventReceived?.Invoke(MouseEvent.IconLeftMouseUp); } isDoubleClick = false; break; - case 0x203: + case WindowsMessages.WM_LBUTTONDBLCLK: isDoubleClick = true; - MouseEventReceived(MouseEvent.IconDoubleClick); + MouseEventReceived?.Invoke(MouseEvent.IconDoubleClick); break; - case 0x204: - MouseEventReceived(MouseEvent.IconRightMouseDown); + case WindowsMessages.WM_RBUTTONDOWN: + MouseEventReceived?.Invoke(MouseEvent.IconRightMouseDown); break; - case 0x205: - MouseEventReceived(MouseEvent.IconRightMouseUp); + case WindowsMessages.WM_RBUTTONUP: + MouseEventReceived?.Invoke(MouseEvent.IconRightMouseUp); break; - case 0x206: + case WindowsMessages.WM_RBUTTONDBLCLK: //double click with right mouse button - do not trigger event break; - case 0x207: - MouseEventReceived(MouseEvent.IconMiddleMouseDown); + case WindowsMessages.WM_MBUTTONDOWN: + MouseEventReceived?.Invoke(MouseEvent.IconMiddleMouseDown); break; - case 520: - MouseEventReceived(MouseEvent.IconMiddleMouseUp); + case WindowsMessages.WM_MBUTTONUP: + MouseEventReceived?.Invoke(MouseEvent.IconMiddleMouseUp); break; - case 0x209: + case WindowsMessages.WM_MBUTTONDBLCLK: //double click with middle mouse button - do not trigger event break; - case 0x402: - var listener = BalloonToolTipChanged; - listener?.Invoke(true); + case WindowsMessages.NIN_BALLOONSHOW: + BalloonToolTipChanged?.Invoke(true); break; - case 0x403: - case 0x404: - listener = BalloonToolTipChanged; - listener?.Invoke(false); + case WindowsMessages.NIN_BALLOONHIDE: + case WindowsMessages.NIN_BALLOONTIMEOUT: + BalloonToolTipChanged?.Invoke(false); break; - case 0x405: - MouseEventReceived(MouseEvent.BalloonToolTipClicked); + case WindowsMessages.NIN_BALLOONUSERCLICK: + MouseEventReceived?.Invoke(MouseEvent.BalloonToolTipClicked); break; - case 0x406: - listener = ChangeToolTipStateRequest; - listener?.Invoke(true); + case WindowsMessages.NIN_POPUPOPEN: + ChangeToolTipStateRequest?.Invoke(true); break; - case 0x407: - listener = ChangeToolTipStateRequest; - listener?.Invoke(false); + case WindowsMessages.NIN_POPUPCLOSE: + ChangeToolTipStateRequest?.Invoke(false); + break; + + case WindowsMessages.NIN_SELECT: + // TODO: Handle NIN_SELECT see https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw + Debug.WriteLine("Unhandled NIN_SELECT"); + break; + + case WindowsMessages.NIN_KEYSELECT: + // TODO: Handle NIN_KEYSELECT see https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw + Debug.WriteLine("Unhandled NIN_KEYSELECT"); break; default: @@ -345,7 +358,6 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop Dispose(false); } - /// /// Removes the windows hook that receives window /// messages and closes the underlying helper window. diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/WindowsMessages.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/WindowsMessages.cs new file mode 100644 index 0000000..2d4b8ec --- /dev/null +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/WindowsMessages.cs @@ -0,0 +1,190 @@ +// hardcodet.net NotifyIcon for WPF +// Copyright (c) 2009 - 2013 Philipp Sumi +// Contact and Information: http://www.hardcodet.net +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the Code Project Open License (CPOL); +// either version 1.0 of the License, or (at your option) any later +// version. +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE + +// ReSharper disable InconsistentNaming + +using System.Diagnostics.CodeAnalysis; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// This enum defines the windows messages we respond to. + /// See more on Windows messages here + /// + [SuppressMessage("ReSharper", "IdentifierTypo")] + public enum WindowsMessages : uint + { + /// + /// Notifies a window that the user clicked the right mouse button (right-clicked) in the window. + /// See WM_CONTEXTMENU message + /// + /// In case of a notify icon: + /// If a user selects a notify icon's shortcut menu with the keyboard, the Shell now sends the associated application a WM_CONTEXTMENU message. Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages. + /// See Shell_NotifyIcon function + /// + WM_CONTEXTMENU = 0x007b, + + /// + /// Posted to a window when the cursor moves. + /// If the mouse is not captured, the message is posted to the window that contains the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_MOUSEMOVE message + /// + WM_MOUSEMOVE = 0x0200, + + /// + /// Posted when the user presses the left mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_LBUTTONDOWN message + /// + WM_LBUTTONDOWN = 0x0201, + + /// + /// Posted when the user releases the left mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_LBUTTONUP message + /// + WM_LBUTTONUP = 0x0202, + + /// + /// Posted when the user double-clicks the left mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_LBUTTONDBLCLK message + /// + WM_LBUTTONDBLCLK = 0x0203, + + /// + /// Posted when the user presses the right mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_RBUTTONDOWN message + /// + WM_RBUTTONDOWN = 0x0204, + + /// + /// Posted when the user releases the right mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_RBUTTONUP message + /// + WM_RBUTTONUP = 0x0205, + + /// + /// Posted when the user double-clicks the right mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_RBUTTONDBLCLK message + /// + WM_RBUTTONDBLCLK = 0x0206, + + /// + /// Posted when the user presses the middle mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_MBUTTONDOWN message + /// + WM_MBUTTONDOWN = 0x0207, + + /// + /// Posted when the user releases the middle mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_MBUTTONUP message + /// + WM_MBUTTONUP = 0x0208, + + /// + /// Posted when the user double-clicks the middle mouse button while the cursor is in the client area of a window. + /// If the mouse is not captured, the message is posted to the window beneath the cursor. + /// Otherwise, the message is posted to the window that has captured the mouse. + /// + /// See WM_MBUTTONDBLCLK message + /// + WM_MBUTTONDBLCLK = 0x0209, + + /// + /// Used to define private messages for use by private window classes, usually of the form WM_USER+x, where x is an integer value. + /// + WM_USER = 0x0400, + + /// + /// This message is only send when using NOTIFYICON_VERSION_4, the Shell now sends the associated application an NIN_SELECT notification. + /// Send when a notify icon is activated with mouse or ENTER key. + /// Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages. + /// + NIN_SELECT = WM_USER, + + /// + /// This message is only send when using NOTIFYICON_VERSION_4, the Shell now sends the associated application an NIN_SELECT notification. + /// Send when a notify icon is activated with SPACEBAR or ENTER key. + /// Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages. + /// + NIN_KEYSELECT = WM_USER + 1, + + /// + /// Sent when the balloon is shown (balloons are queued). + /// + NIN_BALLOONSHOW = WM_USER + 2, + + /// + /// Sent when the balloon disappears. For example, when the icon is deleted. + /// This message is not sent if the balloon is dismissed because of a timeout or if the user clicks the mouse. + /// + /// As of Windows 7, NIN_BALLOONHIDE is also sent when a notification with the NIIF_RESPECT_QUIET_TIME flag set attempts to display during quiet time (a user's first hour on a new computer). + /// In that case, the balloon is never displayed at all. + /// + NIN_BALLOONHIDE = WM_USER + 3, + + /// + /// Sent when the balloon is dismissed because of a timeout. + /// + NIN_BALLOONTIMEOUT = WM_USER + 4, + + /// + /// Sent when the balloon is dismissed because the user clicked the mouse. + /// + NIN_BALLOONUSERCLICK = WM_USER + 5, + + /// + /// Sent when the user hovers the cursor over an icon to indicate that the richer pop-up UI should be used in place of a standard textual tooltip. + /// + NIN_POPUPOPEN = WM_USER + 6, + + /// + /// Sent when a cursor no longer hovers over an icon to indicate that the rich pop-up UI should be closed. + /// + NIN_POPUPCLOSE = WM_USER + 7 + } +}