Monday, April 14, 2014

Refresh tray icons - How to remove dead tray icon after killing a program/process

Sometime you  just need to automate something, like running a script, killing a program, refreshing something...
Killing a program from a script, sometime, have an unwanted side effect: the tray icon of this program wont go away until you move your mouse over it.
So after, say, 10 run of the script, you find yourself with 10 copy of the same (dead) icons.


The solution seam easy: just move your mouse over the dead icons, and they will go away.
But after years of applying this same old solution, I got tired, and I started looking for a better way :-)

And I found it: I found an AutoHotKey script that refresh the trayicons, removing the dead one.
There are various version of this specific script, the one I found seem to works perfectly on Win7 x64 (it's supposed to works even on x32 system, but I didn't try it) 
I would like to credit the original author, but I don't remember the specific forum/website where I downloaded this specific version (I downloaded a lot of ahk script before finding one that worked...)

Here are the download links:

Here is the source code (formatting by  http://codeformatter.blogspot.it/  and  http://prettyprinter.de/module.php?name=PrettyPrinter)
 tray_iconCleanup()  
 tray_icons()  
 {  
   static TB_BUTTONCOUNT := 0x0418, TB_GETBUTTON := 0x0417  
   array := []  
   array[0] := ["sProcess","toolTip","nMsg","uID","idx","idn","Pid","hwnd","sClass","hIcon"]  
   Index := 0  
   detectHiddenWindows_old := a_detectHiddenWindows  
   detectHiddenWindows, on  
   trayWindows := "Shell_TrayWnd,NotifyIconOverflowWindow"  
   loop, parse, trayWindows, csv  
   {  
     winGet, taskbar_pid, PID, ahk_class %a_loopField%  
     hProc := dllCall( "OpenProcess", "Uint", 0x38, "int", 0, "Uint", taskbar_pid )  
     pProc := dllCall( "VirtualAllocEx", "Uint", hProc, "Uint", 0, "Uint", 32, "Uint", 0x1000, "Uint", 0x4 )  
     idxTB := tray_getTrayBar()  
     sendMessage, %TB_BUTTONCOUNT%, 0, 0, ToolbarWindow32%idxTB%, ahk_class %a_loopField%  
     loop, %errorLevel%  
     {  
       sendMessage, %TB_GETBUTTON%, a_index-1, pProc, ToolbarWindow32%idxTB%, ahk_class %a_loopField%  
       varSetCapacity( btn,32,0 ), varSetCapacity( nfo,32,0 )  
       dllCall( "ReadProcessMemory", "Uint", hProc, "Uint", pProc, "Uint", &btn, "Uint", 32, "Uint", 0 )  
       iBitmap := numGet( btn, 0 )  
       idn := numGet( btn, 4 )  
       Statyle := numGet( btn, 8 )  
       if dwData := numGet( btn,12,"Uint" )  
       iString := numGet( btn,16 )  
       else dwData := numGet( btn,16,"int64" ), iString := numGet( btn,24,"int64" )  
       dllCall( "ReadProcessMemory", "Uint", hProc, "Uint", dwData, "Uint", &nfo, "Uint", 32, "Uint", 0 )  
       if numGet( btn,12,"Uint" )  
       {  
         hwnd := numGet( nfo, 0 )  
         uID := numGet( nfo, 4 )  
         nMsg := numGet( nfo, 8 )  
         hIcon := numGet( nfo,20 )  
       }  
       else hwnd := numGet( nfo, 0,"int64" ), uID := numGet( nfo, 8,"Uint" ), nMsg := numGet( nfo,12,"Uint" )  
       winGet, pid, PID, ahk_id %hwnd%  
       winGet, sProcess, ProcessName, ahk_id %hwnd%  
       winGetClass, sClass, ahk_id %hwnd%  
       varSetCapacity( sTooltip,128 ), varSetCapacity( wTooltip,128*2 )  
       dllCall( "ReadProcessMemory", "Uint", hProc, "Uint", iString, "Uint", &wTooltip, "Uint", 128*2, "Uint", 0 )  
       dllCall( "WideCharToMultiByte", "Uint", 0, "Uint", 0, "str", wTooltip, "int", -1, "str", sTooltip, "int", 128, "Uint", 0, "Uint", 0 )  
       idx := a_index-1  
       toolTip := a_isUnicode ? wTooltip : sTooltip  
       Index++  
       for a,b in array[0]  
       array[Index,b] := %b%  
     }  
     dllCall( "VirtualFreeEx", "Uint", hProc, "Uint", pProc, "Uint", 0, "Uint", 0x8000 )  
     dllCall( "CloseHandle", "Uint", hProc )  
   }  
   detectHiddenWindows, %detectHiddenWindows_old%  
   return array  
 }  
 tray_iconCleanup()  
 {  
   /*  
   remove orphan icons ( Their processes have been killed )  
   */  
   tray_icons := tray_icons()  
   for index in tray_icons  
   {  
     ifEqual,index,0,continue  
     if ( tray_icons[index, "sProcess"] = "" )  
     tray_iconRemove( tray_icons[index, "hwnd"], tray_icons[index, "uID"],"", tray_icons[index, "hIcon"] )  
   }  
 }  
 tray_iconRemove( hwnd, uID, nMsg = 0, hIcon = 0, nRemove = 0x2 )  
 {  
   /*  
   NIM_DELETE := 0x00000002  
   typedef struct _NOTIFYICONDATA {  
     DWORD cbSize; x00  
     hwnd hwnd; x04  
     UINT uID; x0c/d12  
     UINT uFlags; x10/d16  
     UINT uCallbackMessage; x14/d20  
     HICON hIcon; x18/d24  
     TCHAR szTip[64]; x20 ( UNICODE )  
     DWORD dwState; xa0  
     DWORD dwStateMask; xa4  
     TCHAR szInfo[256]; xa8 ( UNICODE )  
     union {  
       UINT uTimeout; x01a8  
       UINT uVersion; x01a8  
     };  
     TCHAR szInfoTitle[64]; x01ac ( UNICODE )  
     DWORD dwInfoFlags; x022c  
     GUID guidItem; ( 128bits ) x0230  
     HICON hBalloonIcon; x023c  
     ;end x0244  
   } NOTIFYICONDATA, *PNOTIFYICONDATA;  
   MSDN:  
   http://msdn.microsoft.com/en-us/library/windows/desktop/bb773352%28v=vs.85%29.aspx  
   */  
   varSetCapacity( nid,size := 936+4*a_ptrSize )  
   numPut( size, nid, 0, "uint" )  
   numPut( hwnd, nid, a_ptrSize )  
   numPut( uID, nid,a_ptrSize*2, "uint" )  
   numPut( 1|2|4, nid,a_ptrSize*3, "uint" )  
   numPut( nMsg , nid,a_ptrSize*4, "uint" )  
   numPut( hIcon, nid,a_ptrSize*5, "uint" )  
   return dllCall( "shell32\Shell_NotifyIconA", "Uint", nRemove, "Uint", &nid )  
 }  
 tray_iconHide( idn, bHide = true )  
 {  
   static TB_HIDEBUTTON := 0x404, WM_SETTINGCHANGE := 0x001A  
   idxTB := tray_getTrayBar()  
   sendMessage, %TB_HIDEBUTTON%, idn, bHide, ToolbarWindow32%idxTB%, ahk_class Shell_TrayWnd  ;NotifyIconOverflowWindow  
   sendMessage, %WM_SETTINGCHANGE%, 0, 0, , ahk_class Shell_TrayWnd  
 }  
 tray_iconDelete( idx )  
 {  
   static TB_DELETEBUTTON := 0x416, WM_SETTINGCHANGE := 0x001A  
   idxTB := tray_getTrayBar()  
   sendMessage, %TB_DELETEBUTTON%, idx - 1, 0, ToolbarWindow32%idxTB%, ahk_class Shell_TrayWnd  
   sendMessage, %WM_SETTINGCHANGE%, 0, 0, , ahk_class Shell_TrayWnd  
 }  
 tray_iconMove( idxOld, idxNew )  
 {  
   static TB_MOVEBUTTON := 0x452  
   idxTB := tray_getTrayBar()  
   sendMessage, %TB_MOVEBUTTON%, idxOld - 1, idxNew - 1, ToolbarWindow32%idxTB%, ahk_class Shell_TrayWnd  
 }  
 tray_getTrayBar()  
 {  
   detectHiddenWindows_old := a_detectHiddenWindows  
   detectHiddenWindows, on  
   controlGet, hParent, hwnd,, TrayNotifyWnd1 , ahk_class Shell_TrayWnd  
   controlGet, hChild , hwnd,, ToolbarWindow321, ahk_id %hParent%  
   loop  
   {  
     controlGet, hwnd, hwnd,, ToolbarWindow32%a_index%, ahk_class Shell_TrayWnd  
     if !hwnd  
     break  
     else if ( hwnd = hChild )  
     {  
       idxTB := a_index  
       break  
     }  
   }  
   detectHiddenWindows, %detectHiddenWindows_old%  
   return idxTB  
 }