rover Posted May 5, 2009 Share Posted May 5, 2009 (edited) Remove orphaned Notification Area toolbar buttons (icons)left behind after an application crash or process closeEdit: Dec 5 2k11Added _RefreshNotificationAreaIcons() UDF for Win7/2008R2 - x86/x64Updated for 64 bit OS support.I suppose this issue will finally be put to rest in Win 8...Orphaned icons still hang around in the User Promoted Notification Area and Overflow Notification Area in Win 7,but the system removes orphaned icons from the System Promoted Notification Area after 30 seconds.Now, why didn't they do that for the other two areas?The only use I have for this is replacing the _RefreshSystemTray() code in the SciTE AutoIt3Wrapper script./Edit-------------------------------Win 2k/XP/2003 Version - x86 32 bit onlytested on Win2000, WinXP and VistaSynopsis: Each taskbar Notification Area toolbar button/icon has an applicationwindow handle associated with it that receives the notification messages.If the window handle owner process no longer exists the orphaned button can be deleted.The window handle and application identifier of the icon must match to allow deletion with Shell_NotifyIcon API.(application identifier value is set by developer, so value is sometimes only 0 or 1 (AutoIt EXEs return 1))[Disclaimer]_RefreshTrayIcons() accesses the explorer memspace so it will inherently have a risk of an access violation explorer crashunder unforeseen error conditions or third party shell modifications.this function uses the same memory reading code used in the UDF controls functions.even though there is error handling and limited write permission to explorer memspace, personal use only is recommended.use Valik's _RefreshSystemTray() mouse over method in an enterprise environment as it is fail-safe (no explorer memory access)Don't call this function from a service:MicroSoft's Raymond Chen at 'The Old New Thing' blog, states Shell_NotifyIcon API should never be called from a service.Edit: Looks like the don't run in a service warning is about creating a tray icon for a servicewhich apparently is a security risk - the system not allowing explorer to send messages to a higher security context service process (notification messages).The UDF sends messages to and reads from explorer memorybut does not receive messages so I don't think that warning is applicable.This method of clearing icons is a hack and not recommended for use in production code as mentioned byposters to Raymond Chen's Blog, but certainly suitable for private use.[/Disclaimer]Thanks to:Tuape's SysTray UDF for ideaswraithdu's NOTIFYICONDATA codeGet Shell_NotifyIcon to show tray balloon tipNOTIFYICONDATA Structurehttp://msdn.microsoft.com/en-us/library/bb773352.aspxValik's mouse over method_RefreshSystemTray(), Remove dead icons from Notification areaand MSDN and other sourcesTested on: Win 2000, Win XP and VistaUntested: Win 2003, Win 2008 and Win 7(haven't looked into changes in tray for above OS, maybe MS finally checks if buttons are abandoned by closed processes?...)Untested on 64 bit OS - not currently supportedExamples:_RefreshNotificationAreaIcons() - Vista/2008/Win7/2008R2 - x86/x64included in UDF_RefreshSystemTray() - Win2k/XP/2003 - x86 onlyexpandcollapse popup;#NoTrayIcon Opt('MustDeclareVars', 1) #include <File.au3> #include <Date.au3> ;Usage ;_RefreshTrayIcons() ;Demo ;//create 10 orphaned tray area buttons from closed processes _CleanTrayTest() ;Two examples ;//clear icon if monitored process closed or crashed ;//could be used with dual scripts that restart a program if process is closed (if tray icon used) ;_ProcessMonitor("processname.exe") ;//tray icon monitor with error logging ;monitors tray for orphaned icons, reports to console and log file (for debug testing) ;_TrayMonitor(1000) ;//create 10 orphaned tray area buttons from closed processes Func _CleanTrayTest() Local $iButtonCnt, $iErr, $iExt, $iTimer If MsgBox(1 + 262144, "Notification Area icon refresh demo", _ "About to create and process close 10 temporary GUI's" & _ @CRLF & "and orphan 10 icons in tray" & _ @CRLF & "OK to proceed or Cancel") = 2 Then Exit _RunTestGUI(10) $iTimer = TimerInit() $iButtonCnt = _RefreshTrayIcons() $iErr = @error $iExt = @extended ConsoleWrite("- Runtime " & Round(TimerDiff($iTimer)) & " Milliseconds" & @CRLF) ConsoleWrite("+ Deleted " & $iButtonCnt & " orphaned tray notification area icon(s)" & _ @CRLF & "! Error: " & $iErr & " : Extended: " & $iExt & @CRLF & @CRLF) Exit EndFunc ;==>_CleanTrayTest ;//tray monitor with error logging Func _TrayMonitor($iChkRate = 5000) Local $iButtonCnt, $iErr, $iExt, $iErrBuffer, $iExtBuffer While 1 Sleep($iChkRate) $iButtonCnt = _RefreshTrayIcons() $iErr = @error $iExt = @extended Select Case $iButtonCnt = 0 And $iErr = 0 And $iExt = 0 ContinueLoop Case $iButtonCnt < 1 And $iErr = 183 And $iExt = 183 ;instance already running $iErrBuffer = $iErr $iExtBuffer = $iExt _FileWriteLog(@ScriptDir & "\RefreshTrayIcons.log", "Deleted " & _ $iButtonCnt & " orphaned tray notification area icon(s)" & _ @CRLF & "! Error: " & $iErrBuffer & " : Extended: " & $iExtBuffer, -1) ConsoleWrite("+ " & _Now() & @CRLF & "+ Deleted " & $iButtonCnt & _ " orphaned tray notification area icon(s)" & _ @CRLF & "! Error: " & $iErrBuffer & " : Extended: " & $iExtBuffer & @CRLF & @CRLF) Case $iButtonCnt < 1 And $iErrBuffer = $iErr And $iExtBuffer = $iExt ContinueLoop Case Else $iErrBuffer = $iErr $iExtBuffer = $iExt _FileWriteLog(@ScriptDir & "\RefreshTrayIcons.log", "Deleted " & $iButtonCnt & _ " orphaned tray notification area icon(s)" & _ @CRLF & "! Error: " & $iErrBuffer & " : Extended: " & $iExtBuffer, -1) ConsoleWrite("+ " & _Now() & @CRLF & "+ Deleted " & $iButtonCnt & _ " orphaned tray notification area icon(s)" & _ @CRLF & "! Error: " & $iErrBuffer & " : Extended: " & $iExtBuffer & @CRLF & @CRLF) ;ExitLoop EndSelect WEnd Exit EndFunc ;==>_TrayMonitor ;//monitor a process Func _ProcessMonitor($sProcessname) While ProcessExists($sProcessname) Sleep(2000) WEnd Local $iButtonCnt = _RefreshTrayIcons() Local $iErr = @error, $iExt = @extended ConsoleWrite("+ Deleted " & $iButtonCnt & _ " orphaned tray notification area icon(s) for process: " & _ $sProcessname & @CRLF & "! Error: " & $iErr & _ " : Extended: " & $iExt & @CRLF & @CRLF) Exit EndFunc ;==>_ProcessMonitor Func _RunTestGUI($iGui = 10) Local $aPID[$iGui], $line, $sTemp = EnvGet("TEMP"), $iCnt Local $file = FileOpen($sTemp & "\RunTestGUI.au3", 2) If $file = -1 Then Return $line = 'GuiCreate("Test GUI", 400, 100, -1, -1, -1, 8)' & @CR & 'GuiSetState()' & _ @CR & 'TraySetIcon(@AutoItExe,-3)' & @CR & 'While 1' & @CR & _ 'If GuiGetMsg() = -3 Then Exit' & @CR & 'WEnd' FileWrite($file, $line) FileClose($file) ConsoleWrite(@CRLF & "> Creating test GUI processes" & @CRLF) For $i = 0 To $iGui - 1 $aPID[$i] = Run(@AutoItExe & " /AutoIt3ExecuteScript " & $sTemp & "\RunTestGUI.au3") WinWait("Test GUI", "", 2) If ProcessExists($aPID[$i]) Then $iCnt += 1 Next ConsoleWrite("- Closing " & $iCnt & " test GUI processes" & @CRLF) Sleep(2000) WinSetOnTop("Test GUI", "", 1) WinSetTitle("Test GUI", "", "ORPHANING TRAY ICONS") Sleep(2000) $iCnt = 0 For $i = 0 To $iGui - 1 ProcessClose($aPID[$i]) ProcessWaitClose($aPID[$i], 2) If Not ProcessExists($aPID[$i]) Then $iCnt += 1 Next ConsoleWrite("! Closed " & $iCnt & " processes - tray icons orphaned" & @CRLF) For $i = 0 To 10 Sleep(20) FileDelete($sTemp & "\RunTestGUI.au3") If Not FileExists($sTemp & "\RunTestGUI.au3") Then ExitLoop Next Sleep(3000) EndFunc ;==>_RunTestGUIUDF:_RefreshNotificationAreaIcons() UDF - Vista/2008/Win7/2008R2 - x86/x6464 bit OS support. Tested on Win 7-Win 2008R2_RefreshNotificationAreaIcons.au3_RefreshTrayIcons() UDF - Win2k/XP/2003 - x86 only_RefreshTrayIcons.au3Examples_RefreshTrayIcons_examples.au3 Edited December 6, 2011 by rover I see fascists... Link to comment Share on other sites More sharing options...
wraithdu Posted May 5, 2009 Share Posted May 5, 2009 Really thorough UDF, nice work!There's another SysTray UDF here - http://www.autoitscript.com/forum/index.php?showtopic=13704that uses the same method to get the owner's window handle. However it uses the TB_DELETEBUTTON message along with the icon index to remove icons. I'm not sure it will work for your purpose, but might be easier to implement than the Shell_NotifyIcon API. Link to comment Share on other sites More sharing options...
rover Posted May 5, 2009 Author Share Posted May 5, 2009 Really thorough UDF, nice work!There's another SysTray UDF here - http://www.autoitscript.com/forum/index.php?showtopic=13704that uses the same method to get the owner's window handle. However it uses the TB_DELETEBUTTON message along with the icon index to remove icons. I'm not sure it will work for your purpose, but might be easier to implement than the Shell_NotifyIcon API.Hi wraithdu, thanksI have a link up to Tuape's UDF, I see you've done some updating to it.I was using the TB_DELETEBUTTON method in the early version but switched to Shell_NotifyIconSendMessage TB_DELETEBUTTON is approx. 3x faster than Shell_NotifyIcon** but requires a WM_SIZE message with SIZE_RESTORED to be sent to Shell_TrayWnd to resize toolbar after icons are deleted, whereas Shell_NotifyIcon handles that internally. (Win 2000, WinXP and Vista)(**this is most noticeable if many orphaned icons are removed from the toolbar)I don't know if there are any drawbacks to deleting a tray toolbar button with TB_DELETEBUTTONinstead of the recommended API.I prefer the Shell_NotifyIcon NIM_DELETE method as it won't delete a button unless a matching owner handle and identifier is supplied, which along with a process and IsHWnd checkpractically eliminates the possibility of an accidental deletion of a valid tray icon.The Shell_TrayWnd window in WinXP and Vista (other OS untested but probably work) responds to WM_SIZE, but Win 2000 does not.so the early version of this UDF using TB_DELETEBUTTON added a hidden icon to the tray to trigger a re-size as a workaround for Win 2000. (tried many other methods to trigger a re-size without success)TraySetIcon("blank") ;set blank icon to trigger toolbar re-size on exitOpt("TrayIconHide", 1) ;required to hide blank icon so toolbar does not visibly re-size on exitI'm revising the run in service comment. It looks like it's only applicable to adding a tray icon for a servicewhich apparently is a security risk - the system not allowing explorer to send messages to higher security context service process (returned notification messages).The UDF sends messages to and reads from explorer memory but does not receive messages.cheers I see fascists... Link to comment Share on other sites More sharing options...
ken82m Posted March 18, 2010 Share Posted March 18, 2010 Does anyone know how to get this to work with Windows 7? Â "I believe that when we leave a place, part of it goes with us and part of us remains... Go anywhere, when it is quiet, and just listen.. After a while, you will hear the echoes of all our conversations, every thought and word we've exchanged.... Long after we are gone our voices will linger in these walls for as long as this place remains." Link to comment Share on other sites More sharing options...
rover Posted December 6, 2011 Author Share Posted December 6, 2011 Does anyone know how to get this to work with Windows 7? BLTN? I see fascists... Link to comment Share on other sites More sharing options...
Wh1sp3r Posted December 13, 2011 Share Posted December 13, 2011 Hi, I tried to run this on Windows 7 SP1 (64bits) and the 10 orphaned icons were still in the tray after the script ended. Is there something I'm missing? Link to comment Share on other sites More sharing options...
rover Posted December 15, 2011 Author Share Posted December 15, 2011 Hi, I tried to run this on Windows 7 SP1 (64bits) and the 10 orphaned icons were still in the tray after the script ended. Is there something I'm missing?Welcome to the forums Wh1sp3rThe example script was not updated for the x64 version, it only works with the x86 version.Just run _RefreshNotificationAreaIcons.au3 after running the example script. I see fascists... Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now