{"id":73,"date":"2010-12-28T01:50:00","date_gmt":"2010-12-27T14:50:00","guid":{"rendered":"http:\/\/blog.quppa.net\/?p=73"},"modified":"2010-12-28T01:50:00","modified_gmt":"2010-12-27T14:50:00","slug":"windows-7-style-notification-area-applications-in-wpf-part-6-notify-icon-position-pre-windows-7","status":"publish","type":"post","link":"https:\/\/www.quppa.net\/blog\/2010\/12\/28\/windows-7-style-notification-area-applications-in-wpf-part-6-notify-icon-position-pre-windows-7\/","title":{"rendered":"Windows 7-style Notification Area Applications in WPF: Part 6 (Notify Icon Position: Pre-Windows 7)"},"content":{"rendered":"<blockquote><p><a href=\"https:\/\/github.com\/Quppa\/NotificationAreaIconSampleAppWPF\" title=\"GitHub: NotificationAreaIconSampleAppWPF\">View source on GitHub.<\/a><\/p><\/blockquote>\n<p>In <a title=\"Windows 7-style Notification Area Applications in WPF: Part 2 (Notify Icon Position)\" href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/08\/windows-7-style-notification-area-applications-in-wpf-part-2-notify-icon-position\/\">Part 2<\/a> of this series I demonstrated how to use the <a title=\"MSDN: Shell_NotifyIconGetRect Function\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd378426(v=VS.85).aspx\">Shell_NotifyIconGetRect<\/a> function to find the position of a notify icon. This function is new to Windows 7, however, and we must find a different solution for earlier versions of Windows.<\/p>\n<p>This turns out to be quite difficult. A <a title=\"MSDN Forums: How to get screen coordinates of NotifyIcon\" href=\"http:\/\/social.msdn.microsoft.com\/forums\/en-US\/winforms\/thread\/4ac8d81e-f281-4b32-9407-e663e6c234ae\/\">post<\/a> on the MSDN forums by user <a title=\"parisisinjail\" href=\"http:\/\/social.msdn.microsoft.com\/profile\/parisisinjail\/?type=forum\">parisisinjail<\/a> provided a good starting point, and it led me to a Code Project article by Irek Zielinski that <a title=\"The Code Project: CTrayIconPosition - where is my tray icon?\" href=\"http:\/\/www.codeproject.com\/KB\/shell\/ctrayiconposition.aspx\">explained exactly what to do<\/a> \u2013 in native code. This post shows how to implement this kind of approach in managed code.<\/p>\n<p>I have verified that the following code works with Windows XP and Vista. It also works under Windows 7, but only when the icon is not located in the notification area fly-out (not found in previous versions). As such, this solution should only be used when Shell_NotifyIconGetRect is not available.<\/p>\n<p>The basic idea is that the notification area is actually just a special <a title=\"MSDN: About Toolbars\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb760443(v=VS.85).aspx\">Toolbar control<\/a>, with each icon being a toolbar button. We want to find the toolbar control and loop over the buttons until we find the one that corresponds to our notify icon. We can then find the coordinates of the toolbar button. (The fly-out in Windows 7 has a separate toolbar control, so you could search that instead of using Shell_NotifyIconGetRect if you really wanted to.)<\/p>\n<p>The process sounds straight forward, but the implementation is quite tricky. Read on for the code.<\/p>\n<p>  <!--more-->  <\/p>\n<h1>Step 1: Find the Notification Area Toolbar<\/h1>\n<p>The first thing that we need to do is to find the handle of the notification area toolbar. If we open Spy++ (a tool included with Visual Studio), we can find a top-level window with the class name \u2018Shell_TrayWnd\u2019. This window represents the Windows taskbar (the notification area is a child window). While this class name is not documented anywhere as far as I can tell (meaning that it might change with a future version of Windows), it has been consistent since at least Windows XP. If we look at this window\u2019s children, we\u2019ll find some windows with the class name \u2018ToolbarWindow32\u2019. One of these points to the toolbar containing our notification icon.<\/p>\n<p><a href=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/Spy1.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; border-right-width: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px\" title=\"Notification Area Window Handles\" border=\"0\" alt=\"Spy++ Window Showing Notification Area Toolbar\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/Spy_thumb.png\" width=\"544\" height=\"299\" \/><\/a><\/p>\n<p>The screenshot above is of a system running Windows 7 x64. However, the arrangement of windows underneath the Shell_TrayWnd window varies from version-to-version: it has changed from Windows XP to Vista and again from Vista to 7. Furthermore, the names of windows vary according to the system\u2019s language, so it\u2019s not worth searching for a window called \u2018User Promoted Notification Area\u2019 when it might be called \u2018\uc0ac\uc6a9\uc790 \ud504\ub86c\ud504\ud2b8 \uc54c\ub9bc \uc601\uc5ed\u2019 or any number of other strings (the names also change within language between Windows versions). In other words, we must be careful when we search for the toolbar\u2019s handle.<\/p>\n<p>With that in mind, let us continue.<\/p>\n<h2>Find Shell_TrayWnd<\/h2>\n<p>We\u2019ll use the FindWindow function to search for window with the class name \u2018Shell_TrayWnd\u2019.<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;user32.dll&quot;)]\nprivate static extern IntPtr FindWindow(string lpClassName, string lpWindowName);<\/pre>\n<pre class=\"lang:c# decode:true \">IntPtr naparenthandle = FindWindow(&quot;Shell_TrayWnd&quot;, null);<\/pre>\n<h2>Enumerate Child Windows<\/h2>\n<p>Given the variable nature of the child windows, it seems to me that the best approach is to find all child windows with the class name \u2018ToolbarWindow32\u2019 and search through all of them until we find our notify icon. We\u2019ll use the EnumChildWindows function to make a list of windows. The following code is from <a title=\"PInvoke.net: EnumChildWindows (User32)\" href=\"http:\/\/www.pinvoke.net\/default.aspx\/user32.enumchildwindows\">PInvoke.net<\/a>, with a minor change to make the list include only ToolbarWindow32 windows.<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;user32&quot;)]\n[return: MarshalAs(UnmanagedType.Bool)]\nprivate static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);\nprivate static List&lt;IntPtr&gt; GetChildWindows(IntPtr parent)\n{\n    List&lt;IntPtr&gt; result = new List&lt;IntPtr&gt;();\n    GCHandle listHandle = GCHandle.Alloc(result);\n    try\n    {\n        EnumWindowProc childProc = new EnumWindowProc(EnumToolbarWindow);\n        EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));\n    }\n    finally\n    {\n        if (listHandle.IsAllocated)\n            listHandle.Free();\n    }\n    return result;\n}\nprivate static bool EnumToolbarWindow(IntPtr handle, IntPtr pointer)\n{\n    GCHandle gch = GCHandle.FromIntPtr(pointer);\n    List&lt;IntPtr&gt; list = gch.Target as List&lt;IntPtr&gt;;\n    if (list == null)\n        throw new InvalidCastException(&quot;GCHandle Target could not be cast as List&lt;IntPtr&gt;&quot;);\n    StringBuilder classname = new StringBuilder(128);\n    GetClassName(handle, classname, classname.Capacity);\n    if (classname.ToString() == &quot;ToolbarWindow32&quot;)\n        list.Add(handle);\n    return true;\n}\nprivate delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);<\/pre>\n<p>We\u2019ll call our GetChildWindows function using the pointer to the Shell_TrayWnd window we found earlier:<\/p>\n<pre class=\"lang:c# decode:true \">List&lt;IntPtr&gt; natoolbarwindows = GetChildWindows(naparenthandle);<\/pre>\n<h1>Step 2: Search Toolbars<\/h1>\n<p>We should now have a list with pointers to all the notification area toolbars. We\u2019ll search through each toolbar until we find the notify icon (or run out of possibilities).<\/p>\n<pre class=\"lang:c# decode:true \">bool found = false;\nfor (int i = 0; !found &amp;&amp; i &lt; natoolbarwindows.Count; i++)\n{\n    IntPtr natoolbarhandle = natoolbarwindows[i];<\/pre>\n<p>We next need to determine how many buttons are on the current toolbar. We can send a TB_BUTTONCOUNT message to the toolbar to find out.<\/p>\n<pre class=\"lang:c# decode:true \">private static uint TB_BUTTONCOUNT = 0x418;\n[DllImport(&quot;user32.dll&quot;)]\nprivate static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);<\/pre>\n<pre class=\"lang:c# decode:true \">int buttoncount = SendMessage(natoolbarhandle, TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero).ToInt32();<\/pre>\n<p>Now we can loop through and find data about each button. This is harder than it sounds, since it turns out that we need to allocate memory within the toolbar\u2019s process (and copy it back afterwards) \u2013 we can\u2019t directly use memory belonging to our application. Irek Zielinski <a title=\"The Code Project: CTrayIconPosition - where is my tray icon?\" href=\"http:\/\/www.codeproject.com\/KB\/shell\/ctrayiconposition.aspx\">explains it more eloquently<\/a>.<\/p>\n<p>The GetWindowThreadProcessId function does what its name suggests:<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;user32.dll&quot;)]\nprivate static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);<\/pre>\n<pre class=\"lang:c# decode:true \">uint naprocessid;\nGetWindowThreadProcessId(natoolbarhandle, out naprocessid);<\/pre>\n<p>Next, we\u2019ll use the OpenProcess function to get a handle to the toolbar process:<\/p>\n<pre class=\"lang:c# decode:true \">[Flags]\nprivate enum ProcessAccessFlags : uint\n{\n    All = 0x001F0FFF,\n    Terminate = 0x00000001,\n    CreateThread = 0x00000002,\n    VMOperation = 0x00000008,\n    VMRead = 0x00000010,\n    VMWrite = 0x00000020,\n    DupHandle = 0x00000040,\n    SetInformation = 0x00000200,\n    QueryInformation = 0x00000400,\n    Synchronize = 0x00100000\n}\n[DllImport(&quot;kernel32.dll&quot;)]\nprivate static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);<\/pre>\n<pre class=\"lang:c# decode:true \">IntPtr naprocesshandle = OpenProcess(ProcessAccessFlags.All, false, naprocessid);<\/pre>\n<p>Now we allocate memory within the toolbar\u2019s process to store the information about each button:<\/p>\n<pre class=\"lang:c# decode:true \">[Flags]\npublic enum AllocationType\n{\n    Commit = 0x1000,\n    Reserve = 0x2000,\n    Decommit = 0x4000,\n    Release = 0x8000,\n    Reset = 0x80000,\n    Physical = 0x400000,\n    TopDown = 0x100000,\n    WriteWatch = 0x200000,\n    LargePages = 0x20000000\n}\n[Flags]\npublic enum MemoryProtection\n{\n    Execute = 0x10,\n    ExecuteRead = 0x20,\n    ExecuteReadWrite = 0x40,\n    ExecuteWriteCopy = 0x80,\n    NoAccess = 0x01,\n    ReadOnly = 0x02,\n    ReadWrite = 0x04,\n    WriteCopy = 0x08,\n    GuardModifierflag = 0x100,\n    NoCacheModifierflag = 0x200,\n    WriteCombineModifierflag = 0x400\n}\n[DllImport(&quot;kernel32.dll&quot;)]\nprivate static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);<\/pre>\n<pre class=\"lang:c# decode:true \">IntPtr toolbarmemoryptr = VirtualAllocEx(naprocesshandle, (IntPtr)null, (uint)Marshal.SizeOf(typeof(TBBUTTON)), AllocationType.Commit, MemoryProtection.ReadWrite);<\/pre>\n<p>We allocate enough memory to store a TBBUTTON struct, though we will use the same area to store a RECT struct later (if we find the icon). Since a RECT is smaller than a TBBUTTON, this is not a problem.<\/p>\n<p>We now begin our loop through each button:<\/p>\n<pre class=\"lang:c# decode:true \">for (int j = 0; !found &amp;&amp; j &lt; buttoncount; j++)\n{<\/pre>\n<p>We\u2019ll use the TB_GETBUTTON message to retrieve a TBBUTTON data structure for each button. We could alternatively use the TBBUTTONINFO structure, but it doesn\u2019t provide us with any additional useful information in this case.<\/p>\n<pre class=\"lang:c# decode:true \">private static uint TB_GETBUTTON = 0x417;<\/pre>\n<pre class=\"lang:c# decode:true \">SendMessage(natoolbarhandle, TB_GETBUTTON, new IntPtr(j), toolbarmemoryptr);<\/pre>\n<p>There should now be a populated TBBUTTON structure in the toolbar process\u2019s memory: let\u2019s retrieve it with ReadProcessMemory.<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;kernel32.dll&quot;)]\nprivate static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, out TBBUTTON lpBuffer, int dwSize, out int lpNumberOfBytesRead);<\/pre>\n<pre class=\"lang:c# decode:true \">TBBUTTON buttoninfo = new TBBUTTON();\nReadProcessMemory(naprocesshandle, toolbarmemoryptr, out buttoninfo, Marshal.SizeOf(buttoninfo), out bytesread);<\/pre>\n<p>The <a title=\"MSDN: TBBUTTON Structure\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb760476(v=vs.85).aspx\">TBBUTTON structure<\/a> contains a member called \u2018dwData\u2019, which holds an \u2018application-defined value\u2019. In the case of the notification area, this happens to be a pointer to an IntPtr followed by a uint, holding the notify icon\u2019s window handle and ID, respectively. Let\u2019s retrieve those values. Note the overloads of ReadProcessMemory (two of several). Note also that it is important to use IntPtr, not int, since the length of the handle varies depending on whether the system is 32-bit or 64-bit.<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;kernel32.dll&quot;)]\nprivate static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, out IntPtr lpBuffer, int dwSize, out int lpNumberOfBytesRead);\n[DllImport(&quot;kernel32.dll&quot;)]\nprivate static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, out uint lpBuffer, int dwSize, out int lpNumberOfBytesRead);<\/pre>\n<pre class=\"lang:c# decode:true \">IntPtr niinfopointer = buttoninfo.dwData;\nIntPtr nihandlenew;\nReadProcessMemory(naprocesshandle, niinfopointer, out nihandlenew, Marshal.SizeOf(typeof(IntPtr)), out bytesread);\nuint niidnew;\nReadProcessMemory(naprocesshandle, niinfopointer + Marshal.SizeOf(typeof(IntPtr)), out niidnew, Marshal.SizeOf(typeof(uint)), out bytesread);<\/pre>\n<p>Now all we need to do is check whether the current toolbar button is in fact our notify icon. (I demonstrated how to retrieve the handle and ID of a WinForms NotifyIcon in <a title=\"Windows 7-style Notification Area Applications in WPF: Part 2 (Notify Icon Position)\" href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/08\/windows-7-style-notification-area-applications-in-wpf-part-2-notify-icon-position\/\">Part 2<\/a>. I\u2019ll assume those values are available here.)<\/p>\n<pre class=\"lang:c# decode:true \">if (nihandlenew == nihandle &amp;&amp; niidnew == niid)\n{<\/pre>\n<p>If either condition is false, we\u2019ll just continue on with the loop(s). If, on the other hand, we\u2019ve found the notify icon, we need to find its coordinates: on to Step 3!<\/p>\n<h2><\/h2>\n<h2>TBBUTTON Struct<\/h2>\n<p>You may notice that I\u2019ve not defined the TBBUTTON struct yet. While the structure looks simple enough in the MSDN documentation, implementing it in managed code is a bit tough as its size varies depending on whether the operating system is 32-bit of 64-bit. Julian McFarlane <a title=\"jo0ls&#39; .Net stuff: Structures that vary in size on x86\/x64 - what to do?\" href=\"http:\/\/jo0ls-dotnet-stuff.blogspot.com\/2008\/12\/structures-that-vary-in-size-on-x86x64.html\">blogged about this two years ago<\/a>. I stole this image from his blog:<\/p>\n<p><a title=\"Structures that vary in size on x86\/x64 - what to do?\" href=\"http:\/\/jo0ls-dotnet-stuff.blogspot.com\/2008\/12\/structures-that-vary-in-size-on-x86x64.html\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px\" title=\"TBBUTTON Structure x86 vs x64\" border=\"0\" alt=\"TBBUTTON Structure x86 vs x64\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/TBBUTTON.png\" width=\"383\" height=\"336\" \/><\/a><\/p>\n<p>As you can see, the padding is 2 bytes long under 32-bit Windows, and 6 bytes long under 64-bit Windows. Since we don\u2019t care about fsState and fsStyle for this project, the easiest solution is to simply use three IntPtrs for fsState\/fsStyle\/padding, dwData and iString. This will give us the correct alignment no matter the architecture. (It would of course be possible to retrieve fsState and fsStyle later, if needs be.)<\/p>\n<pre class=\"lang:c# decode:true \">[StructLayout(LayoutKind.Sequential)]\nprivate struct TBBUTTON\n{\n    public int iBitmap;\n    public int idCommand;\n    public IntPtr fsStateStylePadding;\n    public IntPtr dwData;\n    public IntPtr iString;\n}<\/pre>\n<h1>Step 3: Determine Button\u2019s Coordinates<\/h1>\n<p>Now that we\u2019ve determined which toolbar button corresponds to our notify icon, we just need to find its coordinates. This is made simple by the TB_GETITEMRECT message and the MapWindowPoints function.<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;user32.dll&quot;)]\nprivate static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, ref RECT lpPoints, UInt32 cPoints);\nprivate static uint TB_GETITEMRECT = 0x41d;<\/pre>\n<pre class=\"lang:c# decode:true \">SendMessage(natoolbarhandle, TB_GETITEMRECT, new IntPtr(j), toolbarmemoryptr);\nRECT result;\nReadProcessMemory(naprocesshandle, toolbarmemoryptr, out result, Marshal.SizeOf(result), out bytesread);\nMapWindowPoints(natoolbarhandle, (IntPtr)null, ref result, 2);\nfound = true;<\/pre>\n<h1><\/h1>\n<h1>Step 4: Clean Up<\/h1>\n<p>When we deal with unmanaged code, it is important to free resources when we\u2019re done with them, otherwise we\u2019ll start leaking memory.<\/p>\n<pre class=\"lang:c# decode:true \">[Flags]\npublic enum FreeType\n{\n    Decommit = 0x4000,\n    Release = 0x8000,\n}\n[DllImport(&quot;kernel32.dll&quot;)]\nprivate static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, FreeType dwFreeType);\n[DllImport(&quot;kernel32.dll&quot;)]\n[return: MarshalAs(UnmanagedType.Bool)]\nprivate static extern bool CloseHandle(IntPtr hObject);<\/pre>\n<pre class=\"lang:c# decode:true \">VirtualFreeEx(naprocesshandle, toolbarmemoryptr, 0, FreeType.Release);\nCloseHandle(naprocesshandle);<\/pre>\n<p>As promised earlier, look out for a sample solution with all the code from this series of blog posts put together. I\u2019ll try and publish it soon.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>View source on GitHub. In Part 2 of this series I demonstrated how to use the Shell_NotifyIconGetRect function to find the position of a notify icon. This function is new to Windows 7, however, and we must find a different solution for earlier versions of Windows. This turns out to be quite difficult. A post &hellip; <a href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/28\/windows-7-style-notification-area-applications-in-wpf-part-6-notify-icon-position-pre-windows-7\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Windows 7-style Notification Area Applications in WPF: Part 6 (Notify Icon Position: Pre-Windows 7)&#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,75,87,96,146,147,150,159,170,185],"class_list":["post-73","post","type-post","status-publish","format-standard","hentry","category-keiki","category-programming","tag-c","tag-dllimport","tag-interop","tag-keiki-2","tag-kernel32","tag-notification-area","tag-pinvoke","tag-taskbar","tag-tbbutton","tag-toolbar","tag-user32","tag-win32","tag-wpf"],"_links":{"self":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/73","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=73"}],"version-history":[{"count":0,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/73\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/media?parent=73"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/categories?post=73"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/tags?post=73"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}