Statistics
| Branch: | Revision:

root / trunk / NotifyIconWpf / Interop / WindowMessageSink.cs @ 7fcbf914

History | View | Annotate | Download (11.9 kB)

1 e7fb3ef2 George Pantazis
// hardcodet.net NotifyIcon for WPF
2 7fcbf914 George Pantazis
// Copyright (c) 2009 - 2013 Philipp Sumi
3 e7fb3ef2 George Pantazis
// Contact and Information: http://www.hardcodet.net
4 e7fb3ef2 George Pantazis
//
5 e7fb3ef2 George Pantazis
// This library is free software; you can redistribute it and/or
6 e7fb3ef2 George Pantazis
// modify it under the terms of the Code Project Open License (CPOL);
7 e7fb3ef2 George Pantazis
// either version 1.0 of the License, or (at your option) any later
8 e7fb3ef2 George Pantazis
// version.
9 e7fb3ef2 George Pantazis
// 
10 e7fb3ef2 George Pantazis
// The above copyright notice and this permission notice shall be
11 e7fb3ef2 George Pantazis
// included in all copies or substantial portions of the Software.
12 e7fb3ef2 George Pantazis
// 
13 e7fb3ef2 George Pantazis
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 e7fb3ef2 George Pantazis
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15 e7fb3ef2 George Pantazis
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16 e7fb3ef2 George Pantazis
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17 e7fb3ef2 George Pantazis
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 e7fb3ef2 George Pantazis
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 e7fb3ef2 George Pantazis
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 e7fb3ef2 George Pantazis
// OTHER DEALINGS IN THE SOFTWARE.
21 e7fb3ef2 George Pantazis
//
22 e7fb3ef2 George Pantazis
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE
23 e7fb3ef2 George Pantazis
24 e7fb3ef2 George Pantazis
25 e7fb3ef2 George Pantazis
using System;
26 e7fb3ef2 George Pantazis
using System.ComponentModel;
27 800fd841 pkanavos
using System.Diagnostics;
28 800fd841 pkanavos
using System.Runtime.InteropServices;
29 800fd841 pkanavos
30 e7fb3ef2 George Pantazis
namespace Hardcodet.Wpf.TaskbarNotification.Interop
31 e7fb3ef2 George Pantazis
{
32 e7fb3ef2 George Pantazis
    /// <summary>
33 7fcbf914 George Pantazis
    /// Receives messages from the taskbar icon through
34 7fcbf914 George Pantazis
    /// window messages of an underlying helper window.
35 e7fb3ef2 George Pantazis
    /// </summary>
36 7fcbf914 George Pantazis
    public class WindowMessageSink : IDisposable
37 e7fb3ef2 George Pantazis
    {
38 7fcbf914 George Pantazis
        #region members
39 7fcbf914 George Pantazis
40 7fcbf914 George Pantazis
        /// <summary>
41 7fcbf914 George Pantazis
        /// The ID of messages that are received from the the
42 7fcbf914 George Pantazis
        /// taskbar icon.
43 7fcbf914 George Pantazis
        /// </summary>
44 7fcbf914 George Pantazis
        public const int CallbackMessageId = 0x400;
45 7fcbf914 George Pantazis
46 7fcbf914 George Pantazis
        /// <summary>
47 7fcbf914 George Pantazis
        /// The ID of the message that is being received if the
48 7fcbf914 George Pantazis
        /// taskbar is (re)started.
49 7fcbf914 George Pantazis
        /// </summary>
50 7fcbf914 George Pantazis
        private uint taskbarRestartMessageId;
51 7fcbf914 George Pantazis
52 7fcbf914 George Pantazis
        /// <summary>
53 7fcbf914 George Pantazis
        /// Used to track whether a mouse-up event is just
54 7fcbf914 George Pantazis
        /// the aftermath of a double-click and therefore needs
55 7fcbf914 George Pantazis
        /// to be suppressed.
56 7fcbf914 George Pantazis
        /// </summary>
57 7fcbf914 George Pantazis
        private bool isDoubleClick;
58 7fcbf914 George Pantazis
59 7fcbf914 George Pantazis
        /// <summary>
60 7fcbf914 George Pantazis
        /// A delegate that processes messages of the hidden
61 7fcbf914 George Pantazis
        /// native window that receives window messages. Storing
62 7fcbf914 George Pantazis
        /// this reference makes sure we don't loose our reference
63 7fcbf914 George Pantazis
        /// to the message window.
64 7fcbf914 George Pantazis
        /// </summary>
65 7fcbf914 George Pantazis
        private WindowProcedureHandler messageHandler;
66 7fcbf914 George Pantazis
67 7fcbf914 George Pantazis
        /// <summary>
68 7fcbf914 George Pantazis
        /// Window class ID.
69 7fcbf914 George Pantazis
        /// </summary>
70 7fcbf914 George Pantazis
        internal string WindowId { get; private set; }
71 7fcbf914 George Pantazis
72 7fcbf914 George Pantazis
        /// <summary>
73 7fcbf914 George Pantazis
        /// Handle for the message window.
74 7fcbf914 George Pantazis
        /// </summary> 
75 7fcbf914 George Pantazis
        internal IntPtr MessageWindowHandle { get; private set; }
76 7fcbf914 George Pantazis
77 7fcbf914 George Pantazis
        /// <summary>
78 7fcbf914 George Pantazis
        /// The version of the underlying icon. Defines how
79 7fcbf914 George Pantazis
        /// incoming messages are interpreted.
80 7fcbf914 George Pantazis
        /// </summary>
81 7fcbf914 George Pantazis
        public NotifyIconVersion Version { get; set; }
82 7fcbf914 George Pantazis
83 7fcbf914 George Pantazis
        #endregion
84 7fcbf914 George Pantazis
85 7fcbf914 George Pantazis
        #region events
86 7fcbf914 George Pantazis
87 7fcbf914 George Pantazis
        /// <summary>
88 7fcbf914 George Pantazis
        /// The custom tooltip should be closed or hidden.
89 7fcbf914 George Pantazis
        /// </summary>
90 7fcbf914 George Pantazis
        public event Action<bool> ChangeToolTipStateRequest;
91 7fcbf914 George Pantazis
92 7fcbf914 George Pantazis
        /// <summary>
93 7fcbf914 George Pantazis
        /// Fired in case the user clicked or moved within
94 7fcbf914 George Pantazis
        /// the taskbar icon area.
95 7fcbf914 George Pantazis
        /// </summary>
96 7fcbf914 George Pantazis
        public event Action<MouseEvent> MouseEventReceived;
97 7fcbf914 George Pantazis
98 7fcbf914 George Pantazis
        /// <summary>
99 7fcbf914 George Pantazis
        /// Fired if a balloon ToolTip was either displayed
100 7fcbf914 George Pantazis
        /// or closed (indicated by the boolean flag).
101 7fcbf914 George Pantazis
        /// </summary>
102 7fcbf914 George Pantazis
        public event Action<bool> BalloonToolTipChanged;
103 7fcbf914 George Pantazis
104 7fcbf914 George Pantazis
        /// <summary>
105 7fcbf914 George Pantazis
        /// Fired if the taskbar was created or restarted. Requires the taskbar
106 7fcbf914 George Pantazis
        /// icon to be reset.
107 7fcbf914 George Pantazis
        /// </summary>
108 7fcbf914 George Pantazis
        public event Action TaskbarCreated;
109 7fcbf914 George Pantazis
110 7fcbf914 George Pantazis
        #endregion
111 7fcbf914 George Pantazis
112 7fcbf914 George Pantazis
        #region construction
113 7fcbf914 George Pantazis
114 7fcbf914 George Pantazis
        /// <summary>
115 7fcbf914 George Pantazis
        /// Creates a new message sink that receives message from
116 7fcbf914 George Pantazis
        /// a given taskbar icon.
117 7fcbf914 George Pantazis
        /// </summary>
118 7fcbf914 George Pantazis
        /// <param name="version"></param>
119 7fcbf914 George Pantazis
        public WindowMessageSink(NotifyIconVersion version)
120 7fcbf914 George Pantazis
        {
121 7fcbf914 George Pantazis
            Version = version;
122 7fcbf914 George Pantazis
            CreateMessageWindow();
123 7fcbf914 George Pantazis
        }
124 7fcbf914 George Pantazis
125 7fcbf914 George Pantazis
126 7fcbf914 George Pantazis
        private WindowMessageSink()
127 7fcbf914 George Pantazis
        {
128 7fcbf914 George Pantazis
        }
129 7fcbf914 George Pantazis
130 7fcbf914 George Pantazis
131 7fcbf914 George Pantazis
        /// <summary>
132 7fcbf914 George Pantazis
        /// Creates a dummy instance that provides an empty
133 7fcbf914 George Pantazis
        /// pointer rather than a real window handler.<br/>
134 7fcbf914 George Pantazis
        /// Used at design time.
135 7fcbf914 George Pantazis
        /// </summary>
136 7fcbf914 George Pantazis
        /// <returns></returns>
137 7fcbf914 George Pantazis
        internal static WindowMessageSink CreateEmpty()
138 7fcbf914 George Pantazis
        {
139 7fcbf914 George Pantazis
            return new WindowMessageSink
140 7fcbf914 George Pantazis
            {
141 7fcbf914 George Pantazis
                MessageWindowHandle = IntPtr.Zero,
142 7fcbf914 George Pantazis
                Version = NotifyIconVersion.Vista
143 7fcbf914 George Pantazis
            };
144 7fcbf914 George Pantazis
        }
145 7fcbf914 George Pantazis
146 7fcbf914 George Pantazis
        #endregion
147 7fcbf914 George Pantazis
148 7fcbf914 George Pantazis
        #region CreateMessageWindow
149 7fcbf914 George Pantazis
150 7fcbf914 George Pantazis
        /// <summary>
151 7fcbf914 George Pantazis
        /// Creates the helper message window that is used
152 7fcbf914 George Pantazis
        /// to receive messages from the taskbar icon.
153 7fcbf914 George Pantazis
        /// </summary>
154 7fcbf914 George Pantazis
        private void CreateMessageWindow()
155 7fcbf914 George Pantazis
        {
156 7fcbf914 George Pantazis
            //generate a unique ID for the window
157 7fcbf914 George Pantazis
            WindowId = "WPFTaskbarIcon_" + DateTime.Now.Ticks;
158 7fcbf914 George Pantazis
159 7fcbf914 George Pantazis
            //register window message handler
160 7fcbf914 George Pantazis
            messageHandler = OnWindowMessageReceived;
161 7fcbf914 George Pantazis
162 7fcbf914 George Pantazis
            // Create a simple window class which is reference through
163 7fcbf914 George Pantazis
            //the messageHandler delegate
164 7fcbf914 George Pantazis
            WindowClass wc;
165 7fcbf914 George Pantazis
166 7fcbf914 George Pantazis
            wc.style = 0;
167 7fcbf914 George Pantazis
            wc.lpfnWndProc = messageHandler;
168 7fcbf914 George Pantazis
            wc.cbClsExtra = 0;
169 7fcbf914 George Pantazis
            wc.cbWndExtra = 0;
170 7fcbf914 George Pantazis
            wc.hInstance = IntPtr.Zero;
171 7fcbf914 George Pantazis
            wc.hIcon = IntPtr.Zero;
172 7fcbf914 George Pantazis
            wc.hCursor = IntPtr.Zero;
173 7fcbf914 George Pantazis
            wc.hbrBackground = IntPtr.Zero;
174 7fcbf914 George Pantazis
            wc.lpszMenuName = "";
175 7fcbf914 George Pantazis
            wc.lpszClassName = WindowId;
176 7fcbf914 George Pantazis
177 7fcbf914 George Pantazis
            // Register the window class
178 7fcbf914 George Pantazis
            WinApi.RegisterClass(ref wc);
179 7fcbf914 George Pantazis
180 7fcbf914 George Pantazis
            // Get the message used to indicate the taskbar has been restarted
181 7fcbf914 George Pantazis
            // This is used to re-add icons when the taskbar restarts
182 7fcbf914 George Pantazis
            taskbarRestartMessageId = WinApi.RegisterWindowMessage("TaskbarCreated");
183 7fcbf914 George Pantazis
184 7fcbf914 George Pantazis
            // Create the message window
185 7fcbf914 George Pantazis
            MessageWindowHandle = WinApi.CreateWindowEx(0, WindowId, "", 0, 0, 0, 1, 1, IntPtr.Zero, IntPtr.Zero,
186 7fcbf914 George Pantazis
                IntPtr.Zero, IntPtr.Zero);
187 7fcbf914 George Pantazis
188 7fcbf914 George Pantazis
            if (MessageWindowHandle == IntPtr.Zero)
189 7fcbf914 George Pantazis
            {
190 7fcbf914 George Pantazis
#if SILVERLIGHT
191 7fcbf914 George Pantazis
      	throw new Exception("Message window handle was not a valid pointer.");
192 7fcbf914 George Pantazis
#else
193 7fcbf914 George Pantazis
                throw new Win32Exception("Message window handle was not a valid pointer");
194 7fcbf914 George Pantazis
#endif
195 7fcbf914 George Pantazis
            }
196 7fcbf914 George Pantazis
        }
197 7fcbf914 George Pantazis
198 7fcbf914 George Pantazis
        #endregion
199 7fcbf914 George Pantazis
200 7fcbf914 George Pantazis
        #region Handle Window Messages
201 7fcbf914 George Pantazis
202 7fcbf914 George Pantazis
        /// <summary>
203 7fcbf914 George Pantazis
        /// Callback method that receives messages from the taskbar area.
204 7fcbf914 George Pantazis
        /// </summary>
205 7fcbf914 George Pantazis
        private IntPtr OnWindowMessageReceived(IntPtr hwnd, uint messageId, IntPtr wparam, IntPtr lparam)
206 7fcbf914 George Pantazis
        {
207 7fcbf914 George Pantazis
            if (messageId == taskbarRestartMessageId)
208 7fcbf914 George Pantazis
            {
209 7fcbf914 George Pantazis
                //recreate the icon if the taskbar was restarted (e.g. due to Win Explorer shutdown)
210 7fcbf914 George Pantazis
                TaskbarCreated();
211 7fcbf914 George Pantazis
            }
212 7fcbf914 George Pantazis
213 7fcbf914 George Pantazis
            //forward message
214 7fcbf914 George Pantazis
            ProcessWindowMessage(messageId, wparam, lparam);
215 7fcbf914 George Pantazis
216 7fcbf914 George Pantazis
            // Pass the message to the default window procedure
217 7fcbf914 George Pantazis
            return WinApi.DefWindowProc(hwnd, messageId, wparam, lparam);
218 7fcbf914 George Pantazis
        }
219 7fcbf914 George Pantazis
220 7fcbf914 George Pantazis
221 7fcbf914 George Pantazis
        /// <summary>
222 7fcbf914 George Pantazis
        /// Processes incoming system messages.
223 7fcbf914 George Pantazis
        /// </summary>
224 7fcbf914 George Pantazis
        /// <param name="msg">Callback ID.</param>
225 7fcbf914 George Pantazis
        /// <param name="wParam">If the version is <see cref="NotifyIconVersion.Vista"/>
226 7fcbf914 George Pantazis
        /// or higher, this parameter can be used to resolve mouse coordinates.
227 7fcbf914 George Pantazis
        /// Currently not in use.</param>
228 7fcbf914 George Pantazis
        /// <param name="lParam">Provides information about the event.</param>
229 7fcbf914 George Pantazis
        private void ProcessWindowMessage(uint msg, IntPtr wParam, IntPtr lParam)
230 7fcbf914 George Pantazis
        {
231 7fcbf914 George Pantazis
            if (msg != CallbackMessageId) return;
232 7fcbf914 George Pantazis
233 7fcbf914 George Pantazis
            switch (lParam.ToInt32())
234 7fcbf914 George Pantazis
            {
235 7fcbf914 George Pantazis
                case 0x200:
236 7fcbf914 George Pantazis
                    MouseEventReceived(MouseEvent.MouseMove);
237 7fcbf914 George Pantazis
                    break;
238 7fcbf914 George Pantazis
239 7fcbf914 George Pantazis
                case 0x201:
240 7fcbf914 George Pantazis
                    MouseEventReceived(MouseEvent.IconLeftMouseDown);
241 7fcbf914 George Pantazis
                    break;
242 7fcbf914 George Pantazis
243 7fcbf914 George Pantazis
                case 0x202:
244 7fcbf914 George Pantazis
                    if (!isDoubleClick)
245 7fcbf914 George Pantazis
                    {
246 7fcbf914 George Pantazis
                        MouseEventReceived(MouseEvent.IconLeftMouseUp);
247 7fcbf914 George Pantazis
                    }
248 7fcbf914 George Pantazis
                    isDoubleClick = false;
249 7fcbf914 George Pantazis
                    break;
250 7fcbf914 George Pantazis
251 7fcbf914 George Pantazis
                case 0x203:
252 7fcbf914 George Pantazis
                    isDoubleClick = true;
253 7fcbf914 George Pantazis
                    MouseEventReceived(MouseEvent.IconDoubleClick);
254 7fcbf914 George Pantazis
                    break;
255 7fcbf914 George Pantazis
256 7fcbf914 George Pantazis
                case 0x204:
257 7fcbf914 George Pantazis
                    MouseEventReceived(MouseEvent.IconRightMouseDown);
258 7fcbf914 George Pantazis
                    break;
259 7fcbf914 George Pantazis
260 7fcbf914 George Pantazis
                case 0x205:
261 7fcbf914 George Pantazis
                    MouseEventReceived(MouseEvent.IconRightMouseUp);
262 7fcbf914 George Pantazis
                    break;
263 7fcbf914 George Pantazis
264 7fcbf914 George Pantazis
                case 0x206:
265 7fcbf914 George Pantazis
                    //double click with right mouse button - do not trigger event
266 7fcbf914 George Pantazis
                    break;
267 7fcbf914 George Pantazis
268 7fcbf914 George Pantazis
                case 0x207:
269 7fcbf914 George Pantazis
                    MouseEventReceived(MouseEvent.IconMiddleMouseDown);
270 7fcbf914 George Pantazis
                    break;
271 7fcbf914 George Pantazis
272 7fcbf914 George Pantazis
                case 520:
273 7fcbf914 George Pantazis
                    MouseEventReceived(MouseEvent.IconMiddleMouseUp);
274 7fcbf914 George Pantazis
                    break;
275 7fcbf914 George Pantazis
276 7fcbf914 George Pantazis
                case 0x209:
277 7fcbf914 George Pantazis
                    //double click with middle mouse button - do not trigger event
278 7fcbf914 George Pantazis
                    break;
279 7fcbf914 George Pantazis
280 7fcbf914 George Pantazis
                case 0x402:
281 7fcbf914 George Pantazis
                    BalloonToolTipChanged(true);
282 7fcbf914 George Pantazis
                    break;
283 7fcbf914 George Pantazis
284 7fcbf914 George Pantazis
                case 0x403:
285 7fcbf914 George Pantazis
                case 0x404:
286 7fcbf914 George Pantazis
                    BalloonToolTipChanged(false);
287 7fcbf914 George Pantazis
                    break;
288 7fcbf914 George Pantazis
289 7fcbf914 George Pantazis
                case 0x405:
290 7fcbf914 George Pantazis
                    MouseEventReceived(MouseEvent.BalloonToolTipClicked);
291 7fcbf914 George Pantazis
                    break;
292 7fcbf914 George Pantazis
293 7fcbf914 George Pantazis
                case 0x406:
294 7fcbf914 George Pantazis
                    ChangeToolTipStateRequest(true);
295 7fcbf914 George Pantazis
                    break;
296 7fcbf914 George Pantazis
297 7fcbf914 George Pantazis
                case 0x407:
298 7fcbf914 George Pantazis
                    ChangeToolTipStateRequest(false);
299 7fcbf914 George Pantazis
                    break;
300 7fcbf914 George Pantazis
301 7fcbf914 George Pantazis
                default:
302 7fcbf914 George Pantazis
                    Debug.WriteLine("Unhandled NotifyIcon message ID: " + lParam);
303 7fcbf914 George Pantazis
                    break;
304 7fcbf914 George Pantazis
            }
305 7fcbf914 George Pantazis
        }
306 7fcbf914 George Pantazis
307 7fcbf914 George Pantazis
        #endregion
308 7fcbf914 George Pantazis
309 7fcbf914 George Pantazis
        #region Dispose
310 7fcbf914 George Pantazis
311 7fcbf914 George Pantazis
        /// <summary>
312 7fcbf914 George Pantazis
        /// Set to true as soon as <c>Dispose</c> has been invoked.
313 7fcbf914 George Pantazis
        /// </summary>
314 7fcbf914 George Pantazis
        public bool IsDisposed { get; private set; }
315 7fcbf914 George Pantazis
316 7fcbf914 George Pantazis
317 7fcbf914 George Pantazis
        /// <summary>
318 7fcbf914 George Pantazis
        /// Disposes the object.
319 7fcbf914 George Pantazis
        /// </summary>
320 7fcbf914 George Pantazis
        /// <remarks>This method is not virtual by design. Derived classes
321 7fcbf914 George Pantazis
        /// should override <see cref="Dispose(bool)"/>.
322 7fcbf914 George Pantazis
        /// </remarks>
323 7fcbf914 George Pantazis
        public void Dispose()
324 7fcbf914 George Pantazis
        {
325 7fcbf914 George Pantazis
            Dispose(true);
326 7fcbf914 George Pantazis
327 7fcbf914 George Pantazis
            // This object will be cleaned up by the Dispose method.
328 7fcbf914 George Pantazis
            // Therefore, you should call GC.SupressFinalize to
329 7fcbf914 George Pantazis
            // take this object off the finalization queue 
330 7fcbf914 George Pantazis
            // and prevent finalization code for this object
331 7fcbf914 George Pantazis
            // from executing a second time.
332 7fcbf914 George Pantazis
            GC.SuppressFinalize(this);
333 7fcbf914 George Pantazis
        }
334 7fcbf914 George Pantazis
335 7fcbf914 George Pantazis
        /// <summary>
336 7fcbf914 George Pantazis
        /// This destructor will run only if the <see cref="Dispose()"/>
337 7fcbf914 George Pantazis
        /// method does not get called. This gives this base class the
338 7fcbf914 George Pantazis
        /// opportunity to finalize.
339 7fcbf914 George Pantazis
        /// <para>
340 7fcbf914 George Pantazis
        /// Important: Do not provide destructors in types derived from
341 7fcbf914 George Pantazis
        /// this class.
342 7fcbf914 George Pantazis
        /// </para>
343 7fcbf914 George Pantazis
        /// </summary>
344 7fcbf914 George Pantazis
        ~WindowMessageSink()
345 7fcbf914 George Pantazis
        {
346 7fcbf914 George Pantazis
            Dispose(false);
347 7fcbf914 George Pantazis
        }
348 7fcbf914 George Pantazis
349 7fcbf914 George Pantazis
350 7fcbf914 George Pantazis
        /// <summary>
351 7fcbf914 George Pantazis
        /// Removes the windows hook that receives window
352 7fcbf914 George Pantazis
        /// messages and closes the underlying helper window.
353 7fcbf914 George Pantazis
        /// </summary>
354 7fcbf914 George Pantazis
        private void Dispose(bool disposing)
355 7fcbf914 George Pantazis
        {
356 7fcbf914 George Pantazis
            //don't do anything if the component is already disposed
357 7fcbf914 George Pantazis
            if (IsDisposed) return;
358 7fcbf914 George Pantazis
            IsDisposed = true;
359 7fcbf914 George Pantazis
360 7fcbf914 George Pantazis
            //always destroy the unmanaged handle (even if called from the GC)
361 7fcbf914 George Pantazis
            WinApi.DestroyWindow(MessageWindowHandle);
362 7fcbf914 George Pantazis
            messageHandler = null;
363 7fcbf914 George Pantazis
        }
364 7fcbf914 George Pantazis
365 7fcbf914 George Pantazis
        #endregion
366 e7fb3ef2 George Pantazis
    }
367 9bae55d1 Panagiotis Kanavos
}