Enumerate System Tray Icons [Windows]

Share your advanced PureBasic knowledge/code with the community.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Enumerate System Tray Icons [Windows]

Post by RASHAD »

PB x86 & x64 OK
Tested with PB 6.xx x86 & x64 - Windows 11 x64
Tested with with VM Windows 7,8,8.1,10

New :
- Code modified
- Tested OK with VM XP x64 too

Code: Select all

Global count,turn,hWnd

Global Dim Stray$(0),Dim Icon(0)

Structure TRAYDATA Align #PB_Structure_AlignC
  hwnd.i
  uID.l             
  uCallbackMessage.l
  Reserved.l[2]     
  hIcon.i           
EndStructure

Structure TRAYDATA_wow64 Align #PB_Structure_AlignC
  hwnd.q
  uID.l             
  uCallbackMessage.l
  Reserved.l[2]     
  hIcon.i           
EndStructure

Structure TBBUTTON_wow64 Align #PB_Structure_AlignC
  iBitmap.l
  idCommand.l
  fsState.b
  fsStyle.b
  bReserved.b[6]
  dwData.q
  iString.q
EndStructure 

Procedure Is64Bit()
  bIsWow64 = 0 
  If OpenLibrary(0,"kernel32.dll")
    *MAlloc = GetFunction(0, "IsWow64Process")
    CallFunctionFast(*MAlloc,GetCurrentProcess_(), @bIsWow64)
    CloseLibrary(0)
  EndIf 
  ProcedureReturn bIsWow64
EndProcedure

