/// <summary> /// The actual trayIconBuster /// </summary> /// <returns>The number of tray icons removed.</returns> public static uint RemovePhantomIcons() { uint removedCount = 0; lock (key) { // prevent concurrency problems IntPtr hWnd = IntPtr.Zero; FindNestedWindow(ref hWnd, "Shell_TrayWnd"); FindNestedWindow(ref hWnd, "TrayNotifyWnd"); FindNestedWindow(ref hWnd, "SysPager"); FindNestedWindow(ref hWnd, "ToolbarWindow32"); // create an object so we can exchange data with other process using (LP_Process process = new LP_Process(hWnd)) { ToolBarButton tbb = new ToolBarButton(); IntPtr remoteButtonPtr = process.Allocate(tbb); TrayData td = new TrayData(); process.Allocate(td); uint itemCount = (uint)SendMessage(hWnd, TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero); log("There are " + itemCount + " tray icons (some of them hidden)"); bool foundSomeExe = false; // for safety reasons we perform two passes: // pass1 = search for my own NotifyIcon // pass2 = search phantom icons and remove them // pass2 doesnt happen if pass1 fails for (int pass = 1; pass <= 2; pass++) { for (uint item = 0; item < itemCount; item++) { // index changes when previous items got removed ! uint item2 = item - removedCount; uint SOK = (uint)SendMessage(hWnd, TB_GETBUTTON, new IntPtr(item2), remoteButtonPtr); if (SOK != 1) { throw new ApplicationException("TB_GETBUTTON failed"); } process.Read(tbb, remoteButtonPtr); process.Read(td, tbb.dwData); if (td.hWnd == IntPtr.Zero) { throw new ApplicationException("Invalid window handle"); } using (LP_Process proc = new LP_Process(td.hWnd)) { string filename = proc.GetImageFileName(); if (pass == 1 && filename != null) { filename = filename.ToLower(); if (filename.EndsWith(".exe")) { foundSomeExe = true; log("found real icon created by: " + filename); break; } } // a phantom icon has no imagefilename if (pass == 2 && filename == null) { SOK = (uint)SendMessage(hWnd, TB_DELETEBUTTON, new IntPtr(item2), IntPtr.Zero); if (SOK != 1) { throw new ApplicationException("TB_DELETEBUTTON failed"); } removedCount++; } } } // if I did not see myself, I will not run the second // pass, which would try and remove phantom icons if (!foundSomeExe) { throw new ApplicationException( "Failed to find any real icon"); } } } log(removedCount.ToString() + " icons removed"); } return(removedCount); }
/// <summary> /// The actual trayIconBuster /// </summary> /// <returns>The number of tray icons removed.</returns> public unsafe static uint RemovePhantomIcons() { bool is64bitWindows = Is64BitWindows(); ToolBarButton64 tbb64 = new ToolBarButton64(); ToolBarButton32 tbb32 = new ToolBarButton32(); TrayData td = new TrayData(); bool foundSomeExe = false; uint totalRemovedCount = 0; uint totalItemCount = 0; // for safety reasons we perform two passes: // pass1 = search for my own NotifyIcon // pass2 = search phantom icons and remove them // pass2 doesnt happen if pass1 fails lock (key) { // prevent concurrency problems for (int pass = 1; pass <= 2; pass++) { for (int kind = 0; kind < 2; kind++) { IntPtr hWnd = IntPtr.Zero; if (kind == 0) { // get the regular icon collection that exists on all Windows versions FindNestedWindow(ref hWnd, "Shell_TrayWnd"); FindNestedWindow(ref hWnd, "TrayNotifyWnd"); FindNestedWindow(ref hWnd, "SysPager"); FindNestedWindow(ref hWnd, "ToolbarWindow32"); } else { // get the hidden icon collection that exists since Windows 7 try { FindNestedWindow(ref hWnd, "NotifyIconOverflowWindow"); FindNestedWindow(ref hWnd, "ToolbarWindow32"); } catch { // fail silently, as NotifyIconOverflowWindow did not exist prior to Win7 break; } } // create an object so we can exchange data with other process using (LP_Process process = new LP_Process(hWnd)) { IntPtr remoteButtonPtr; if (is64bitWindows) { remoteButtonPtr = process.Allocate(tbb64); } else { remoteButtonPtr = process.Allocate(tbb32); } process.Allocate(td); uint itemCount = (uint)SendMessage(hWnd, TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero); //log("There are "+itemCount+" tray icons (some of them hidden)"); uint removedCount = 0; for (uint item = 0; item < itemCount; item++) { totalItemCount++; // index changes when previous items got removed ! uint item2 = item - removedCount; uint SOK = (uint)SendMessage(hWnd, TB_GETBUTTON, new IntPtr(item2), remoteButtonPtr); if (SOK != 1) { throw new ApplicationException("TB_GETBUTTON failed"); } if (is64bitWindows) { process.Read(tbb64, remoteButtonPtr); process.Read(td, tbb64.dwData); } else { process.Read(tbb32, remoteButtonPtr); process.Read(td, tbb32.dwData); } IntPtr hWnd2 = td.hWnd; if (hWnd2 == IntPtr.Zero) { throw new ApplicationException("Invalid window handle"); } using (LP_Process proc = new LP_Process(hWnd2)) { string filename = proc.GetImageFileName(); if (pass == 1 && filename != null) { filename = filename.ToLower(); if (filename.EndsWith(".exe")) { foundSomeExe = true; log("(kind=" + kind + ") found real icon created by: " + filename); break; } } // a phantom icon has no imagefilename if (pass == 2 && filename == null) { SOK = (uint)SendMessage(hWnd, TB_DELETEBUTTON, new IntPtr(item2), IntPtr.Zero); if (SOK != 1) { throw new ApplicationException("TB_DELETEBUTTON failed"); } removedCount++; totalRemovedCount++; } } } } } // next kind // if I did not see myself, I will not run the second // pass, which would try and remove phantom icons if (totalItemCount != 0 && !foundSomeExe) { throw new ApplicationException( "Failed to find any real icon"); } } } // release lock log(totalItemCount.ToString() + " icons found, " + totalRemovedCount + " icons removed"); return(totalRemovedCount); }
/// <summary> /// Removes any zombie icons (those whose processes no longer exist) from the notification tray. /// </summary> /// <returns>The number of tray icons removed.</returns> public static uint RemoveZombieIcons() { var toolbarButton = new ToolbarButton(); var extraData = new ExtraButtonData(); uint totalRemovedCount = 0; uint totalItemCount = 0; foreach (var windowList in TrayWindowSearchList) { //Get a handle to the toolbar window IntPtr toolbarHandle = FindNestedWindow(windowList); if (toolbarHandle == IntPtr.Zero) { Utilities.Log($"No window found for list: {windowList}"); continue; } //Use that handle to open the toolbar's process using (LP_Process process = new LP_Process(toolbarHandle)) { //Allocate shared memory in the process to store toolbar button data for us to read IntPtr remoteButtonPtr = process.Allocate(toolbarButton); process.Allocate(extraData); //Ask the window how many buttons it contains so we can iterate over them uint itemCount = (uint)SendMessage(toolbarHandle, TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero); totalItemCount += itemCount; Utilities.Log($"Found {itemCount} tray icons (some may be hidden)"); uint removedCount = 0; for (uint item = 0; item < itemCount; item++) { //We remove items starting from the leftmost (#0), so for each item we have removed, the remaining items' indices // will have 1 subtracted from them (since they all just got shifted left by 1). uint index = item - removedCount; //Get info for this toolbar button if ((uint)SendMessage(toolbarHandle, TB_GETBUTTON, new IntPtr(index), remoteButtonPtr) == 0) { throw new ApplicationException("TB_GETBUTTON failed"); } process.Read(toolbarButton, remoteButtonPtr); process.Read(extraData, toolbarButton.dwData); //Open the handle for this button to see if its parent process exists or not. If it has no parent, it's a zombie - kill it! IntPtr buttonHandle = extraData.hWnd; if (buttonHandle == IntPtr.Zero) { throw new ApplicationException("Invalid handle in tray button data"); } using (LP_Process proc = new LP_Process(buttonHandle)) { if (proc.ownerProcessID == 0) { RemoveIcon(extraData); removedCount++; totalRemovedCount++; } } } } } Utilities.Log($"Done. {totalItemCount} icons found, {totalRemovedCount} icons removed."); return(totalRemovedCount); }