How to open or show a running application from the Traybar

Just starting out? Need help? Post your questions and find answers here.
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

I won't be able to offer you any code but if you are working with XP or greater, it is possible to send clicks to the tray Notification Area from a PB app.

Here's a little sample I cut out from one of my apps. I tested with XP and Vista. Some icons may not be exact duplicates and some icons may not even be listed. Having all icons listed isn't a problem but I just didin't have time to redo the entire code for this example. :wink:

Sample app >>> http://www.heysparkie.com/Apps/TrayClicks.exe
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
Philippe-felixer76-2
Enthusiast
Enthusiast
Posts: 135
Joined: Sat Aug 18, 2007 7:09 am
Location: Netherlands

Post by Philippe-felixer76-2 »

Sparkie wrote:I won't be able to offer you any code but if you are working with XP or greater, it is possible to send clicks to the tray Notification Area from a PB app.

Here's a little sample I cut out from one of my apps. I tested with XP and Vista. Some icons may not be exact duplicates and some icons may not even be listed. Having all icons listed isn't a problem but I just didin't have time to redo the entire code for this example. :wink:

Sample app >>> http://www.heysparkie.com/Apps/TrayClicks.exe
Yeah works great ;-P

Seems like a solution, what's the point
of showing me and not sharing the code?

Gr,
Phil.

Here you have food, but you can't eat it.
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

The code is ripped from one of my apps and in it's current state it's a mess. If I get some free time I'll be glad to reduce the code down to something you can use. :)

In the meantime, here's the heart of the code. hIcons is the handle to the Toolbar (in the Notification Area) where the icons reside.

Code: Select all

Macro MakeLong(low, high) 
  low | high <<16 
EndMacro 

