System tray button count

Discuss other programming languages besides AutoHotkey
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

System tray button count

10 Dec 2022, 15:14

Hi Folks,

My knowlege of VisualBasic and PowerShell is minimal, so hoping to get some help here. I'd like a sample script (for submitting a Windows 11 issue to Microsoft) in one of those languages that displays the count of the icons in the system tray, something equivalent to these two lines of AHK code that call a function in the TrayIcon library:

Code: Select all

TrayInfo:=TrayIcon_GetInfo()
MsgBox % TrayInfo.MaxIndex()
The script should have no AHK code, so it needs to utilize the TB_BUTTONCOUNT message for the system tray, as the TrayIcon library does in the TrayIcon_GetInfo function. Thanks much, Joe
skyth540
Posts: 72
Joined: 24 Aug 2022, 10:07

Re: System tray button count

31 Dec 2022, 04:01

Here is a sample VisualBasic script that displays the count of icons in the system tray by using the TB_BUTTONCOUNT message:

Code: Select all

Imports System.Runtime.InteropServices

Public Class Form1

    Private Const TB_BUTTONCOUNT As Integer = WM_USER + 24

    Private Const WM_USER As Integer = &H400

    Private Const TB_GETBUTTON As Integer = (WM_USER + 23)

    Private Const TB_GETBUTTONINFO As Integer = (WM_USER + 65)

    Private Const TB_HITTEST As Integer = (WM_USER + 69)

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
    Public Structure TBBUTTON
        Public iBitmap As Integer
        Public idCommand As Integer
        Public fsState As Byte
        Public fsStyle As Byte
        Public bReserved0 As Byte
        Public bReserved1 As Byte
        Public dwData As IntPtr
        Public iString As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
    Public Structure TBBUTTONINFO
        Public cbSize As Integer
        Public dwMask As Integer
        Public idCommand As Integer
        Public iImage As Integer
        Public fsState As Byte
        Public fsStyle As Byte
        Public cx As Byte
        Public lParam As IntPtr
        Public pszText As String
        Public cchText As Integer
    End Structure

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
    End Function

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim trayHandle As IntPtr = FindWindow("Shell_TrayWnd", "")
        Dim buttonCount As Integer = SendMessage(trayHandle, TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero)
        MessageBox.Show(String.Format("There are {0} icons in the system tray.", buttonCount))
    End Sub

End Class
To use this script, create a new Windows Forms Application project in Visual Studio and paste the code into the Form1.vb file. Then, run the project to display a message box with the count of icons in the system tray.

Here is the equivalent PowerShell script:

Code: Select all

Add-Type -TypeDefinition @"
using System.Runtime.InteropServices;

