/// <summary> /// Removes the given tray icon using the TrayData we've fetched for it. /// </summary> private static void RemoveIcon(ExtraButtonData td) { var data = new NotifyIconData() { hWnd = td.hWnd, uID = td.uID }; //Try to remove the icon. Throw the last Win32 error if this fails. if (!Shell_NotifyIcon(NIM_DELETE, data)) { throw new Win32Exception(); } }
/// <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); }