Jump to content

Properly display the NotifyIconOverflowWindow programmatically


Recommended Posts

Does anyone know the proper way to programmatically display the NotifyIconOverflowWindow so that the OS knows the window has been displayed and handles it correctly?

For anyone who doesn't already know, the NotifyIconOverflowWindow is where the system tray icons that aren't always visible are kept. It's what appears when you click on the ^ button to 'Show hidden icons':

image.png.11be95881605224561e218a01621a4a9.png

Here's the improper way:

WinSetState("[CLASS:NotifyIconOverflowWindow]", "", @SW_SHOW)

While this does make the icon overflow window visible, the routines that work the window haven't properly been told that it's visible, so the window doesn't always resize or work correctly and it no longer auto-hides until you click on that 'Show hidden icons' button a couple of times to get its visible state back into sync with what the OS thinks it should be.

A possible way to properly display it is to simulate a click on the button in the Shell_TrayWnd window like this:

#include <AutoItConstants.au3>

If BitAND(WinGetState("[CLASS:NotifyIconOverflowWindow]", ""), $WIN_STATE_VISIBLE) = 0 Then
    ControlClick("[CLASS:Shell_TrayWnd]", "", "Button2", "main")
EndIf

This method works nicely, but I'm concerned that the 'Show hidden icons' button may not always be Button2 on all Win10 and Win11 configurations. Is it?

What would seem preferable would be some API function to ask the OS to display that window so that it knows it just displayed it and handles the functionality of the window as normal. Anybody know that bit of API magic?

Link to comment
Share on other sites

You're probably on the right track, I didn't really find a good way to do it, and how you've done it is similar to how its been done before: 

 

However as you mentioned, it looks like that Melba coded Button 1, however for me it's also Button 2 (Windows 10 x64 with a shell replacement thing), so who knows how it'll change per system.

There are some ways that you can try to make sure you get the button, for instance if you search for/get all controls and their information, there's some things that point to the correct control:
EucLdLd.png

You can see that the button is visible, and it's the only 'Button' that is. It's also got a width x height of: 24 x 30, with a position near the right edge of the screen (3440px width screen, and it's at 3224). To generate these controls I'm using the function from here (modified some to store them in an array):

 

I don't know if there's some better way to trigger the button and/or popup, using something like a Message (using SendMessage or PostMessage) or some DllCall. ControlClick isn't always the best way to do it, and you may need to first ControlFocus or WinActivate for ControlClick to work correctly.

Edited by mistersquirrle

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

@mistersquirrle Thanks for the input.

I'm finding that ControlClick is working reliably (so far) every time for this use, without having to ControlFocus or WinActivate.

I've ended up with the following code that seems to be working well. It assumes that the first visible Button control it finds on the Shell_TrayWnd window is the ^ button. On my Win10 64 system, like yours, that's the only visible Button control that's found even if I turn on all of the stuff (toolbars, search, news, cortana, task view, people, ink, keyboard) that I normally keep turned off.

I also tested this to see what would happen if you had your notification settings like this:

image.png.e1d50dc207fcacdc7f5f4dd6512b7027.png

Even if you do this so the overflow area isn't used, the button for it is still listed as visible by the code (even if not visible to the eye onscreen) but ControlClick-ing it does nothing good or bad.

#include <WinAPISysWin.au3>

Example()

Func Example()
    Local $id = _GetTrayOverflowButtonID()
    If $id <> 0 Then
        Local $hWnd = @extended
        ControlClick("[CLASS:Shell_TrayWnd]", "", $id, "main")
        MsgBox(0, "Tray Overflow Button", "ID = " & $id & @TAB & "Handle = 0x" & Hex($hWnd))
    EndIf
EndFunc   ;==>Example

Func _GetTrayOverflowButtonID()
    Local $aControls = _WinAPI_EnumChildWindows(WinGetHandle("[CLASS:Shell_TrayWnd]"), True) ; only enumerate visible controls
    Local $idBtn = 0, $hBtn = 0
    If IsArray($aControls) Then
        For $i = 1 To $aControls[0][0]
            If $aControls[$i][1] = "Button" Then
                ; there should only be one visible Button control
                $hBtn = $aControls[$i][0]
                $idBtn = _WinAPI_GetDlgCtrlID($hBtn)
                ExitLoop
            EndIf
        Next
    EndIf
    Return SetExtended($hBtn, $idBtn) ; return control ID (with hWnd in @extended)
EndFunc   ;==>_GetTrayOverflowButtonID

Anyone care to try this on a Win11 system to see if it works there?

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...