Sign in to follow this  
Followers 0
therks

Close traytip (tray balloons) via AutoIt

23 posts in this topic

Hey there, I've been trying to figure this out for a little while, but I've gotten stumped. I just want a way to quickly close TrayTips without clicking on them (Will use a hotkey).

I know it's simple code, but I'm having trouble identifying the traytip window.

Opt('WinTitleMatchMode', 4)
HotKeySet('#{SPACE}', '_ClearTrayTips')

While 1
  Sleep(100)
WEnd

Func _ClearTrayTips()
  $aTips = WinList('classname=tooltips_class32')
  For $i = 1 to $aTips[0][0]
    WinClose($aWins[$i][1])
  Next
EndFunc

It can't be much more complex than that, right? But the tooltips_class32 classname is attached to more than just TrayTips, so I'm afraid of closing things that should be left open. Any ideas on how I can filter it down to just the TrayTip?

Share this post


Link to post
Share on other sites



Well I don't have the slightest clue what the fu** I did, but I made it so tray icons don't even pop-up anymore on my computer whether the script is running or not lol. I turned off windows firewall and it made a weird little sound, but nothing popped up. Like I said, I don't have a clue why this happens but hey, it'll stop them lol...

Opt('WinTitleMatchMode', 4)
HotKeySet('{Del}', '_ClearTrayTips')

While 1
  Sleep(100)
WEnd

Func _ClearTrayTips()
    $var = WinList('classname=tooltips_class32')
    For $i = 1 to $var[0][0]
        WinKill($var[$i][1])
    Next
EndFunc

Be careful, I'm not sure why this works the way it does, and I'm not real great with AutoIt yet...


- Dan [Website]

Share this post


Link to post
Share on other sites

Here's a shot, if your only trying to kill it in your system tray

Opt('WinTitleMatchMode', 4)
HotKeySet('{END}', '_ClearTrayTips')

While 1
  Sleep(100)
  _ClearTrayTips()
WEnd

Func _ClearTrayTips()
    $OptWTMM = Opt('WinTitleMatchMode', 4)
    $OptMCM = Opt('MouseCoordMode', 2)
    Local $aTips = WinList('classname=tooltips_class32')
    Local $aCPos = ControlGetPos('classname=Shell_TrayWnd', '', 'ToolbarWindow321')
    Local $aMpos = MouseGetPos()
    For $iCC = 1 to $aTips[0][0]
        If IsArray($aCPos) And IsArray($aMpos) And _
            $aMpos[0] >= $aCPos[0] And _
            $aMpos[0] <= $aCPos[0] + $aCPos[2] And _
            $aMpos[1] >= $aCPos[1] And _
            $aMpos[1] <= $aCPos[1] + $aCPos[3] And _
            BitAND(WinGetState($aTips[$iCC][1]), 2) Then
            WinClose($aTips[$iCC][1])
        EndIf
    Next
    Opt('WinTitleMatchMode', $OptWTMM)
    Opt('MouseCoordMode', $OptMCM)
    Return 1
EndFunc


[center]Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.[/center]

Share this post


Link to post
Share on other sites

Actually, it turns out that if I actually close (WinClose, WinKill) the traytip (or any tooltip for that matter) it won't popup again until explorer.exe is restarted. I'm guessing that's what happened for you, dandymcgee. I guess when you click a traytip it doesn't close it, it hides it or something, but when I tried hiding it with WinSetState, the majority of the bubble vanishes, but it left it's shadow behind.

Share this post


Link to post
Share on other sites

If you actually can find the balloon tip, then try sending ControlClick() to it. You definitely do not want to close that window, it belongs to Explorer, as you've found out. All balloon tips shown in the notification area in that manner use an Explorer controlled balloon tip.

Share this post


Link to post
Share on other sites

If you actually can find the balloon tip, then try sending ControlClick() to it. You definitely do not want to close that window, it belongs to Explorer, as you've found out. All balloon tips shown in the notification area in that manner use an Explorer controlled balloon tip.

Unfortunately, the problem I've encountered with this method is that sometimes a window opens unless the [x] on the balloon is clicked on specifically (The balloon that appears when my wireless network can't connect) this is more the reason I wanted the script. It's easy enough to just click on the balloon, but making sure I hit that [x] with my touchpad was annoying. I'm thinking what I'll do is grab the balloon coords, calculate where the [x] should be and click it. It seems to always the same sized [x] and in the same position from the top right corner of the balloon, regardless of the message or options. Think that will work?

Share this post


Link to post
Share on other sites

The only thing you need to keep in mind is not all balloon tips will have a close box. Use TrayTip() without a title to confirm that. Outside of that advice, using MouseClick() really is about all you can do. I just spent a few minutes watching messages as I closed the balloon but I couldn't see anything particularly special that could be used.

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

