Dual SysTray, Threaded & "Animated" "imageres.dll" Icons...

Share your advanced PureBasic knowledge/code with the community.
User avatar
Teddy Rogers
User
User
Posts: 98
Joined: Sun Feb 23, 2014 2:05 am
Location: Australia
Contact:

Dual SysTray, Threaded & "Animated" "imageres.dll" Icons...

Post by Teddy Rogers »

This afternoon I had some spare time and decided to see what I can do with PureBasic. I was looking through some of the examples and noticed the system tray example displayed two icons but no menu(s). Thought I'd have a try at fixing that. In the process I entirely used icons from "imageres.dll" and one of the tray icons automatically cycles through the whole set. I guess this could be a way of adding system tray icon animation if you wanted to modify it. It's threaded too so it allows the menu to be used when in operation. The second tray icon when clicked on cycles through the icons manually. The menu's I could only get to display separately by using SetWindowCallback. I'm not sure if "imageres.dll" is available on Windows Vista but it's available on Windows 7 and 8 in varying quantities of icons.

Feel free to use and abuse it, I'm still very green to PureBasic so I'm interested on seeing your improvements! If this is in the wrong forum please move it...

Code: Select all

;
; ------------------------------------------------------------------
;
;   Dual SysTray, Threaded & "Animated" "imageres.dll" System Icons
;   "imageres.dll" is available on Window 7 & 8 (Vista??)
;
;   By Teddy Rogers / PureBasic 5.22
;
; ------------------------------------------------------------------
;

; Declare our procedures

Declare WinCallback_Icon1(WindowID, uMsg, wParam, lParam)
Declare WinCallback_Icon2(WindowID, uMsg, wParam, lParam)

Declare SetImage(iMenu, iID, ico)
Declare ChangeIcon(void)

; Setup some variables/constants

Global Num1 = 100   ; Set our default "imageres.dll" tray icon 1
Global Num2 = 101   ; Set our default "imageres.dll" tray icon 2
Global ThreadID     ; This is used to end the thread

Global TaskbarRestart = RegisterWindowMessage_("TaskbarCreated")  ; Register a message with the "TaskbarCreated" string

; Start the example program...