namespace SystemTray
{
    public class SystemTray
    {
        private const int TB_BUTTONCOUNT = 0x0424;
        private const int WM_USER = 0x0400;
        private const int TB_GETBUTTON = (WM_USER + 23);
        private const int TB_GETBUTTONINFO = (WM_USER + 65);
        private const int TB_HITTEST = (WM_USER + 69);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct TBBUTTON
        {
            public int iBitmap;
            public int
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

31 Dec 2022, 04:39

Hi @skyth540,

Thank you for the response...very much appreciated!

Re the VB code, I don't have Visual Studio installed and am wondering if it is possible to put that code in a stand-alone .VBS file with a msgbox command to show the result.

Re the PowerShell code, it's missing lines...probably a bad copy/paste into the code block. Please try posting again...thanks!

Regards, Joe
User avatar
mikeyww
Posts: 26977
Joined: 09 Sep 2014, 18:38

Re: System tray button count

31 Dec 2022, 07:58

The script should have no AHK code,
It just seems to me that you have the wrong forum if you are looking for a script in a different language.

[Moderator note: Agreed. Moved to appropriate forum.]
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

31 Dec 2022, 12:24

Thanks for the comment, mikey, and thanks to the Mod for moving it...much appreciated! I didn't even realize that there's an "Other Programming Languages" forum. Regards, Joe
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: System tray button count

01 Jan 2023, 01:42

JoeWinograd wrote:
31 Dec 2022, 04:39
Re the VB code, I don't have Visual Studio installed and am wondering if it is possible to put that code in a stand-alone .VBS file with a msgbox command to show the result.
It is not. Visual Basic (.NET), PowerShell and C# all compile to a .NET assembly. VBScript is a completely different beast, older and far more primitive.

You can compile and execute VB and C# code with only the .NET Framework installed. CLR.ahk can be used to interface with the compiler, and can instruct it to produce an EXE which can run independently of AutoHotkey. I've never used the C# or VB compiler directly from the command line, but I assume that is a valid alternative.
skyth540 wrote:Here is the equivalent PowerShell script:
Funny that you call it a PowerShell script, when it looks like all or most of the work would be done using C# code.
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

01 Jan 2023, 18:45

lexikos wrote:It is not.
Thanks for letting me know that...and for the subsequent comments...all very helpful!

For anyone interested in this issue, a colleague at another forum wrote this VBA code for me:

Code: Select all

Option Explicit

Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr

Const WM_USER = &H400
Const TB_BUTTONCOUNT = (WM_USER + 24)

Public Sub TestCount()
    Dim hTaskBar As LongPtr
    Dim lNumToolbar As LongPtr
    Dim lNumOverflow As LongPtr
    Dim sText As String

    sText = ""

    hTaskBar = GetToolbar("Shell_TrayWnd,TrayNotifyWnd,SysPager,ToolbarWindow32")
    If hTaskBar <> 0 Then
        lNumToolbar = SendMessage(hTaskBar, TB_BUTTONCOUNT, 0, ByVal 0&)
        sText = sText & "Toolbar Buttons : " & lNumToolbar & vbCrLf
    Else
        sText = sText & "*ERROR* Could not find Main Toolbar." & vbCrLf
    End If

    hTaskBar = GetToolbar("NotifyIconOverflowWindow,ToolbarWindow32")
    If hTaskBar <> 0 Then
        lNumOverflow = SendMessage(hTaskBar, TB_BUTTONCOUNT, 0, ByVal 0&)
        sText = sText & "OverFlow Buttons : " & lNumOverflow & vbCrLf
    Else
        sText = sText & "*ERROR* Could not find Overflow Toolbar." & vbCrLf
    End If

    MsgBox sText, vbOKOnly, "Done"

End Sub

Private Function GetToolbar(sWindowList As String) As LongPtr
    Dim lhWnd As LongPtr
    Dim aWindowList() As String
    Dim i As Integer

    If sWindowList = "" Then
        Exit Function
    End If

    aWindowList = Split(sWindowList, ",")

    lhWnd = FindWindow(aWindowList(0), vbNullString)
    For i = 1 To UBound(aWindowList)
        lhWnd = FindWindowEx(lhWnd, 0&, aWindowList(i), vbNullString)
    Next i

    GetToolbar = lhWnd
End Function

It is designed to run in Excel. Here's the result when run on the latest build of W10 22H2 with the latest Excel 365:

W10-result-works.png
W10-result-works.png (2.66 KiB) Viewed 6277 times

Works great!

Here's the result when run on the latest build of W11 22H2 with the latest Excel 365 (there are numerous buttons in both trays):

W11-result-fails.png
W11-result-fails.png (3.74 KiB) Viewed 6277 times

FAILS!

These are the same results as the two-line AHK script in my initial post. Regards, Joe
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: System tray button count

01 Jan 2023, 20:30

I have 22621.963 on my main computer, but found I have 22623.1037 on my laptop. Comparing the two, I see the cause of the issue quite clearly.

For the overflow area, it is not exactly that the toolbar messages are not working, but that the window and control you are looking for doesn't exist. The overflow window class is now "TopLevelWindowForOverflowXamlIsland" rather than "NotifyIconOverflowWindow", and it does not contain a Win32 toolbar control.

Errors like this are immediately obvious in v2, because an error is thrown when you attempt to do things with a window or control that does not exist.

As for the main tray, there are still toolbar controls with the text "System Promoted Notification Area" and "User Promoted Notification Area", but they have zero width and height, and are not visible.

It appears that the taskbar and overflow area now use XAML to represent the tray icons. You may notice that the highlight around tray icons now has rounded corners like the rest of the UI, whereas older builds had a rectangular highlight with sharp edges like Windows 10. I believe this was just a matter of time, as most of the Windows 11 UI uses XAML. It wouldn't make sense to embed Win32 controls within that indefinitely, especially given that they don't support things like legacy toolbars on the taskbar.

This change of controls is clearly by design. Perhaps they could insert buttons into the hidden toolbar control as well to make your script work - maybe this is the purpose for which they have kept the two toolbar windows - but it seems very unlikely to me, as they assuredly never supported the use of toolbar messages as a means of manipulating the tray icons. I would suggest considering alternatives.
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

01 Jan 2023, 21:45

lexikos wrote:Comparing the two, I see the cause of the issue quite clearly.
Thanks for figuring that out and providing the explanation.
lexikos wrote:I would suggest considering alternatives.
I'll gladly consider any alternatives, even if they work only with W11/22623.1020+. I already started doing that, but coming up short. I've been using four functions from the TrayIcon library:

TrayIcon_GetInfo - But I need this only for the other functions, so if I can find alternatives to the functions below, I don't need this one.

TrayIcon_Remove - I have an alternative approach for what I was doing with this. Need to solve only the two below.

TrayIcon_Button - This is the most important. I use it to establish hotkeys that right-click the tray icons so that physically impaired users who have trouble using a mouse can use the keyboard to expose the tray icon's context menu. Do you know an alternative way to do this?

TrayIcon_Move - Several of my programs have multiple instances running, each with its own (different) icon. Their position in the tray is important to users, so I offer three tools to position them: (1) Move All Icons to Beginning of Tray, (2) Move All Icons to End of Tray, (3) Put Icons in Custom Order (based on a user-specified configuration file). Do you know an alternative way to do this?

Thanks much, Joe
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: System tray button count

01 Jan 2023, 23:28

I meant "alternatives" pretty broadly, including changing your UI to use more conventional patterns built on officially supported APIs, giving up certain features... or supporting only older builds.

These are tray icons created by your scripts? Why automate the tray icons when you can automate the script itself? You can show a script's tray menu by sending it the same notification message that the tray would send: AHK_NOTIFYICON (1028) with lParam = WM_RBUTTONUP (0x205). These are "documented" in the source code, but if you are not comfortable with relying on that, you can implement your own messages or some other form of IPC.

Tray icons must be readable by screen readers and other accessibility software, so can likely be read by MSAA and UI Accessibility, which can also determine the icon's location or set focus to it. However, it's likely that you'll only get the location and tooltip, not any details that identify which process owns it. Maybe that's not an issue if the tooltips are unique.

I doubt there is any way to reorder a XAML-based set of icons/buttons from outside the application which created them, aside from using the mouse. The user can still re-order them with drag-drop, and as far as I can tell, the order persists across reboots. I think that the order is persisted in the same place as the icon's visibility; in the TrayNotify registry key. I've read that Microsoft obfuscated this and specifically chose not to provide programmatic access, to put the user in control (or so they say). Others have apparently worked out that the content of the key uses the ROT13 cipher. You could do some research and try your luck. I suspect that it won't work well for multiple instances of the same application (e.g. AutoHotkey.exe), but maybe for separate compiled scripts.
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

02 Jan 2023, 00:48

lexikos wrote:giving up certain features
Yes, I'm starting to do that. For example, one program has a feature to create a tray icon report, either for all icons in the tray or just for that program's icons (there can be many). The report contains each icon's position number in the tray, its owning process, tooltip, and which tray it is in (Shell_TrayWnd or NotifyIconOverflowWindow). It's a useful report, but at this point, I'm willing to give it up on W11.
lexikos wrote:or supporting only older builds
Agreed. For example, I may keep the tray icon report on earlier W11 builds and all W10 (and prior) releases.
lexikos wrote:These are tray icons created by your scripts?
Each instance of the program has its own icon. For example, one program has various color stars in the tray, such as Blue, Green, Red, Yellow (and lots more):

color star tray icons.png
color star tray icons.png (2.16 KiB) Viewed 6194 times

Another program allows the user to toggle Caps Lock, Num Lock, and Scroll Lock via tray icons. Each key's icon is red when it's off and green when it's on:

caps num scroll lock tray icons.png
caps num scroll lock tray icons.png (1.42 KiB) Viewed 6194 times

Both of those programs have a feature that allows the user to move the icons in the tray. For example, the Caps Lock, Num Lock, and Scroll Lock icons have this context submenu hanging off a Tools menu:

move tray icons.png
move tray icons.png (2.08 KiB) Viewed 6194 times

The process name is the same for all instances of the same program. The various programs are compiled with Ahk2Exe and they all have an installer (a Setup.exe file) created with NSIS.
lexikos wrote:Why automate the tray icons when you can automate the script itself?
The programs have plenty of automation, but I chose to have the UI for these programs center around the context menus of the system tray icons, although many of the context menu choices lead to "normal" windows, many of which contain GUIs, ListViews, etc.
lexikos wrote:You can show a script's tray menu by sending it the same notification message that the tray would send: AHK_NOTIFYICON (1028) with lParam = WM_RBUTTONUP (0x205).
That seems to be what the TrayIcon_Button function does, which is not working on the recent W11 builds. It would be great if I can assign a hotkey to that for each instance, such as Alt+Ctrl+B to open the context menu of the Blue star.
lexikos wrote:Maybe that's not an issue if the tooltips are unique.
Correct...not an issue...each running instance has a unique tooltip.
lexikos wrote:The user can still re-order them with drag-drop
Yes. That had stopped working in W11 Build 22581, but Microsoft fixed it. Interesting thread with Microsoft on that issue (8 months ago):
Microsoft Response: We'll be continuing to monitor this feedback, but with the updates we made for the new tablet-optimized taskbar in Build 22563, we're no longer supporting dragging icons in the system tray or between the system tray and the show hidden icons flyout. Instead, you should use the Settings > Personalization > Taskbar > System tray section to manage these icons.

UPDATE: We have decided to disable the changes to the system tray introduced in Build 22581 for now. The system tray and the Show hidden icons flyout will now function the same way it did with the original release of Windows 11, including the ability to rearrange icons in the flyout. We hope to bring these changes back in the future after further refinement of the experience by addressing some of this feedback. This is now available with Build 22616, now available in the Dev and Beta Channels: https://aka.ms/wip22616
lexikos wrote:as far as I can tell, the order persists across reboots
I don't know...haven't tested that.
lexikos wrote:but maybe for separate compiled scripts
As noted above, each of my programs is a separately compiled script, each with its own (Setup.exe) installer, so that may work.

Thanks for all your thoughts on this...much appreciated! Regards, Joe
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: System tray button count

02 Jan 2023, 03:45

That seems to be what the TrayIcon_Button function does, which is not working on the recent W11 builds.
TrayIcon_Button fails because it relies on the information returned by TrayIcon_GetInfo, which fails. Specifically, it tries to get the message number, ID and HWND based on the process name you gave it. You don't need to get that from the tray, because I told you what the message number is, AutoHotkey ignores the ID number when handling the message, and you can find the HWND with WinExist or WinGet List (or directly address the script's main window by title instead).

The ID number is the same as the message number (1028). This is used with the Shell_NotifyIcon function to add, modify or delete the tray icon ...

Oh. :idea:

If your alternative to TrayIcon_Remove isn't working out, you might consider using Shell_NotifyIcon. See KillTrayIcon.

As noted above, each of my programs is a separately compiled script
But is each icon a separate program? Based on your description, I'm going to guess that the stars are all one program, and the keyboard indicator icons are all a second program. That meaning two programs with multiple instances each, not one program per icon. My point was that Windows might not differentiate between multiple icons belonging to one program, with respect to representing them in the Settings app, persisting them when the program terminates, or restoring the order when new instances start.

That might work out in your favor. I'd guess that if Windows remembers what order the icons should be in, it will put them in that order, ignoring the order that the tray icons were most recently added to the tray. If it doesn't remember the order of individual icons, maybe it will leave them in the order that they are added to the tray. In that case, you can remove them and add them back in the appropriate order.
I don't know...haven't tested that.
I know, because I tested it, hence my words. But I had only tested it on build 22623.1037 with separate programs, one icon per program.

Now I've tested with multiple instances of one script. What I have observed is:
  • Windows will refuse to split the icons apart; they are always grouped together. If you try to drag-drop some other icon between the script's icons, it will end up at either end.
  • If you drag one icon to or from the overflow window, the other icons will follow. You cannot "promote" or "demote" the individual icons, only the app to which they belong.
  • Drag-drop within the group does not work as expected; whichever icon you drag ends up at the far right.
  • The newest icon is placed on the left, so if you remove the icon and add it back, it will move to the far left of the group.
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

02 Jan 2023, 15:01

Hi lexikos,

Sorry for the delayed response. I'm in the U.S. Central time zone and had packed it in for the night.
lexikos wrote:You don't need to get that from the tray, because I told you what the message number is, AutoHotkey ignores the ID number when handling the message, and you can find the HWND with WinExist or WinGet List
Ah, got it! Missed that the first time around.
lexikos wrote:or directly address the script's main window by title instead
There is no main window that is always running. The only time a desktop window appears is when the user opens one via the tray context menu (or runs the program's desktop or Start menu shortcut, but that main window goes away after loading the color star icons into the tray).
lexikos wrote:If your alternative to TrayIcon_Remove isn't working out, you might consider using Shell_NotifyIcon.
So far, it's working out, but thanks for the idea...always good to have alternatives. The issue is that I had been using Process,Close with the uID and hWnd returned by TrayInfo, but that leaves stale icons in the tray, so I did a TrayIcon_Remove before the Process,Close to prevent the stale icons. I switched to using DetectHiddenWindows,On followed by a search for the pID via ComObjGet("winmgmts:") on Win32_Process (I know what the CommandLine is that runs the program). Now I'm doing a WinClose,ahk_pid, which properly removes the tray icon.
lexikos wrote:I'm going to guess that the stars are all one program, and the keyboard indicator icons are all a second program. That meaning two programs with multiple instances each, not one program per icon.
Correct! Each instance of a particular program is created via a unique parameter on its command line. For example:

Stars.exe Blue
Stars,exe Green
Stars,exe Red
Keys.exe CapsLock
Keys.exe NumLock
Keys.exe ScrollLock
lexikos wrote:My point was that Windows might not differentiate between multiple icons belonging to one program, with respect to representing them in the Settings app
Interesting point! I just checked Stars.exe and Keys.exe in the Settings app on W11/22H2/22623.1037...you are correct...it does not differentiate! They are either all on or all off. Actually, there's only a single entry for the executable, regardless of the number of instances (with different icons) that are running.
lexikos wrote:or restoring the order when new instances start
Just tested...does not happen.
lexikos wrote:maybe it will leave them in the order that they are added to the tray
It does.
lexikos wrote:In that case, you can remove them and add them back in the appropriate order.
Yes.
lexikos wrote:Windows will refuse to split the icons apart; they are always grouped together.
Not what I'm seeing here. The icons are split, as you can see with the Stars and Keys screenshots above...unless I'm misunderstanding your comment, which I probably am.
lexikos wrote:If you try to drag-drop some other icon between the script's icons, it will end up at either end.
Yes, same behavior here.
lexikos wrote:If you drag one icon to or from the overflow window, the other icons will follow. You cannot "promote" or "demote" the individual icons, only the app to which they belong.
Yes, same behavior here.
lexikos wrote:Drag-drop within the group does not work as expected; whichever icon you drag ends up at the far right.
Yes, same behavior here.
lexikos wrote:The newest icon is placed on the left, so if you remove the icon and add it back, it will move to the far left of the group.
Yes, same behavior here.

Thanks again for your help. Regards, Joe
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: System tray button count

02 Jan 2023, 21:09

JoeWinograd wrote:
02 Jan 2023, 15:01
Sorry for the delayed response. I'm in the U.S. Central time zone and had packed it in for the night.
In this medium, I prefer delayed responses. I'd guess most forum users are inactive while I am active (UTC+10).
There is no main window that is always running.
Yes, there is. If there was no window, where do you think the message would be sent, in order to detect that the tray icon was clicked? When I say the script's main window, I mean the script's main window. Every script has it. You must target this window, not one of your GUI windows.
The icons are split, as you can see with the Stars and Keys screenshots above...
No, what I see in the screenshots is that all of the stars are grouped together (icons belonging to Stars.exe), and all of the keys are grouped together (icons belonging to Keys.exe). As I said, I listed the results of testing with multiple instances of one script. In practice, whether they are one script or multiple scripts doesn't matter; only the exe matters.
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

02 Jan 2023, 22:48

lexikos wrote:Yes, there is.
Got it!
lexikos wrote:what I see in the screenshots is that all of the stars are grouped together
OK, we're on the same page now. I was imparting a different meaning to "grouped together". Now I understand what you said.
lexikos wrote:you can show a script's tray menu by sending it the same notification message that the tray would send: AHK_NOTIFYICON (1028) with lParam = WM_RBUTTONUP (0x205)
Works perfectly on the latest W11! Great solution! The only issue left now is moving/ordering the tray icons, which I may just simply ditch for A_OSVersion>=10.0.22623 (along with ditching the icon report). Thanks for your help on this. Regards, Joe
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: System tray button count

03 Jan 2023, 00:04

JoeWinograd wrote:The only issue left now is moving/ordering the tray icons
I wrote:The newest icon is placed on the left, so if you remove the icon and add it back, it will move to the far left of the group.
I suppose that you can just (re)load the scripts in right-to-left order.

Another option would be to monitor a custom message with OnMessage, and have your master script send this message to each script to instruct it to reapply the tray icon (A_IconHidden := true, A_IconHidden := false).

Shell_NotifyIcon can be used externally to remove the icon, and I'm almost certain it could be used to add the icon back (or even add an icon for a script that didn't have one), but you would need to get a handle to the appropriate icon. Loading the icon from file in your master script might cause issues if the master script exits before the other process.
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

03 Jan 2023, 23:50

lexikos wrote:I suppose that you can just (re)load the scripts in right-to-left order.
For some programs that would work well. For others (such as the Stars program), that would be a big performance hit, as it can take several seconds to load each color star (depending on its configuration). That's one of the nice features of TrayIcon_Move...it's fast.
lexikos wrote:Another option would be to monitor a custom message with OnMessage, and have your master script send this message to each script to instruct it to reapply the tray icon (A_IconHidden := true, A_IconHidden := false).
Fascinating idea! I don't have the notion of a "master" script, but I could. Currently, all the programs with multiple instances (and, thus, multiple/different icons, such as the Stars and Keys programs) execute the same EXE without any master script that runs and/or controls them.
lexikos wrote:Shell_NotifyIcon can be used externally to remove the icon, and I'm almost certain it could be used to add the icon back
Another very interesting idea!
lexikos wrote:but you would need to get a handle to the appropriate icon
I can get the PID without using TrayIcon_GetInfo, but don't know if I can get the icon's handle from that...what TrayIcon_GetInfo returns as uID. Maybe I can pull the code from TrayIcon_GetInfo that gets the uID, but I haven't looked at it carefully yet to know if that's possible in the latest W11.
lexikos wrote:Loading the icon from file in your master script might cause issues if the master script exits before the other process.
If I decide to go with a master script, thanks for the heads-up on that.

Regards, Joe
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: System tray button count

04 Jan 2023, 18:13

JoeWinograd wrote:
03 Jan 2023, 23:50
I don't have the notion of a "master" script, but I could.
It doesn't matter. The "master" script is whichever instance is reordering the tray icons at any given moment.
I can get the PID without using TrayIcon_GetInfo, but don't know if I can get the icon's handle from that...
You can't, unless you implement some way to "ask" the window for its icon. One way is to set the icon of the each instance's main window (or a GUI) with WM_SETICON, so it can be retrieved with WM_GETICON. Implementing a custom message is another way. If you have a GUI window, you can just use WM_GETICON because the icon is already set for you.
what TrayIcon_GetInfo returns as uID.
No, not the tray icon ID. The icon handle. TrayIcon_GetInfo returns it as hicon.

The tray icon ID is always AHK_NOTIFYICON, as I said. Only the HWND-ID pair needs to be unique. AutoHotkey only has one tray icon, so it can just use a constant ID.
Maybe I can pull the code from TrayIcon_GetInfo that gets the uID, but I haven't looked at it carefully yet to know if that's possible in the latest W11.
No, none of that will work, otherwise you wouldn't have started this topic. All of the information is retrieved from a Win32 Toolbar control, which is non-existent on newer builds.
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

04 Jan 2023, 21:55

lexikos wrote:The "master" script is whichever instance is reordering the tray icons at any given moment.
Understood.
lexikos wrote:One way is to set the icon of the each instance's main window (or a GUI) with WM_SETICON, so it can be retrieved with WM_GETICON. Implementing a custom message is another way.
OK, I'll look into both ways.
lexikos wrote:No, not the tray icon ID. The icon handle. TrayIcon_GetInfo returns it as hicon.
Got it.
lexikos wrote:All of the information is retrieved from a Win32 Toolbar control, which is non-existent on newer builds.
Ah, got it this time...didn't sink in the first time around.

Thanks, Joe
User avatar
JoeWinograd
Posts: 2200
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: System tray button count

09 Jan 2023, 03:54

lexikos wrote:you can show a script's tray menu by sending it the same notification message that the tray would send: AHK_NOTIFYICON (1028) with lParam = WM_RBUTTONUP (0x205)
Hi lexikos,

After commenting earlier that this works perfectly and is a great solution, I discovered (when I tried to integrate it into my big script) that my testing for it was flawed. So I wrote a test script for only three colors to show the problem I'm having.

The first parameter is Color, which can be Blue, Green, or Red...or Master, which assigns the hotkeys. The second parameter is Debug, which can be 0 or 1.

By running with Debug=1, I determined that:

(1) The hotkeys are firing. For example:

blue hotkey fires.png
blue hotkey fires.png (3.4 KiB) Viewed 5656 times

(2) The GetColorPID function works correctly (PIDs returned by it match the PIDs in the Details tab of Task Manager). For example:

GetColorPID works.png
GetColorPID works.png (6.76 KiB) Viewed 5656 times

(3) AHK_NOTIFYICON gets actual/physical right and left clicks on the tray icons.

However, the hotkeys for the three colors (!#b, !#g, !#r in the posted code) do not result in opening the context for the icons. It would seem that the SendMessage command is not working, but I think it is coded correctly.

The code is below and the four supporting icons (PNG files for Blue, Green, Red, Master) are in the attached ZIP file (so is the script). I hope you can see what's wrong. Thanks very much, Joe

Code: Select all

#SingleInstance Off ; allow multiple instances to run concurrently

; get params
Color:=A_Args[1] ; Blue, Green, Red, Master
Debug:=A_Args[2] ; 0 or 1

DetectHiddenWindows,On
CurrentPID:=DllCall("GetCurrentProcessId")
Modifier:="!#"
TrayIconFile:=A_ScriptDir . "\" . Color . ".png" ; for testing, only 3 colors: Blue.png Green.png Red.png - plus Master.png
If (!FileExist(TrayIconFile))
{
  MsgBox,262160,Fatal Error,TrayIconFile does not exist:`n%TrayIconFile%
  ExitApp
}
Menu,Tray,Icon,%TrayIconFile%

If (Color="Master") ; Master assigns the hotkeys
{
  ; for testing, only 3 hotkeys
  HotkeyBlue:=Modifier . "b"
  Hotkey,%HotkeyBlue%,RightClickBlue,On
  HotkeyGreen:=Modifier . "g"
  Hotkey,%HotkeyGreen%,RightClickGreen,On
  HotkeyRed:=Modifier . "r"
  Hotkey,%HotkeyRed%,RightClickRed,On
}

OnMessage(0x404,"AHK_NOTIFYICON")

AHK_NOTIFYICON(wParam,lParam)
{
  global
  DetectHiddenWindows,On
  If ((Debug) and ((lParam=0x202) or (lParam=0x205)))
    MsgBox,,Enter AHK_NOTIFYICON,wParam=%wParam%`nlParam=%lParam%
  If (lParam=0x202) ; WM_LBUTTONUP=0x0202
    Menu,Tray,Show ; show context menu on left click, too
  If (lParam=0x205) ; WM_RBUTTONUP=0x0205
    Menu,Tray,Show ; show context menu on right click - this should actually result in showing menu twice because system should automatically show it
  Return
}

Return ; end auto-execute

RightClickBlue:
PID:=GetColorPID("Blue")
If (PID=-1)
{
  MsgBox Blue not running
  Return
}
If (Debug)
  MsgBox % "right-click Blue currpid=" . CurrentPID . "`nBluePID=" . PID
SendMessage,0x404,,0x205,,ahk_id %PID% ; WM_RBUTTONUP=0x0205
Return

RightClickGreen:
PID:=GetColorPID("Green")
If (PID=-1)
{
  MsgBox Green not running
  Return
}
If (Debug)
  MsgBox % "right-click Green currpid=" . CurrentPID . "`nGreenPID=" . PID
SendMessage,0x404,,0x205,,ahk_id %PID% ; WM_RBUTTONUP=0x0205
Return

RightClickRed:
PID:=GetColorPID("Red")
If (PID=-1)
{
  MsgBox Red not running
  Return
}
If (Debug)
  MsgBox % "right-click Red currpid=" . CurrentPID . "`nRedPID=" . PID
SendMessage,0x404,,0x205,,ahk_id %PID% ; WM_RBUTTONUP=0x0205
Return

GetColorPID(Color)
{
  global
  DetectHiddenWindows,On
  For Process in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
  {
    CmdLine:=Process.CommandLine
    CmdPID:=Process.ProcessId
    If (InStr(CmdLine,A_ScriptName) and InStr(CmdLine,Color))
    {
      If (Debug)
        MsgBox,,Color Running Info - %Color%,CmdLine=%CmdLine%`nScriptName=%A_ScriptName%`nCmdPID=%CmdPID%`nCurrentPID=%CurrentPID%
      Return CmdPID ; as soon as it finds process
    }
  }
  Return -1 ; not found after looking through all processes
}
Attachments
RightClick.zip
(14.02 KiB) Downloaded 839 times

Return to “Other Programming Languages”

Who is online

Users browsing this forum: No registered users and 63 guests