On second thought, I did notice something that might be useful. When the shell's balloon tip exists (and is visible), it looks like the very next handle will be to it's SysShadow window (The shadow you saw left behind). Theoretically, the following would work (Requires some DllCall() stuff).

  • Use whatever method you currently use to detect when a balloon tip is visible.
  • Immediately call the Windows API function GetNextWindow() passing in the HWND of the balloon tip and GW_HWNDNEXT.
  • Use the handle returned by GetNextWindow() to detect if it's of class SysShadow and that it is visible (This is probably not necessary but I definitely recommend verifying you're closing the correct shadow).
  • Use WinClose() on the SysShadow handle.
  • Use WinSetState() with @SW_HIDE on the balloon tip.
This will hide the balloon and destroy its shadow which should be safe to do. The shadow is re-created every time the balloon tip is shown after it's been hidden.

Edit: Spell check needs to learn to read my mind. Spelling a word right but using the wrong word sucks.

Edited by Valik

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

Wow, thanks so much, you have solved my problem. Sorta, heh.

The only thing I'm worried about now is how unreliable my method for discerning traytips is. Using GetWindow (Aside: When I tried GetNextWindow, I got @error = 3; function not in DLL. So I'm using GetWindow with the GW_HWNDNEXT flag as noted in the Remarks for GetNextWindow on MSDN) I'm currently just running through all tooltips_class32 windows and checking their "next window." When I hit one where the next window is a class of SysShadow I assume that it's the traytip I'm looking for.

I'm not sure if this will have me running into errors, but at the moment the worst I can see is that a tooltip with a shadow will be hidden and lose it's shadow. Any comments/suggestions on this?

Anyway, here's the function I have.

Deprecated function was here. See below.

Interesting side note: Notice the commented line. This is what I had originally instead of the AnimateWindow line, but I noticed two things when I just hid the window. 1) Normally when you click on a traytip it fades out. And 2) There was a noticable pause between the window disappearing, and the shadow disappearing. So I decided to switch to the fadeout Animatewindow. Unfortunately, instead of fading out, the window just disappears right away, BUT, there's no noticable pause between the window and the shadow disappearing. I found it odd myself.

Again, thanks for the help, and any more input is more than welcome.

*Edit: Forgot to include the constants in my code sample.

Edited by Saunders

Share this post


Link to post
Share on other sites

Come to think of it, GetNextWindow() is probably a pre-processor macro that is expanded to a GetWindow() call so I'm not surprised it's not in the DLL.

There are much more reliable ways to determine the correct balloon tip.

  • See if the control has a style of TTS_BALLOON (0x40).
  • See if the parent window is class "Shell_TrayWnd". Use AutoIt to get a handle to the window with that class and then use the Windows API GetParent() to get the parent of the tooltip and finally compare the two handles.
If you do both of those I'd say you have a 99% chance of being right.

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Cool, thanks again, I'll be sure to check those out. But I have to admit I just found one strange problem. I used the function I have now to hide the traytip from my wireless connection, and it seemed to work fine, but when the script ended the traytip reappeared. I added a #NoTrayIcon to the top of the script and, again, it seemed to work fine, but then if any other tray icons move the balloon reappears again. I guess when the tray's icons move it refreshes balloon tips.

I've discovered since though, that if I WinActivate the tip and Send('{ESC}') it closes the balloon very nicely. Can't believe I never thought of that in the first place. Nevermind, I must have done something else the first couple times cus it's not working now.

*Edit: Cleaned up the post a little.

Edited by Saunders

Share this post


Link to post
Share on other sites

Meh. Forgive me for being brain-dead this entire time. I was looking at the problem from the standpoint of treating it as a window and instead I needed to treat it like the control it was. Write some code to reliably find the balloon tip using the comments from above. Once you have it's HWND, use SendMessage() to send TTM_TRACKACTIVATE with wParam and lParam set to 0. This will hide the balloon tip the correct way. Sending that message manually is the exact same thing that happens when the balloon times out or the application that wanted it closes taking it's tray icon with it. That is the proper way to do it.

Share this post


Link to post
Share on other sites

Awesome, now THAT works perfectly. I have one question though, and I feel stupid asking this, sorry, but earlier you mentioned "See if the control has a style of TTS_BALLOON (0x40)." I don't know how to check the style though.

Share this post


Link to post
Share on other sites

GetWindowLong() with the flag GWL_STYLE and use BitAnd() to test for that specific flag.

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

Thanks again Valik, I know you don't like stupid questions (or people) so I didn't really want to have to ask such a simple seeming question. :)

Anyway, here's what I've cooked up with your help.