If OpenWindow(1, 0, 0, 0, 0, "", #PB_Window_Invisible)    ; Define two windows for seperate icons/menu's and callbacks
  If OpenWindow(2, 0, 0, 0, 0, "", #PB_Window_Invisible)
    
    SetWindowCallback(@WinCallback_Icon1(), 1)    ; These are the callbacks to watch for events on each of the icons
    SetWindowCallback(@WinCallback_Icon2(), 2)

    AddSysTrayIcon(1, WindowID(1), ImageID(SetImage(WindowID(1), 0, Num1)))     ; Add the system tray icons using icons from "imageres.dll"
    AddSysTrayIcon(2, WindowID(2), ImageID(SetImage(WindowID(2), 0, Num2)))
 
    SysTrayIconToolTip(1, "You are hovering over icon 1")     ; Setup some tooltips...
    SysTrayIconToolTip(2, "You are hovering over icon 2")
 
    If CreatePopupImageMenu(0, #PB_Menu_ModernLook)       ; Create icon 1 menu with PureBasic modern look (we use "imageres.dll" for menu icons)
      MenuItem(01, "Open", ImageID(SetImage(GetSubMenu_(MenuID(0),0), 0, 174)))
      MenuItem(02, "Save", ImageID(SetImage(GetSubMenu_(MenuID(0),0), 0, 39)))
      MenuItem(03, "Save as", ImageID(SetImage(GetSubMenu_(MenuID(0),0), 0, 23)))
      MenuItem(04, "Quit", ImageID(SetImage(GetSubMenu_(MenuID(0),0), 0, 84)))
      MenuBar()
      OpenSubMenu("Recent files")
      MenuItem(05, "PureBasic.exe")
      MenuItem(06, "Test.txt")
      CloseSubMenu()
    EndIf
   
    If CreatePopupImageMenu(2, 0)         ; Create icon 2 menu with standard look (we use "imageres.dll" for menu icons)
      MenuItem(07, "Open", ImageID(SetImage(GetSubMenu_(MenuID(0),0), 0, 174)))
      MenuItem(08, "Save", ImageID(SetImage(GetSubMenu_(MenuID(0),0), 0, 39)))
      MenuItem(09, "Save as", ImageID(SetImage(GetSubMenu_(MenuID(0),0), 0, 23)))
      MenuItem(10, "Quit", ImageID(SetImage(GetSubMenu_(MenuID(0),0), 0, 84)))
      MenuBar()
      OpenSubMenu("Recent files")
      MenuItem(11, "PureBasic.exe")
      MenuItem(12, "Test.txt")
      CloseSubMenu()
    EndIf
   
    ; Wait for a MenuItem to be selected... then do some stuff...
   
    Repeat
      Event = WaitWindowEvent()
      Select Event
          
        Case #PB_Event_Menu        ; An item of the popup-menu was clicked
          Select EventMenu()       ; Get the clicked menu item...
             
            ; Icon 1 menu actions...
             
            Case 01 : Debug "Menu: Open (Icon 1)"
            Case 02 : Debug "Menu: Save (Icon 1)"
            Case 03 : Debug "Menu: Save as (Icon 1)"
            Case 04 : End
            Case 05 : Debug "Menu: PureBasic.exe (Icon 1)"
            Case 06 : Debug "Menu: Text.txt (Icon 1)"
             
            ; Icon 2 menu actions...
           
            Case 07 : Debug "Menu: Open (Icon 2)"
            Case 08 : Debug "Menu: Save (Icon 2)"
            Case 09 : Debug "Menu: Save as (Icon 2)"
            Case 10 : End
            Case 11 : Debug "Menu: PureBasic.exe (Icon 2)"
            Case 12 : Debug "Menu: Text.txt (Icon 2)"
              
          EndSelect
      EndSelect
    Until Event = #PB_Event_CloseWindow
  EndIf
EndIf

; We will use the Windows default system icons for our own the menu options

Procedure SetImage(iMenu, iID, ico)
  ExtractIconEx_("imageres.dll", ico, 0, @iIcon, 1)
  im=CreateImage(#PB_Any, 16, 16 ,32)                                                 ; Dimensions and bit depth (16, 256, 32bits)
  StartDrawing(ImageOutput(im))
     Box(0, 0, 16, 16, GetSysColor_(#COLOR_MENU))
     DrawingMode(#PB_2DDrawing_AllChannels)
     DrawImage(iIcon, 0, 0, 16, 16)                                                   ; Display icon size
  StopDrawing()
  DestroyIcon_(iIcon)
  ProcedureReturn im
EndProcedure

; Icon Number 1 (clicking left mouse button starts automatic cycling through the icons, clicking left again end the thread)

Procedure WinCallback_Icon1(WindowID, uMsg, wParam, lParam)
  Select lParam
     
    Case #WM_LBUTTONDOWN
      If Not ThreadID = 1
        CreateThread(@ChangeIcon(), 0)
      Else
        ThreadID = 0              ; Tell the running thread to end.
      EndIf
     
    Case #WM_RBUTTONDOWN
      DisplayPopupMenu(0, WindowID(1))  ; Display popup-menu 1
      
  EndSelect
  
  ; Listen for "TaskbarCreated" broadcast to be sent to all windows if the taskbar is recreated then...
  ; Recreate our tray icon including the tool tip...
  
  Select uMsg
      
    Case TaskbarRestart
      AddSysTrayIcon(1, WindowID(1), ImageID(SetImage(WindowID(1), 0, Num1)))
      SysTrayIconToolTip(1, "You are hovering over icon 1")
      
  EndSelect
 
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

; Icon Number 2 (clicking left mouse button manually cycles through the icons one at a time)

Procedure WinCallback_Icon2(WindowID, uMsg, wParam, lParam)
  Select lParam
     
    Case #WM_LBUTTONDOWN
      If Not Num2 = 218     ; Cycle through "imageres.dll" icons. Windows 7 has 218 and Window 8 has 384!!!
        ChangeSysTrayIcon(2, ImageID(SetImage(WindowID(2), 0, Num2)))
        Num2 + 1
      Else
        Num2 = 0
      EndIf
         
    Case #WM_RBUTTONDOWN
      DisplayPopupMenu(2, WindowID(1))  ; Display popup-menu 2
            
  EndSelect    
  
  ; Listen for "TaskbarCreated" broadcast to be sent to all windows if the taskbar is recreated then...
  ; Recreate our tray icon including the tool tip...  
  
  Select uMsg
      
    Case TaskbarRestart
      AddSysTrayIcon(2, WindowID(2), ImageID(SetImage(WindowID(2), 0, Num2)))
      SysTrayIconToolTip(2, "You are hovering over icon 2")
      
  EndSelect

  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

; Create a thread to cycle through the icons, creating a thread allows us to continue using tray menu(s)

Procedure ChangeIcon(void)
  ThreadID = 1          ; If we set this we can check if we have a thread running so we don't start it again
 
  Repeat
    If Not Num1 = 218     ; Cycle through "imageres.dll" icons. Windows 7 has 218 and Window 8 has 384!!!
      Sleep_(500)
      ChangeSysTrayIcon(1, ImageID(SetImage(WindowID(1), 0, Num1)))
      Num1 + 1
    Else
      Num1 = 0
    EndIf
  Until ThreadID = 0
 
EndProcedure
Ted.
Last edited by Teddy Rogers on Sat Apr 05, 2014 3:22 am, edited 2 times in total.
User avatar
em_uk
Enthusiast
Enthusiast
Posts: 366
Joined: Sun Aug 08, 2010 3:32 pm
Location: Manchester UK

Re: Dual SysTray, Threaded & "Animated" "imageres.dll" Icons

Post by em_uk »

Very nice and tidy!
----

R Tape loading error, 0:1
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Dual SysTray, Threaded & "Animated" "imageres.dll" Icons

Post by davido »

Impressive! :o
DE AA EB
User avatar
Teddy Rogers
User
User
Posts: 98
Joined: Sun Feb 23, 2014 2:05 am
Location: Australia
Contact:

Re: Dual SysTray, Threaded & "Animated" "imageres.dll" Icons

Post by Teddy Rogers »

I have updated the sample code above to listen out for the "TaskbarCreated" broadcast so that if the taskbar crashed and is restarted the tray icons including tool tips are restarted (recreated) along with it. If this does not happen your tray icons will remain missing.

You can simulate the broadcast by using: SendNotifyMessage_(#HWND_BROADCAST, RegisterWindowMessage_("TaskbarCreated"), 0, 0)

Alternatively in Windows Task Manager you can end explorer.exe process and then restart it by going to: File -> New Task (Run) -> then enter in, "explorer.exe"

If everything works the icons and tool tips should show up again. You would likely only really need to listen out for the broadcast and recreate both of the tray icons and tool tips in one of the callback procedures but I've split them up for consistency with the example code.

You can find more information on this and the taskbar in general by visiting Microsoft's page on it...

http://msdn.microsoft.com/en-us/library ... 85%29.aspx

Ted.
Post Reply