If clickit > -1
  index = SendMessage_(hIcons, #TB_COMMANDTOINDEX, iCmd, 0)
  SendMessage_(hIcons, #TB_GETITEMRECT, index, *Buff2)
  ReadProcessMemory_(tbPrc, *Buff2, @rc.RECT, SizeOf(RECT), @result)
  Select clickit
    Case #PB_EventType_LeftClick
      SetForegroundWindow_(hIcons)
      PostMessage_(hIcons, #WM_LBUTTONDOWN, 0, MakeLong(rc\left, rc\top))
      PostMessage_(hIcons, #WM_LBUTTONUP, 0, MakeLong(rc\left, rc\top))
      PostMessage_(hIcons, #WM_LBUTTONDOWN, 0, MakeLong(rc\left, rc\top))
      PostMessage_(hIcons, #WM_LBUTTONUP, 0, MakeLong(rc\left, rc\top))
      PostMessage_(hIcons, #WM_LBUTTONDBLCLK, 0, MakeLong(rc\left, rc\top))
      clickit = -1
  EndSelect
EndIf
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
Philippe-felixer76-2
Enthusiast
Enthusiast
Posts: 135
Joined: Sat Aug 18, 2007 7:09 am
Location: Netherlands

Post by Philippe-felixer76-2 »

Sparkie wrote:The code is ripped from one of my apps and in it's current state it's a mess. If I get some free time I'll be glad to reduce the code down to something you can use. :)

In the meantime, here's the heart of the code. hIcons is the handle to the Toolbar (in the Notification Area) where the icons reside.

Code: Select all

Macro MakeLong(low, high) 
  low | high <<16 
EndMacro 

If clickit > -1
  index = SendMessage_(hIcons, #TB_COMMANDTOINDEX, iCmd, 0)
  SendMessage_(hIcons, #TB_GETITEMRECT, index, *Buff2)
  ReadProcessMemory_(tbPrc, *Buff2, @rc.RECT, SizeOf(RECT), @result)
  Select clickit
    Case #PB_EventType_LeftClick
      SetForegroundWindow_(hIcons)
      PostMessage_(hIcons, #WM_LBUTTONDOWN, 0, MakeLong(rc\left, rc\top))
      PostMessage_(hIcons, #WM_LBUTTONUP, 0, MakeLong(rc\left, rc\top))
      PostMessage_(hIcons, #WM_LBUTTONDOWN, 0, MakeLong(rc\left, rc\top))
      PostMessage_(hIcons, #WM_LBUTTONUP, 0, MakeLong(rc\left, rc\top))
      PostMessage_(hIcons, #WM_LBUTTONDBLCLK, 0, MakeLong(rc\left, rc\top))
      clickit = -1
  EndSelect
EndIf
Hey Tnx man...

This is very usefull to me..

Gr,
Phil.
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

Here's some code for you to use. I only coded in a simulated left doubleclick. You can easily add support for single left or right clicks as needed. ;)

Code: Select all

;/============================================================
;/ Code       : Simulate Doubleclick on Notification Area icon
;/ Author     : Sparkie
;/ Date       : 03/16/2008
;/ PB Support : PB 4.00 or later
;/ OS Support : Windows XP or later
;/ License    : Free to use with no restrictions
;/============================================================

If OSVersion() < #PB_OS_Windows_XP
  MessageRequester("Sorry", "WinXP or later is required", #PB_MessageRequester_Ok | #MB_ICONEXCLAMATION)
  End
EndIf

;/ Constants ========================================================
#ICON_SMALL = 0

;/ Enumerations =====================================================
Enumeration
  #WindowMain
EndEnumeration

Enumeration
  #TextInfo
  #ListIcons
  #ButtonRefresh
EndEnumeration

;/ Macros ===========================================================

Macro MakeLong(low, high) 
  low | high <<16 
EndMacro 

;/ ========================= Structures ==========================

Structure EXTRAINFO
  hwnd.l
  dummy0.l
  dummy1.l
  dummy2.l
  dummy3.l
  exePath.l
  dummy5.l
EndStructure

;/ ========================= Procedures ==========================

;- Find Taskbar and enumerate buttons
Procedure.l TaskBarList(clickit.l, iCmd.l)
  cf.CHARFORMAT
  cf\cbSize = SizeOf(CHARFORMAT)
  cf\dwMask = #CFM_COLOR
  
  Protected hIcons.l = 0
  Protected hTray.l = 0
  Protected hNotify.l = 0
  Protected hPager.l = 0
  result.l = 0
  
  ;- Find the Notification Area Toolbar that holds the icons
  hTray = FindWindow_("Shell_TrayWnd", 0)
  If hTray
    result + 1
    hNotify = FindWindowEx_(hTray, 0, "TrayNotifyWnd", 0)
    If hNotify
      result + 1
      hPager = FindWindowEx_(hNotify, 0, "SysPager", 0)
      If hPager
        result + 1
        hIcons = FindWindowEx_(hPager, 0, "ToolbarWindow32", 0)
        If hIcons
          result + 1
        EndIf
      EndIf
    EndIf
  EndIf
  If result < 4
    MessageRequester("Error Code " + Str(result), "Unable to locate Taskbar", #MB_OK | #MB_ICONERROR)
    End
  EndIf
  result=0
  
  ;- Get the Notification icon (button) count
  buttonCount.l = SendMessage_(hIcons, #TB_BUTTONCOUNT, 0, 0)
  
  ;- Open taskbar process
  pid.l = 0
  GetWindowThreadProcessId_(hIcons, @pid)
  If pid
    result + 1
    Protected trayProc.l = 0
    trayProc.l = OpenProcess_(#PROCESS_VM_OPERATION | #PROCESS_VM_READ | #PROCESS_VM_WRITE, #False, pid)
    If trayProc
      result + 1
      ;- Create buffer to hold result for reading taskbar info
      *Buff1 = VirtualAllocEx_(trayProc, 0, 2048, #MEM_COMMIT,  #PAGE_READWRITE)
      If *Buff1
        result + 1
      EndIf
    EndIf
  EndIf
  If result < 3
    MessageRequester("Error Code " + Str(result), "Unable to init Taskbar", #MB_OK | #MB_ICONERROR)
    End
  EndIf
  
  ;- Write TBBUTTONINFO from tray icons
  tbi.TBBUTTONINFO
  tbi\cbSize = SizeOf(TBBUTTONINFO)
  tbi\dwMask = #TBIF_BYINDEX | #TBIF_LPARAM | #TBIF_SIZE | #TBIF_STATE | #TBIF_STYLE | #TBIF_COMMAND
  If *Buff1
    WriteProcessMemory_(trayProc, *Buff1, @tbi, SizeOf(TBBUTTONINFO), @result)
  Else
    MessageRequester("Error", "unable to write to Buffer", #MB_OK | #MB_ICONERROR)
    End
  EndIf
  result = 0
  ;- Loop through all taskbar buttons
  For i = 0 To buttonCount - 1
    ;--- Read the TBBUTTONINFO
    SendMessage_(hIcons, #TB_GETBUTTONINFO, i, *Buff1)
    If *Buff1
      ReadProcessMemory_(trayProc, *Buff1, @tbi, SizeOf(TBBUTTONINFO), @result)
    Else
      MessageRequester("Error", "unable to write to buffer", #MB_OK | #MB_ICONERROR)
      End
    EndIf
    result = 0
    ;- Get the extra button info found within lparam (hwnd, exepath)
    eInfo.EXTRAINFO
    If tbi\lParam
      ReadProcessMemory_(trayProc, tbi\lParam, @eInfo, SizeOf(EXTRAINFO), @result)
    Else
      MessageRequester("Error", "unable to read memory", #MB_OK | #MB_ICONERROR)
      End
    EndIf
    result = 0
    ;- Get the Notification icon tooltip text
    bLen = SendMessage_(hIcons, #TB_GETBUTTONTEXT, tbi\idCommand, 0)
    If bLen > 0
      button$ = Space(bLen)
      SendMessage_(hIcons, #TB_GETBUTTONTEXT, tbi\idCommand, *Buff1 + SizeOf(TBBUTTONINFO))
      If *Buff1
        ReadProcessMemory_(trayProc, *Buff1 + SizeOf(TBBUTTONINFO), @button$, bLen, @result)
      Else
        MessageRequester("Error", "unable to read memory", #MB_OK | #MB_ICONERROR)
        End
      EndIf
    Else
      button$ = "hidden"
    EndIf
    
    ;- Get the icon
    If button$ <> "hidden"
      hIconSmall.l = 0
      ;- First try to get icon
      hIconSmall = SendMessage_(eInfo\hwnd, #WM_GETICON, #ICON_SMALL, 0)
      If hIconSmall = 0
        ;- Second try to get icon
        hIconSmall = GetClassLong_(eInfo\hwnd, #GCL_HICONSM)
        If hIconSmall = 0
          ;- Third try to get icon
          GetWindowThreadProcessId_(eInfo\hwnd, @ctpid.l)
          hProcess = OpenProcess_(#PROCESS_QUERY_INFORMATION | #PROCESS_VM_READ, #False, ctpid)
          If OpenLibrary(0, "psapi.dll")
            exepath$ = Space(#MAX_PATH)
            CallFunction(0, "GetModuleFileNameExA", hProcess, 0, @exepath$, #MAX_PATH)
            CloseLibrary(0)
          EndIf
          CloseHandle_(hProcess)
          ExtractIconEx_(@exepath$, 0, 0, @hIconSmall, 1)
          di = 1
          If hIconSmall = 0
            ;- When all else fails
            hIconSmall = LoadIcon_(0, #IDI_WINLOGO)
          EndIf
        EndIf
      EndIf
    EndIf
    ;- Fill our ListIconGadget with visible Notification icons
    If tbi\fsState & #TBSTATE_ENABLED And Not tbi\fsState & #TBSTATE_HIDDEN And clickit = -2
      ;- Some text may contain Chr(10) so we will remove it
      tt$ = ReplaceString(button$, Chr(10), " ")
      AddGadgetItem(#ListIcons, -1, tt$, hIconSmall)
      ;- Store the command id for our icon
      SetGadgetItemData(#ListIcons, CountGadgetItems(#ListIcons) - 1, tbi\idCommand)
    EndIf
    ;- Destroy Icon if we extracted it from exepath
    If di
      DestroyIcon_(hIconSmall)
      di = 0
    EndIf
    ;- If we detect a click on our ListIconGadget
    If clickit > -1
      ;- Get the Notification icon position/coordinates
      index = SendMessage_(hIcons, #TB_COMMANDTOINDEX, iCmd, 0)
      SendMessage_(hIcons, #TB_GETITEMRECT, index, *Buff1)
      ReadProcessMemory_(trayProc, *Buff1, @rc.RECT, SizeOf(RECT), @result)
      ;- Convert the ListIconGadget click to a doubleclick on Notification icon
      Select clickit
        Case #PB_EventType_LeftClick
          SetForegroundWindow_(hIcons)
          PostMessage_(hIcons, #WM_LBUTTONDOWN, 0, MakeLong(rc\left, rc\top))
          PostMessage_(hIcons, #WM_LBUTTONUP, 0, MakeLong(rc\left, rc\top))
          PostMessage_(hIcons, #WM_LBUTTONDOWN, 0, MakeLong(rc\left, rc\top))
          PostMessage_(hIcons, #WM_LBUTTONUP, 0, MakeLong(rc\left, rc\top))
          PostMessage_(hIcons, #WM_LBUTTONDBLCLK, 0, MakeLong(rc\left, rc\top))
          clickit = -1
      EndSelect
    EndIf
    ct + 1
  Next
  
  ;- Clean up
  If *Buff1
    VirtualFreeEx_(trayProc, *Buff1, 0, #MEM_RELEASE)
  EndIf
  If trayProc
    CloseHandle_(trayProc) 
  EndIf
EndProcedure

;/ Main Window ======================================================
If OpenWindow(#WindowMain, 0, 0, 400, 500, "Tray Icon Test by Sparkie",  #PB_Window_SizeGadget | #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_TitleBar) And CreateGadgetList(WindowID(#WindowMain))
  info$ = "Click on any item to simulate a doubleclick on the tray icon."
  TextGadget(#TextInfo, 10, 10, 380, 50, info$, #PB_Text_Center)
  ButtonGadget(#ButtonRefresh, 10, 60, 72, 25, "Refresh list")
  ListIconGadget(#ListIcons, 10, 90, 380, 400, "Tray Icon ToolTip Text", 375, #PB_ListIcon_FullRowSelect | #PB_ListIcon_GridLines)
  ;- Get Notification Area icons and fill our ListIcon
  TaskBarList(-2, -1)
  
  ;/ Main Event Loop ==================================================
  Repeat
    event = WaitWindowEvent()
    eType = EventType()
    Select event
      Case #PB_Event_Gadget
        Select EventGadget()
          Case #ListIcons
            ;- Get the Notification icon command id
            iCmd = GetGadgetItemData(#ListIcons, GetGadgetState(#ListIcons))
            Select eType
              Case #PB_EventType_LeftClick
                TaskBarList(EventType(), iCmd)
            EndSelect
          Case #ButtonRefresh
            ClearGadgetItemList(#ListIcons)
            TaskBarList(-2, -1)
        EndSelect
    EndSelect
  Until event = #PB_Event_CloseWindow
EndIf
End
Last edited by Sparkie on Sun Mar 16, 2008 10:52 pm, edited 1 time in total.
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
Philippe-felixer76-2
Enthusiast
Enthusiast
Posts: 135
Joined: Sat Aug 18, 2007 7:09 am
Location: Netherlands

Post by Philippe-felixer76-2 »

Sparkie wrote:Here's some code for you to use. I only coded in a simulated left doubleclick. You can easily add support for single left or right clicks as needed. ;)
Thank you very much :P Very usefull..
One little thing, refresh button doesn't clear the iconlist. ;-)

Gr,
Phil.
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

You're welcome :)

Thank you for the bug report...I edited the code to apply the fix.
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
ZX80
Enthusiast
Enthusiast
Posts: 331
Joined: Mon Dec 12, 2016 1:37 pm

Re: How to open or show a running application from the Trayb

Post by ZX80 »

10 years later...
Who can change this great code by Sparkie to getting tooltip-text for each button ?

Thanks in advance.
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: How to open or show a running application from the Trayb

Post by Little John »

First steps to adapt the code by Sparkie to PureBasic 5.62 (x64) on Windows 10:
  • In line 145, replace ReadProcessMemory_(trayProc, *Buff1 + SizeOf(TBBUTTONINFO), @button$, bLen, @result)
    with ReadProcessMemory_(trayProc, *Buff1 + SizeOf(TBBUTTONINFO), @button$, 2 * bLen, @result).
    This is required, because in PB 5.62 all strings are in Unicode format.
  • In line 161, replace GetClassLong_ with GetClassLongPtr_.
    This way, the code is compatible with both 32-bit and 64-bit versions of Windows.
  • In line 168, replace GetModuleFileNameExA with GetModuleFileNameExW.
  • In line 225, remove And CreateGadgetList(WindowID(#WindowMain)).
    This is obsolete now and not needed anymore.
  • In line 248, replace ClearGadgetItemList(#ListIcons) with ClearGadgetItems(#ListIcons).
These are only first steps for modernization. After applying these changes, there are still some quirks here.
Before proceeding with additional changes, I recommend to write EnableExplicit as the first executable line, and then declare all variables accordingly. When doing so, change most if not all variables of type .l to type .i, so that the code is compatible with both 32-bit and 64-bit versions of PureBasic.
ZX80
Enthusiast
Enthusiast
Posts: 331
Joined: Mon Dec 12, 2016 1:37 pm

Re: How to open or show a running application from the Trayb

Post by ZX80 »

Little John, good day!
In this case, probably line 122 also needs to be changed
SendMessage_(hIcons, #TB_GETBUTTONINFO, i, *Buff1)
to
SendMessage_(hIcons, #TB_GETBUTTONINFOW, i, *Buff1)
for unicode. And lines 140, 143: #TB_GETBUTTONTEXT -> #TB_GETBUTTONTEXTW.

Thanks a lot for reply, edit, comments and tip, but...
It's still not enough for my task :oops:

For some reason on my test application I get memory read error message. Program stopped on line 135. I had to exclude lines 129 to 137. And variable bLen is always equal to -1. Perhaps because of fact that text on button is drawn. But each of them has a unique tooltip-text. This is what I need to get in order determine what kind of button it is. Well ... I looking for this solution.
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: How to open or show a running application from the Trayb

Post by Little John »

Hi ZX80!
ZX80 wrote:For some reason on my test application I get memory read error message. Program stopped on line 135. I had to exclude lines 129 to 137. And variable bLen is always equal to -1. Perhaps because of fact that text on button is drawn. But each of them has a unique tooltip-text. This is what I need to get in order determine what kind of button it is.
That's strange. Here with PB 5.62 (x64) on Windows 10, after applying the modifications that I had mentioned, I'm getting a Window with a ListIconGadget that contains the correct tooltip texts of all icons in the noticication area. The ListIconGadget also contains almost all regarding icons.
The main problem here is, that after clicking at some entries in the ListIconGadget the simulated doubleclick goes to the proper program, while after clicking at other entries the simulated doubleclick goes to a wrong program.

Which PB version and which Windows version are you using?

I'm not an expert in this field, so unfortunately I can't offer more help, sorry. I only listed the simple problems that I could detect. But here are other people with good knowledge of the Windows API, who probably can give you more information.
ZX80
Enthusiast
Enthusiast
Posts: 331
Joined: Mon Dec 12, 2016 1:37 pm

Re: How to open or show a running application from the Trayb

Post by ZX80 »

I'm not an expert in this field, so unfortunately I can't offer more help, sorry.
Thank you anyway.
But here are other people with good knowledge of the Windows API
Yes, I know. And I hope that they see/read this theme.

Code: Select all

Which PB version and which Windows version are you using?
PB v5.61(x86) and Win7(x86). But I don't think that problem in it.
That's strange.
I'am very sorry. I did not say at once... :oops:
With noticication area - all is okay. No problem/error messages.

I wanted to use this wonderful code for toolbar.
i.e. I want press target-button on toolbar of third-party application. Because that program have not menu item or hotkey for action I need. Only button on toolbar. Otherwise it would not be a problem.
That's all I wanted to do.
Last edited by ZX80 on Sun Sep 16, 2018 1:54 pm, edited 1 time in total.
ZX80
Enthusiast
Enthusiast
Posts: 331
Joined: Mon Dec 12, 2016 1:37 pm

Re: How to open or show a running application from the Trayb

Post by ZX80 »

Аll buttons are visible and accessible (from ListIconGadget), but none has a specific text or icon. In this case how do you determine which button you need pressed ? All - faceless (text is - "hidden"). This is a problem. Because some of them can be added or removed time by time.
Post Reply