Func _CloseTips()
    Local Const $GWL_STYLE = -16
    Local Const $GW_HWNDNEXT = 2
    Local Const $TTM_TRACKACTIVATE = 0x411
    
    Local $i_PrevWTMM = Opt('WinTitleMatchMode', 4)
    Local $a_GetStyle, $a_GetParent, $a_GetClass, $a_NextWin, $a_Tips
    Local $h_User32 = DllOpen('user32.dll')
    
    $a_Tips = WinList('classname=tooltips_class32')
    For $i = 1 to $a_Tips[0][0]
        If BitAND(WinGetState($a_Tips[$i][1]), 2) Then
            $a_GetStyle = DllCall($h_User32, 'int', 'GetWindowLong', 'hwnd', $a_Tips[$i][1], 'int', $GWL_STYLE)
            If @error Then Return SetError(1, @error, 0)
            If BitAND($a_GetStyle[0], 0x40) Then
                $a_GetParent = DllCall($h_User32, 'hwnd', 'GetParent', 'hwnd', $a_Tips[$i][1])
                If @error Then Return SetError(2, @error, 0)
                $a_GetClass = DllCall($h_User32, 'int', 'GetClassName', 'hwnd', $a_GetParent[0], 'str', '', 'int', 255)
                If @error Then Return SetError(3, @error, 0)
                If $a_GetClass[2] = 'Shell_TrayWnd' Then
                    $a_NextWin = DllCall($h_User32, 'hwnd', 'GetWindow', 'hwnd', $a_Tips[$i][1], 'int', $GW_HWNDNEXT)
                    If @error Then Return SetError(4, @error, 0)
                    $a_GetClass = DllCall($h_User32, 'int', 'GetClassName', 'hwnd', $a_NextWin[0], 'str', '', 'int', 255)
                    If @error Then Return SetError(5, @error, 0)
                    If $a_GetClass[2] = 'SysShadow' Then
                        DllCall($h_User32, 'int', 'SendMessage', 'hwnd', $a_Tips[$i][1], 'int', $TTM_TRACKACTIVATE, 'int', 0, 'int', 0)
                    EndIf
                EndIf
            EndIf
        EndIf
    Next

    DllClose($h_User32)
    Opt('WinTitleMatchMode', $i_PrevWTMM)
EndFunc

And as near as I've seen, it seems to work like a hot damn.

Edited by Saunders

Share this post


Link to post
Share on other sites

I don't think you need to be that rigid about finding the window. This should probably suffice. I've also fixed a logic error. You were saving the state of WinTitleMatchMode and restoring it on return - but only if the function succeeded. If the function encountered an error, you just returned without restoring the state (or closing the DLL handle). I've put the bulk of the code in a one-run loop so that instead of returning on error, you set a couple variables and jump out of the loop to the cleanup code at the bottom.

Func _CloseTips()
    Local Const $GWL_STYLE = -16
    Local Const $TTM_TRACKACTIVATE = 0x411
    Local Const $TTS_BALLOON = 0x40

    Local $i_PrevWTMM = Opt('WinTitleMatchMode', 4)
    Local $a_GetStyle, $a_GetParent, $a_Tips
    Local $h_User32 = DllOpen('user32.dll')
    Local $hTray = WinGetHandle("classname=Shell_TrayWnd")
    $a_Tips = WinList('classname=tooltips_class32')
    Local $nError = 0, $nExtended = 0
    Do
        For $i = 1 to $a_Tips[0][0]
            If BitAND(WinGetState($a_Tips[$i][1]), 2) Then
                $a_GetStyle = DllCall($h_User32, 'int', 'GetWindowLong', 'hwnd', $a_Tips[$i][1], 'int', $GWL_STYLE)
                If @error Then
                    $nError = 1
                    $nExtended = @error
                    ExitLoop 2
                EndIf
                If BitAND($a_GetStyle[0], $TTS_BALLOON) Then
                    $a_GetParent = DllCall($h_User32, 'hwnd', 'GetParent', 'hwnd', $a_Tips[$i][1])
                    If @error Then
                        $nError = 2
                        $nExtended = @error
                        ExitLoop 2
                    EndIf
                    If $hTray And $a_GetParent[0] = $hTray Then
                        DllCall($h_User32, 'int', 'SendMessage', 'hwnd', $a_Tips[$i][1], 'int', $TTM_TRACKACTIVATE, 'int', 0, 'int', 0)
                    EndIf
                EndIf
            EndIf
        Next
    Until True

    DllClose($h_User32)
    Opt('WinTitleMatchMode', $i_PrevWTMM)
    Return SetError($nError, $nExtended, 0)
EndFunc

Share this post


Link to post
Share on other sites

#17 ·  Posted (edited)

