{"id":17,"date":"2010-12-08T13:58:53","date_gmt":"2010-12-08T02:58:53","guid":{"rendered":"http:\/\/blog.quppa.net\/?p=17"},"modified":"2010-12-08T13:58:53","modified_gmt":"2010-12-08T02:58:53","slug":"windows-7-style-notification-area-applications-in-wpf-part-2-notify-icon-position","status":"publish","type":"post","link":"https:\/\/www.quppa.net\/blog\/2010\/12\/08\/windows-7-style-notification-area-applications-in-wpf-part-2-notify-icon-position\/","title":{"rendered":"Windows 7-style Notification Area Applications in WPF: Part 2 (Notify Icon Position)"},"content":{"rendered":"<blockquote><p><a href=\"https:\/\/github.com\/Quppa\/NotificationAreaIconSampleAppWPF\" title=\"GitHub: NotificationAreaIconSampleAppWPF\">View source on GitHub.<\/a><\/p><\/blockquote>\n<p>You may have noticed that the notification area applications in Windows 7 (Volume\/Power\/Network\/Action Centre) appear centred above their icon. I wanted <a title=\"Keiki Usage Meter\" href=\"http:\/\/www.quppa.net\/keiki\/\">Keiki<\/a> to do the same; the current version is hardcoded to sit in the bottom right of the screen, which causes a few problems:<\/p>\n<ol>\n<li>The taskbar position is not taken into account; the window will be in the bottom right even if the taskbar is at the top of the screen.<\/li>\n<li>The window appears on top of the new Windows 7 fly-out interface for hiding notify icons if the Keiki icon is kept there.<\/li>\n<\/ol>\n<p>In this post, I will demonstrate how to retrieve the location of a <a title=\"MSDN: System.Windows.Forms.NotifyIcon Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.notifyicon.aspx\">System.Windows.Forms.NotifyIcon<\/a> with a function new to shell32.dll in Windows 7: <a title=\"MSDN: Shell_NotifyIconGetRect Function\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd378426(v=VS.85).aspx\">Shell_NotifyIconGetRect<\/a>. Windows Vista unfortunately lacks this function: I will cover the approach I use in Vista in a later post.<\/p>\n<p><em>Thanks to Fr\u00e9d\u00e9ric Hamidi for <\/em><a title=\"StackOverflow: Determining location of tray icon\" href=\"http:\/\/stackoverflow.com\/questions\/4366449\/determining-location-of-tray-icon\"><em>pointing me in the right direction<\/em><\/a><em>.<\/em><\/p>\n<p>  <!--more-->  <\/p>\n<p>The Shell_NotifyIconGetRect function takes two parameters: a <a title=\"MSDN: NOTIFYICONIDENTIFIER Structure\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd391553(v=VS.85).aspx\">NOTIFYICONIDENTIFIER<\/a> (a structure that identifies the icon) and a&#160; <a title=\"MSDN: RECT Structure\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd162897(v=VS.85).aspx\">RECT<\/a> (a structure which will receive the coordinates of the icon) and returns an <a title=\"MSDN: HRESULT\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb401631.aspx\">HRESULT<\/a> indicating whether the method succeeds or not. So, how can we use this function in managed code?<\/p>\n<h2><\/h2>\n<h1>PInvoke<\/h1>\n<p>When it comes to using unmanaged APIs within managed code, <a title=\"PInvoke.net\" href=\"http:\/\/www.pinvoke.net\/\">PInvoke.net<\/a> is probably the best place to start. Many PInvoke signatures for the Win32 API can be found here. Regrettably, as Shell_NotifyIconGetRect is a new function, it doesn\u2019t yet have an entry, so we have to work some stuff out for ourselves.<\/p>\n<p>First, we write the PInvoke signature of the <a title=\"MSDN: Shell_NotifyIconGetRect Function\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd378426(v=VS.85).aspx\">Shell_NotifyIconGetRect function<\/a>. The syntax of the function is:<\/p>\n<pre class=\"lang:c decode:true \">HRESULT Shell_NotifyIconGetRect(\n  __in   const NOTIFYICONIDENTIFIER *identifier,\n  __out  RECT *iconLocation\n);<\/pre>\n<p>Which gives us this signature:<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;Shell32&quot;, SetLastError = true)]\nprivate static extern Int32 Shell_NotifyIconGetRect([In] ref NOTIFYICONIDENTIFIER identifier, [Out] out RECT iconLocation<br \/>);<\/pre>\n<p>We also need to define the two structures used in the function: NOTIFYICONIDENTIFIER and RECT (which is not compatible with <a title=\"MSDN: System.Rect Structure\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.rect.aspx\">System.Rect<\/a>).<\/p>\n<p>The <a title=\"MSDN: NOTIFYICONIDENTIFIER Structure\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd391553(v=VS.85).aspx\">NOTIFYICONIDENTIFIER structure<\/a> looks like this:<\/p>\n<pre class=\"lang:c decode:true \">typedef struct _NOTIFYICONIDENTIFIER {\n  DWORD cbSize;\n  HWND  hWnd;\n  UINT  uID;\n  GUID  guidItem;\n} NOTIFYICONIDENTIFIER, *PNOTIFYICONIDENTIFIER;<\/pre>\n<p>Converting that to C#, we get this:<\/p>\n<pre class=\"lang:c# decode:true \">private struct NOTIFYICONIDENTIFIER\n{\n  public uint cbSize;\n  public IntPtr hWnd;\n  public uint uID;\n  public Guid guidItem;\n}<\/pre>\n<p>And the <a title=\"MSDN: RECT Structure\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd162897(v=VS.85).aspx\">RECT structure<\/a> looks like this:<\/p>\n<pre class=\"lang:c decode:true \">typedef struct _RECT {\n  LONG left;\n  LONG top;\n  LONG right;\n  LONG bottom;\n} RECT, *PRECT;<\/pre>\n<p>Which we can implement in C# as:<\/p>\n<pre class=\"lang:c# decode:true \">[StructLayout(LayoutKind.Sequential)]\nprivate struct RECT\n{\n  public int left;\n  public int top;\n  public int right;\n  public int bottom;\n}<\/pre>\n<h1>System.Windows.Forms.NotifyIcon<\/h1>\n<p>As there is currently no wrapper for <a title=\"MSDN: Shell_NotifyIcon Function\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb762159(VS.85).aspx\">Shell_NotifyIcon<\/a> included with WPF, <a title=\"Keiki Usage Meter\" href=\"http:\/\/www.quppa.net\/keiki\/\">Keiki<\/a> uses the <a title=\"MSDN: NotifyIcon Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.notifyicon.aspx\">System.Windows.Forms.NotifyIcon<\/a> class to create its notification area icon. This hasn\u2019t been an issue: I need only basic functionality, and there is the additional benefit (for me) of avoiding WPF menus, which imitate the style of native menus, but are in fact slightly different, thus sticking out (at least to my pedant eyes). The WinForms wrapper has its own downsides: it doesn\u2019t support custom icons (introduced in XP SP2 but not important for my project) and it would be nice not to have to reference that namespace at all in a WPF program.<\/p>\n<p>There are numerous high quality 3rd-party tray icon implementations for WPF: the best I\u2019ve seen is <a title=\"CodeProject: WPF NotifyIcon\" href=\"http:\/\/www.codeproject.com\/KB\/WPF\/wpf_notifyicon.aspx\">WPF NotifyIcon<\/a> by <a title=\"Philipp Sumi&#39;s Blog\" href=\"http:\/\/www.hardcodet.net\/\">Philipp Sumi<\/a>. However, I\u2019ve decided to stick with the standard WinForms NotifyIcon for this project, mostly for simplicity\u2019s sake. So, how can we use this class with the Shell_NotifyIconGetRect function that we just got working in managed code?<\/p>\n<p>Notice that the documentation for <a title=\"MSDN: NOTIFYICONIDENTIFIER\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd391553(v=VS.85).aspx\">NOTIFYICONIDENTIFIER<\/a> states:<\/p>\n<blockquote>\n<p>The icon can be identified to Shell_NotifyIconGetRect through this structure in two ways:<br \/>\n    <br \/>guidItem alone (recommended)<br \/>\n    <br \/>hWnd plus uID<\/p>\n<p>If guidItem is used, hWnd and uID are ignored.<\/p>\n<\/blockquote>\n<p>Using a <a title=\"Wikipedia: GUID\" href=\"http:\/\/en.wikipedia.org\/wiki\/GUID\">GUID<\/a> to identify a notify icon is an approach that is <a title=\"MSDN: NOTIFYICONDATA Structure\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb773352(v=VS.85).aspx\">new to Windows 7<\/a> and one that isn\u2019t used by the WinForms NotifyIcon class. Thus, we need to find the notify icon\u2019s hWnd (window handle) and uID so that we may pass them to our Shell_NotifyIconGetRect function.<\/p>\n<h2>Icon Handle &amp; ID<\/h2>\n<p><em>Igor Kushnarev\u2019s Code Project article \u2018<a title=\"The Code Project: Embedding .NET Controls to NotifyIcon Balloon Tooltip\" href=\"http:\/\/www.codeproject.com\/KB\/dotnet\/EmbCtrlNotIc.aspx\">Embedding .NET Controls to NotifyIcon Balloon Tooltip<\/a>\u2019 was helpful for this section.<\/em><\/p>\n<p>The list of properties for <a title=\"MSDN: NotifyIcon Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.notifyicon.aspx\">NotifyIcon<\/a> isn\u2019t very long, and it\u2019s clear that hWnd and uID are not obviously accessible.<\/p>\n<p>If we open up <a title=\".NET Reflector\" href=\"http:\/\/www.red-gate.com\/products\/reflector\/\">.NET Reflector<\/a> and navigate to System.Windows.Forms.NotifyIcon, the disassembler shows us that there is indeed a variable called \u2018id\u2019:<\/p>\n<pre class=\"lang:c# decode:true \">private int id;<\/pre>\n<p>We can use reflection in our code to retrieve this value at runtime:<\/p>\n<pre class=\"lang:c# decode:true \">FieldInfo idFieldInfo = notifyicon.GetType().GetField(&quot;id&quot;, BindingFlags.NonPublic | BindingFlags.Instance);\nint iconid = (int)idFieldInfo.GetValue(notifyicon);<\/pre>\n<p>where \u2018notifyicon\u2019 is an instance of System.Windows.Forms.NotifyIcon. (Some error handling code wouldn\u2019t go astray here.)<\/p>\n<p>Now we need to find the notify icon\u2019s window handle. In .NET Reflector we can see a variable called \u2018window\u2019:<\/p>\n<pre class=\"lang:c# decode:true \">private NotifyIconNativeWindow window;<\/pre>\n<p>System.Windows.Forms.NotifyIcon.NotifyIconNativeWindow inherits from <a title=\"MSDN: System.Windows.Forms.NativeWindow Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.nativewindow.aspx\">System.Windows.Forms.NativeWindow<\/a>, which has a property called Handle: this is what we were searching for.<\/p>\n<pre class=\"lang:c# decode:true \">FieldInfo windowFieldInfo = notifyicon.GetType().GetField(&quot;window&quot;, BindingFlags.NonPublic | BindingFlags.Instance);\nSystem.Windows.Forms.NativeWindow nativeWindow = (System.Windows.Forms.NativeWindow)windowFieldInfo.GetValue(notifyicon);\nIntPtr iconhandle = nativeWindow.Handle;<\/pre>\n<h1>Finally\u2026<\/h1>\n<p>After what seems like a lot of effort (though not a lot of code), we can at last call our Shell_NotifyIconGetRect function!<\/p>\n<pre class=\"lang:c# decode:true \">RECT rect = new RECT();\nNOTIFYICONIDENTIFIER nid = new NOTIFYICONIDENTIFIER()\n{\n    hWnd = iconhandle,\n    uID = (uint)iconid\n};\nnid.cbSize = (uint)Marshal.SizeOf(nid);\nint result = Shell_NotifyIconGetRect(ref nid, out rect);<\/pre>\n<p>If everything has happened as it should, \u2018rect\u2019 should now hold the coordinates of the notify icon. (Again, some error handling would be nice.)<\/p>\n<p>In the next post in this series I will look at how to get the position of the taskbar so that we place the window correctly using the result from Shell_NotifyIconGetRect.<\/p>\n<p>Interesting note: if the notify icon is inside the fly-out notification area box, Shell_NotifyIconGetRect will only return its position within that box if the box is currently open. If the box is closed, the result seems to point to the location of the notification area expansion arrow.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>View source on GitHub. You may have noticed that the notification area applications in Windows 7 (Volume\/Power\/Network\/Action Centre) appear centred above their icon. I wanted Keiki to do the same; the current version is hardcoded to sit in the bottom right of the screen, which causes a few problems: The taskbar position is not taken &hellip; <a href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/08\/windows-7-style-notification-area-applications-in-wpf-part-2-notify-icon-position\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Windows 7-style Notification Area Applications in WPF: Part 2 (Notify Icon Position)&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,6],"tags":[28,47,72,74,87,88,96,128,170,179,185],"class_list":["post-17","post","type-post","status-publish","format-standard","hentry","category-keiki","category-programming","tag-c","tag-dllimport","tag-interop","tag-keiki-2","tag-notification-area","tag-notifyicon","tag-pinvoke","tag-shell32","tag-win32","tag-winforms","tag-wpf"],"_links":{"self":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/17","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/comments?post=17"}],"version-history":[{"count":0,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/17\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/media?parent=17"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/categories?post=17"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/tags?post=17"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}