Procedure.s GetImageName(hWnd,PID)
  OpenLibrary(0, "psapi.dll")
  filename$ = Space(#MAX_PATH) 
  CompilerIf #PB_Compiler_Unicode
    *fn = GetFunction(0, "GetProcessImageFileNameW")
  CompilerElse
    *fn = GetFunction(0, "GetProcessImageFileNameA")
  CompilerEndIf
  GetWindowThreadProcessId_(hWnd, @processID.i)
  hProc = OpenProcess_(#PROCESS_ALL_ACCESS	, #False, processID)
  If hProc 
    CallFunctionFast(*fn, hProc, @filename$, #MAX_PATH)
    CloseHandle_(hProc)
  EndIf
  CloseLibrary(hDLL)
  ProcedureReturn filename$
EndProcedure

Procedure Find_PB_TrayIcons()
  Protected hwnd
  For n = 1 To 2
    If n = 1
      hWnd = FindWindow_("Shell_TrayWnd", #Null)
      If hWnd
        hWnd = FindWindowEx_(hWnd, #Null, "TrayNotifyWnd", #Null)
        If hWnd
          hWnd = FindWindowEx_(hWnd,#Null, "SysPager", #Null)
          If hWnd
            hTray = FindWindowEx_(hWnd, #Null, "ToolbarWindow32", #Null)
          Else
            ProcedureReturn 0
          EndIf
        Else
          ProcedureReturn 0
        EndIf
      Else
        ProcedureReturn 0
      EndIf
      count = SendMessage_(hTray, #TB_BUTTONCOUNT, 0, 0)
    ElseIf n = 2
      hWnd = FindWindow_("NotifyIconOverflowWindow", #Null)
      If hWnd
          hTray = FindWindowEx_(hWnd, #Null, "ToolbarWindow32", #Null)
      Else
        ProcedureReturn 0
      EndIf
      count = count + SendMessage_(hTray, #TB_BUTTONCOUNT, 0, 0)
    EndIf
      
  If Is64Bit()
    Dim button_td2.TRAYDATA_wow64(count*2)
    Dim button2.TBBUTTON_wow64   (count*2)
  Else
    Dim button_td.TRAYDATA(count*2)
    Dim button.TBBUTTON   (count*2)
  EndIf
  
  ReDim Stray$(count*2)
  ReDim Icon(count*2)  
 
  dwExplorerThreadId=GetWindowThreadProcessId_(hTray, @dwExplorerProcessId)
  hProc = OpenProcess_(#PROCESS_ALL_ACCESS, #False, dwExplorerProcessId)
  If Is64Bit()
    *lpData = VirtualAllocEx_(hProc, #Null, SizeOf(TBBUTTON_wow64)*count, #MEM_COMMIT, #PAGE_READWRITE)
  Else
    *lpData = VirtualAllocEx_(hProc, #Null, SizeOf(TBBUTTON)*count, #MEM_COMMIT, #PAGE_READWRITE)
  EndIf
  
  For i = 0 To count - 1
    If Is64Bit()
      SendMessage_(hTray, #TB_GETBUTTON, i, *lpData+i*SizeOf(TBBUTTON_wow64) )
      ReadProcessMemory_(hProc, *lpData+(i*SizeOf(TBBUTTON_wow64)), @button2.TBBUTTON_wow64(i), SizeOf(TBBUTTON_wow64), #Null)
      ReadProcessMemory_(hProc, button2(i)\dwData, @button_td2.TRAYDATA_wow64(i), SizeOf(TRAYDATA_wow64), #Null)
      hIcon = button_td2.TRAYDATA_wow64(i)\hIcon
      hWnd = button_td2.TRAYDATA_wow64(i)\hwnd
      uID = button_td2.TRAYDATA_wow64(i)\uID
      iState = button2.TBBUTTON_wow64(i)\fsState
      IconName$ = GetImageName(hWnd,uID)      
      win$ = Space(#MAX_PATH)
      GetWindowsDirectory_(@win$,#MAX_PATH)
      win$ = RemoveString(win$, "\Windows",#PB_String_NoCase)
      If Trim(IconName$) = ""
        IconName$ = "System Icon"
      Else
        ;IconName$ = RemoveString(IconName$,"\Windows",#PB_String_NoCase)
        left$ = Left(IconName$,23)
        IconName$ = ReplaceString(IconName$,left$,win$)
      EndIf
      If n = 1
        turn = i
        STray$(turn) = "     " + Str(hIcon) +  Chr(10) + Str(hWnd) + Chr(10) + Str(iState) + Chr(10) + Str(uID) + Chr(10) + IconName$
      Else
        turn = turn + 1
        STray$(turn) = "     " + Str(hIcon) +  Chr(10) + Str(hWnd) + Chr(10) + "OFN" + Chr(10) + Str(uID) + Chr(10) + IconName$
      EndIf        
        Icon(turn) = hIcon
    Else
      SendMessage_(hTray, #TB_GETBUTTON, i, *lpData+i*SizeOf(TBBUTTON) )
      ReadProcessMemory_(hProc, *lpData+(i*SizeOf(TBBUTTON)), @button.TBBUTTON(i), SizeOf(TBBUTTON), #Null)
      ReadProcessMemory_(hProc, button(i)\dwData, @button_td.TRAYDATA(i), SizeOf(TRAYDATA), #Null)
      hIcon = button_td.TRAYDATA(i)\hIcon
      hWnd = button_td.TRAYDATA(i)\hwnd
      uID = button_td.TRAYDATA(i)\uID
      iState = button.TBBUTTON(i)\fsState
      IconName$ = GetImageName(hWnd,uID)
      win$ = Space(#MAX_PATH)
      GetWindowsDirectory_(@win$,#MAX_PATH)
      win$ = RemoveString(win$, "\Windows",#PB_String_NoCase)
      If Trim(IconName$) = ""
        IconName$ = "System Icon"
      Else
        ;IconName$ = RemoveString(IconName$,"\Windows",#PB_String_NoCase)
        left$ = Left(IconName$,23)
        IconName$ = ReplaceString(IconName$,left$,win$)
      EndIf
      If n = 1
         turn = i
         STray$(turn) = "     " + Str(hIcon)  + Chr(10) + Str(hWnd) + Chr(10) + Str(iState) + Chr(10) + Str(uID) + Chr(10) + IconName$
      Else
        turn = turn + 1
        STray$(turn) = "     " + Str(hIcon)  + Chr(10) + Str(hWnd) + Chr(10) + "OFN" + Chr(10) + Str(uID) + Chr(10) + IconName$
      EndIf        
        Icon(turn) = hIcon
    EndIf
  Next
    
  Next
 
  VirtualFreeEx_(hProc, *lpData, #Null, #MEM_RELEASE)
  VirtualFreeEx_(hProc, *lpRect, #Null, #MEM_RELEASE)
  CloseHandle_(hProc)  
 
EndProcedure

OpenWindow(0, 0, 0, 600, 300, "SysTray Notification Icons", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)

ListIconGadget(0,10,10,580,280,"Icon-Icon Handle", 130, #PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect )
;SetGadgetColor(0, #PB_Gadget_FrontColor, $FD4B0B)
;SetGadgetColor(0, #PB_Gadget_BackColor, $E8FEFE)
   
AddGadgetColumn(0, 1, "Win Handle", 100)
AddGadgetColumn(0, 2, "Status", 55)
AddGadgetColumn(0, 3, "PID", 60)
AddGadgetColumn(0, 4, "Image name", 600)

Find_PB_TrayIcons()

For x = 0 To count-1
  AddGadgetItem(0,-1, STray$(x), Icon(x))
Next

Repeat
     iEvent = WaitWindowEvent()
     
     Select iEvent
         Case #PB_Event_Gadget
             Select EventGadget()
             EndSelect
     EndSelect       
Until iEvent = #PB_Event_CloseWindow 

Sorry copy paste error as usual :wink:
Edit : Bug fixed
Edit2 : Bug fixed
Last edited by RASHAD on Sun Mar 05, 2023 6:09 am, edited 2 times in total.
Egypt my love
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: Enumerate System Tray Icons [Windows]

Post by AZJIO »

RASHAD
[ERROR] Array index out of bounds.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: Enumerate System Tray Icons [Windows]

Post by RASHAD »

How many icons you have?
See if the problem solved or not

Previous post Updated to consider any no.of system tray buttons
Egypt my love
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: Enumerate System Tray Icons [Windows]

Post by AZJIO »

It's ok now, link.
Where there are no icons - explorer.exe. Should be a network and sound icon, not explorer.exe
Once gave an error, it was not possible to repeat the error how many did not run. On line "AddGadgetItem(0,-1, STray$(x), Icon(x))"
[ERROR] AddGadgetItem(): The specified 'ImageID' is not valid.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Enumerate System Tray Icons [Windows]

Post by Kwai chang caine »

AZJIO wrote: I On line "AddGadgetItem(0,-1, STray$(x), Icon(x))"
[ERROR] AddGadgetItem(): The specified 'ImageID' is not valid.
The same problem :cry:
Count = 11 here
ImageThe happiness is a road...
Not a destination
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: Enumerate System Tray Icons [Windows]

Post by RASHAD »

Hi KCC
I can't guess what kind of icon cause the error
Try the next turn then report

Code: Select all


Find_PB_TrayIcons()

For x = 0 To count-1
  icoH = CopyImage_(Icon(x),#IMAGE_ICON,16,16,#LR_COPYDELETEORG)
  If icoH
    AddGadgetItem(0,-1, STray$(x), icoH)
  Else
    AddGadgetItem(0,-1, STray$(x), 0)
  EndIf
Next

Egypt my love
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: Enumerate System Tray Icons [Windows]

Post by AZJIO »

The "sound" and "network" icons are not visible to me, because the background is white. It is necessary to read the color of the taskbar in the registry and make the same background for the icon.

Code: Select all

		icoH = CopyImage_(Icon(x), #IMAGE_ICON, 16, 16, #LR_COPYDELETEORG)
		If icoH
			AddGadgetItem(0,  -1, STray$(x), icoH)
			DestroyIcon_(icoH)
		Else
			AddGadgetItem(0,  -1, STray$(x), 0)
		EndIf
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: Enumerate System Tray Icons [Windows]

Post by RASHAD »

The icons background color is fine with Windows 7,9,10
No need to spend more effort for just small glitch
Why don't you try to solve that glitch and add it here instead of try to be smart
I just solved the main topic while nobody else did
Be careful :wink:
Egypt my love
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: Enumerate System Tray Icons [Windows]

Post by RASHAD »

Solved the back ground color of some Windows 10,11 icons without playing with the registry [Too dangerous] :wink:
That is enough for me ,any more work to do is your turn

Code: Select all


Global count,turn,hWnd

Global Dim Stray$(0),Dim Icon(0)

Structure TRAYDATA Align #PB_Structure_AlignC
  hwnd.i
  uID.l             
  uCallbackMessage.l
  Reserved.l[2]     
  hIcon.i           
EndStructure

Structure TRAYDATA_wow64 Align #PB_Structure_AlignC
  hwnd.q
  uID.l             
  uCallbackMessage.l
  Reserved.l[2]     
  hIcon.i           
EndStructure

Structure TBBUTTON_wow64 Align #PB_Structure_AlignC
  iBitmap.l
  idCommand.l
  fsState.b
  fsStyle.b
  bReserved.b[6]
  dwData.q
  iString.q
EndStructure 

Procedure Is64Bit()
  bIsWow64 = 0 
  If OpenLibrary(0,"kernel32.dll")
    *MAlloc = GetFunction(0, "IsWow64Process")
    CallFunctionFast(*MAlloc,GetCurrentProcess_(), @bIsWow64)
    CloseLibrary(0)
  EndIf 
  ProcedureReturn bIsWow64
EndProcedure

Procedure.s GetImageName(hWnd,PID)
  OpenLibrary(0, "psapi.dll")
  filename$ = Space(#MAX_PATH) 
  CompilerIf #PB_Compiler_Unicode
    *fn = GetFunction(0, "GetProcessImageFileNameW")
  CompilerElse
    *fn = GetFunction(0, "GetProcessImageFileNameA")
  CompilerEndIf
  GetWindowThreadProcessId_(hWnd, @processID.i)
  hProc = OpenProcess_(#PROCESS_ALL_ACCESS	, #False, processID)
  If hProc 
    CallFunctionFast(*fn, hProc, @filename$, #MAX_PATH)
    CloseHandle_(hProc)
  EndIf
  CloseLibrary(hDLL)
  ProcedureReturn filename$
EndProcedure

Procedure Find_PB_TrayIcons()
  Protected hwnd
  CreateImage(100,16,16,32,#PB_Image_Transparent)
  For n = 1 To 2
    If n = 1
      hWnd = FindWindow_("Shell_TrayWnd", #Null)
      If hWnd
        hWnd = FindWindowEx_(hWnd, #Null, "TrayNotifyWnd", #Null)
        If hWnd
          hWnd = FindWindowEx_(hWnd,#Null, "SysPager", #Null)
          If hWnd
            hTray = FindWindowEx_(hWnd, #Null, "ToolbarWindow32", #Null)
          Else
            ProcedureReturn 0
          EndIf
        Else
          ProcedureReturn 0
        EndIf
      Else
        ProcedureReturn 0
      EndIf
      count = SendMessage_(hTray, #TB_BUTTONCOUNT, 0, 0)
    ElseIf n = 2
      hWnd = FindWindow_("NotifyIconOverflowWindow", #Null)
      If hWnd
          hTray = FindWindowEx_(hWnd, #Null, "ToolbarWindow32", #Null)
      Else
        ProcedureReturn 0
      EndIf
      count = count + SendMessage_(hTray, #TB_BUTTONCOUNT, 0, 0)
    EndIf
      
  If Is64Bit()
    Dim button_td2.TRAYDATA_wow64(count*2)
    Dim button2.TBBUTTON_wow64   (count*2)
  Else
    Dim button_td.TRAYDATA(count*2)
    Dim button.TBBUTTON   (count*2)
  EndIf
  
  ReDim Stray$(count*2)
  ReDim Icon(count*2)  
 
  dwExplorerThreadId=GetWindowThreadProcessId_(hTray, @dwExplorerProcessId)
  hProc = OpenProcess_(#PROCESS_ALL_ACCESS, #False, dwExplorerProcessId)
  If Is64Bit()
    *lpData = VirtualAllocEx_(hProc, #Null, SizeOf(TBBUTTON_wow64)*count, #MEM_COMMIT, #PAGE_READWRITE)
  Else
    *lpData = VirtualAllocEx_(hProc, #Null, SizeOf(TBBUTTON)*count, #MEM_COMMIT, #PAGE_READWRITE)
  EndIf
  
  For i = 0 To count - 1
    If Is64Bit()
      SendMessage_(hTray, #TB_GETBUTTON, i, *lpData+i*SizeOf(TBBUTTON_wow64) )
      ReadProcessMemory_(hProc, *lpData+(i*SizeOf(TBBUTTON_wow64)), @button2.TBBUTTON_wow64(i), SizeOf(TBBUTTON_wow64), #Null)
      ReadProcessMemory_(hProc, button2(i)\dwData, @button_td2.TRAYDATA_wow64(i), SizeOf(TRAYDATA_wow64), #Null)      
      hIcon = button_td2.TRAYDATA_wow64(i)\hIcon
      hWnd = button_td2.TRAYDATA_wow64(i)\hwnd
      uID = button_td2.TRAYDATA_wow64(i)\uID
      iState = button2.TBBUTTON_wow64(i)\fsState
      IconName$ = GetImageName(hWnd,uID)      
      win$ = Space(#MAX_PATH)
      GetWindowsDirectory_(@win$,#MAX_PATH)
      win$ = RemoveString(win$, "\Windows",#PB_String_NoCase)
      If Trim(IconName$) = ""
        IconName$ = "System Icon"
      Else
        left$ = Left(IconName$,23)
        IconName$ = ReplaceString(IconName$,left$,win$)
      EndIf
      If n = 1
        turn = i
        STray$(turn) = "     " + Str(hIcon) +  Chr(10) + Str(hWnd) + Chr(10) + Str(iState) + Chr(10) + Str(uID) + Chr(10) + IconName$
      Else
        turn = turn + 1
        STray$(turn) = "     " + Str(hIcon) +  Chr(10) + Str(hWnd) + Chr(10) + "OFN" + Chr(10) + Str(uID) + Chr(10) + IconName$
      EndIf        
        Icon(turn) = hIcon
    Else
      SendMessage_(hTray, #TB_GETBUTTON, i, *lpData+i*SizeOf(TBBUTTON) )
      ReadProcessMemory_(hProc, *lpData+(i*SizeOf(TBBUTTON)), @button.TBBUTTON(i), SizeOf(TBBUTTON), #Null)
      ReadProcessMemory_(hProc, button(i)\dwData, @button_td.TRAYDATA(i), SizeOf(TRAYDATA), #Null)
      hIcon = button_td.TRAYDATA(i)\hIcon
      hWnd = button_td.TRAYDATA(i)\hwnd
      uID = button_td.TRAYDATA(i)\uID
      iState = button.TBBUTTON(i)\fsState
      IconName$ = GetImageName(hWnd,uID)
      win$ = Space(#MAX_PATH)
      GetWindowsDirectory_(@win$,#MAX_PATH)
      win$ = RemoveString(win$, "\Windows",#PB_String_NoCase)
      If Trim(IconName$) = ""
        IconName$ = "System Icon"
      Else
        ;IconName$ = RemoveString(IconName$,"\Windows",#PB_String_NoCase)
        left$ = Left(IconName$,23)
        IconName$ = ReplaceString(IconName$,left$,win$)
      EndIf
      If n = 1
         turn = i
         STray$(turn) = "     " + Str(hIcon)  + Chr(10) + Str(hWnd) + Chr(10) + Str(iState) + Chr(10) + Str(uID) + Chr(10) + IconName$
      Else
        turn = turn + 1
        STray$(turn) = "     " + Str(hIcon)  + Chr(10) + Str(hWnd) + Chr(10) + "OFN" + Chr(10) + Str(uID) + Chr(10) + IconName$
      EndIf        
        Icon(turn) = hIcon
    EndIf
  Next
    
  Next
 
  VirtualFreeEx_(hProc, *lpData, #Null, #MEM_RELEASE)
  VirtualFreeEx_(hProc, *lpRect, #Null, #MEM_RELEASE)
  CloseHandle_(hProc)  
 
EndProcedure

OpenWindow(0, 0, 0, 600, 300, "SysTray Notification Icons", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)

ListIconGadget(0,10,10,580,280,"Icon-Icon Handle", 130, #PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect )
;SetGadgetColor(0, #PB_Gadget_FrontColor, $FD4B0B)
;SetGadgetColor(0, #PB_Gadget_BackColor, $E8FEFE)
   
AddGadgetColumn(0, 1, "Win Handle", 100)
AddGadgetColumn(0, 2, "Status", 55)
AddGadgetColumn(0, 3, "PID", 60)
AddGadgetColumn(0, 4, "Image name", 600)

Find_PB_TrayIcons()

For x = 0 To count - 1
  icoH = CopyIcon_(Icon(x))
  If icoH
    If OSVersion() >= #PB_OS_Windows_10
      CreateImage(100,16,16,24,$889145)      
      hdc = StartDrawing(ImageOutput(100))
        DrawIconEx_(hdc,0,0,Icon(x),16,16,0,0,#DI_NORMAL)
      StopDrawing()
      AddGadgetItem(0,-1, STray$(x), ImageID(100))
      FreeImage(100)
    Else
      AddGadgetItem(0,-1, STray$(x), Icon(x))
    EndIf      
  Else
    AddGadgetItem(0,-1, STray$(x), 0)
  EndIf
  DestroyIcon_(icoH)
Next

Repeat
     iEvent = WaitWindowEvent()
     
     Select iEvent
         Case #PB_Event_Gadget
             Select EventGadget()
             EndSelect
     EndSelect       
Until iEvent = #PB_Event_CloseWindow 


Egypt my love
Post Reply