I don't think you need to be that rigid about finding the window. This should probably suffice. I've also fixed a logic error. You were saving the state of WinTitleMatchMode and restoring it on return - but only if the function succeeded. If the function encountered an error, you just returned without restoring the state (or closing the DLL handle). I've put the bulk of the code in a one-run loop so that instead of returning on error, you set a couple variables and jump out of the loop to the cleanup code at the bottom.

Right you are. Thanks for the fix. I'll have to remember this one-run loop trick for other scripts, I can think of times where it would have been handy.

*Edit: One little note. Now that I think about it, it should be all right to exit the loop after sending TTM_TRACKACTIVATE, shouldn't it? I mean, as near as I know, there should never be more than one traytip showing, correct?

Edited by Saunders

Share this post


Link to post
Share on other sites

Correct. Only one may show at a time. Exiting the loop after finding the correct tooltip would be a good idea.

Share this post


Link to post
Share on other sites

Hello, guys I'm a n00b. :shocked: I use software that blocks registry writes, but each time the software blocks, it displays balloon tip and after sometimes it becomes annoying. The balloon tip always display this text: "...protected registry".

I have been trying to keep up with the conversations but unfortunately I could not make the script to work. From what I read "TTM_TRACKACTIVATE" is used to close the balloon tip, so it should be in:

DllCall($h_User32, 'int', 'SendMessage', 'hwnd', $a_Tips[$i][1], 'int', $TTM_TRACKACTIVATE, 'int', 0, 'int', 0)

After trial and error I couldn't make it to work, even with any balloon tip. :( And I also try a n00bish test to know if I could treat it as a window and if it able to differentiate balloon tip from other applications, but also no luck. I tried this code with result it hides any balloon tip even with "protected":

Opt('WinTitleMatchMode', 4)

While 1
  Sleep(20)
  _ClearTrayTips()
WEnd

Func _ClearTrayTips()
    $var = WinList('classname=tooltips_class32')
    For $i = 1 to $var[0][0]
        WinSetState($var[$i][1],"protected",@SW_HIDE)
    Next
EndFunc

To make it short I need a code so the balloon tip from the software could be identified; and a code to close the balloon tip properly (I believe it uses TTM_TRACKACTIVATE). I'm sorry if I ask stupid questions.

Thank you.

Share this post


Link to post
Share on other sites

Hello, guys I'm a n00b. :shocked: I use software that blocks registry writes, but each time the software blocks, it displays balloon tip and after sometimes it becomes annoying. The balloon tip always display this text: "...protected registry".

I have been trying to keep up with the conversations but unfortunately I could not make the script to work. From what I read "TTM_TRACKACTIVATE" is used to close the balloon tip, so it should be in:

DllCall($h_User32, 'int', 'SendMessage', 'hwnd', $a_Tips[$i][1], 'int', $TTM_TRACKACTIVATE, 'int', 0, 'int', 0)

After trial and error I couldn't make it to work, even with any balloon tip. :( And I also try a n00bish test to know if I could treat it as a window and if it able to differentiate balloon tip from other applications, but also no luck. I tried this code with result it hides any balloon tip even with "protected":

Opt('WinTitleMatchMode', 4)

While 1
  Sleep(20)
  _ClearTrayTips()
WEnd

Func _ClearTrayTips()
    $var = WinList('classname=tooltips_class32')
    For $i = 1 to $var[0][0]
        WinSetState($var[$i][1],"protected",@SW_HIDE)
    Next
EndFunc

To make it short I need a code so the balloon tip from the software could be identified; and a code to close the balloon tip properly (I believe it uses TTM_TRACKACTIVATE). I'm sorry if I ask stupid questions.

Thank you.

You can do this by using the native ToolTip API calls. Here's a little ditty that will close a ToolTip based on it's text. Run it and then put your cursor over the AutoIt icon in the system tray. You'll need the beta version of AutoIt and Auto3Lib to run this:

#include <A3LToolTip.au3>

GUICreate("ToolTip Test", 200, 100)
GUISetState()

do
  _Lib_PopupClear()
  _Lib_PopupScan ()
  ; See if we found a ToolTip
  if (_Lib_PopupCount() > 0) and (_Lib_PopupGetType() = 3) then
    ; Get the handle to the ToolTip
    $hToolTip = _Lib_PopupGetHwnd()
    ; Get the ToolTip info
    $aToolInfo = _ToolTip_GetCurrentTool($hToolTip)
    ; See if it's a ToolTip to our program and close it if it is
    if StringInStr($aToolInfo[8], "AutoIt - ") > 0 then _ToolTip_Pop($hToolTip)
  endif
until GUIGetMsg() = $GUI_EVENT_CLOSE

Auto3Lib: A library of over 1200 functions for AutoIt

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0