It is currently Thu Jul 18, 2019 2:49 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 9 posts ] 
Author Message
 Post subject: List Taskbar buttons
PostPosted: Fri Aug 05, 2005 2:16 am 
Offline
Enthusiast
Enthusiast

Joined: Sat Apr 26, 2003 3:23 pm
Posts: 263
Code updated For 5.20+

Hi all,

Other methods of doing this have never quite worked 100% for me, but this seems to do the trick. I'd be interested in hearing if it gets it wrong for anyone (state the program if it does!). You need the debugger turned on, and please note that it won't list the Debug Output window since that's not open when it makes the list!

USAGE: Call GetTBarButtons () and iterate through the TBarButtons () list, which can show the window's title -- "TBarButtons ()\name" -- and window handle -- "TBarButtons ()\window".

Code:
Structure TaskbarButton
    name.s
    window.l
EndStructure

Global NewList TBarButtons.TaskbarButton ()

; The rules for checking whether a button is shown on the Taskbar
; or not are listed at the bottom of this file...

; Note: one difference with Task Manager's output is that this lists
; Task Manager's Taskbar button -- Task Manager skips it!

; GAH! Frigging EditPad and its crazy not-hidden-but-invisible window! (Sorted?)

; Windows 10 fix by BarryG:

Prototype DwmGetWindowAttribute_(hWnd,dwAttribute.l,*pvAttribute,cbAttribute.l)
Procedure IsHwndVisible(hWnd)
  vis=-1 ; Means hWnd not found or couldn't determine visibility status.
  c$=Space(999)
  GetClassName_(hWnd,c$,999)
  If c$<>"ApplicationFrameWindow" And c$<>"Windows.UI.Core.CoreWindow" ; Normal Win32 window.
    vis=IsWindowVisible_(hWnd)
    If vis<>0 : vis=1 : EndIf
  Else ; Windows 10 UWP window, which can return non-zero for IsWindowVisible_() despite not shown!
    Define DwmGetWindowAttribute_.DwmGetWindowAttribute_
    #DWMWA_CLOAKED=14
    DWMAPIDLL=OpenLibrary(#PB_Any,"DWMAPI.DLL")
    If DWMAPIDLL
      DwmGetWindowAttribute_=GetFunction(DWMAPIDLL,"DwmGetWindowAttribute")
      If DwmGetWindowAttribute_ And DwmGetWindowAttribute_(hWnd,#DWMWA_CLOAKED,@Cloaked,SizeOf(Cloaked))=#S_OK
        If Cloaked=0
          vis=1
        Else
          vis=0
        EndIf
      EndIf
      CloseLibrary(DWMAPIDLL)
    EndIf
  EndIf
  ProcedureReturn vis
EndProcedure

Procedure ListTaskbarButtons (window, lparam)

    If IsHwndVisible (window)

        flags = GetWindowLong_ (window, #GWL_EXSTYLE)

        If flags & #WS_EX_APPWINDOW
            visible = #True
        Else
            If flags & #WS_EX_TOOLWINDOW
                visible = #False
            Else
                ; Neither style set...
                If GetParent_ (window) = #Null
                    visible = #True
                EndIf
            EndIf
        EndIf

        ; Not covered in rules, but EditPad 3.5.1 is listed as two buttons when only
        ; one is present, and this flag is in the child window (something to do with
        ; allowing input to reach child windows with this flag)...
       
        If flags & #WS_EX_CONTROLPARENT
            visible = #False
        EndIf
       
        If visible
       
            title$ = Space (1024)
            GetWindowText_ (window, @title$, 1024)

            AddElement (TBarButtons ())
            TBarButtons ()\name = title$
            TBarButtons ()\window = window
           
        EndIf
       
    EndIf

    ProcedureReturn #True

EndProcedure

Procedure GetTBarButtons ()
    ClearList (TBarButtons ())
    EnumWindows_ (@ListTaskBarButtons (), 0)
EndProcedure

; D E M O . . .

; Get list of visible buttons...

GetTBarButtons ()

; Iterate through list...

ResetList (TBarButtons ())
While NextElement (TBarButtons ())
    Debug TBarButtons ()\name + " (handle: " + Str (TBarButtons ()\window) + ")"
Wend

; THE RULES FOR TASKBAR BUTTON VISIBILITY!

; From: http://www.microsoft.com/msj/1197/win321197.aspx

; The rules the taskbar uses to decide whether a button should be shown for a window are really quite simple,
; but are not well documented. When you create a window, the taskbar examines the window's extended style
; to see if either the WS_EX_APPWINDOW (defined as 0x00040000) or WS_EX_TOOLWINDOW (defined as
; 0x00000080) style is turned on. If WS_EX_APPWINDOW is turned on, the taskbar shows a button for the
; window, and if WS_EX_ TOOLWINDOW is turned on, the taskbar does not show a button for the window. You
; should never create a window that has both of these extended styles.

; You can create a window that doesn't have either of these styles. If a window has neither style, the taskbar
; decides to create a button if the window is unowned and does not create a button if the window is owned.

; One final note: before making any of the above tests, the taskbar first checks to see if a window has the
; standard WS_VISIBLE window style turned on. If this style bit is off, the window is hidden; the taskbar
; never shows a button for a hidden window. Only if the WS_VISIBLE style bit is on will the taskbar check
; the WS_EX_APPWINDOW, WS_ EX_TOOLWINDOW, and window ownership information.


_________________
James Boyd
http://www.hi-toro.com/
Death to the Pixies!


Last edited by Hi-Toro on Mon Jun 03, 2019 5:39 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: And...
PostPosted: Fri Aug 05, 2005 2:22 am 
Offline
Enthusiast
Enthusiast

Joined: Sat Apr 26, 2003 3:23 pm
Posts: 263
You can combine it with this to get the processes listed in the Taskbar:

Code:
; -----------------------------------------------------------------------------
; Return a window's process ID from its handle...
; -----------------------------------------------------------------------------

Procedure.l FindWindowProcessID (window)
    GetWindowThreadProcessId_ (window, @pid)
    ProcedureReturn pid
EndProcedure


... and combine it with this to kill the program (only if sending a close message to the window fails!)...

Code:
#PROCESS_TERMINATE = $1
#PROCESS_CREATE_THREAD = $2
#PROCESS_VM_OPERATION = $8
#PROCESS_VM_READ = $10
#PROCESS_VM_WRITE = $20
#PROCESS_DUP_HANDLE = $40
#PROCESS_CREATE_PROCESS = $80
#PROCESS_SET_QUOTA = $100
#PROCESS_SET_INFORMATION = $200
#PROCESS_QUERY_INFORMATION = $400
#PROCESS_ALL_ACCESS = #STANDARD_RIGHTS_REQUIRED | #SYNCHRONIZE | $FFF

; This appears to be pretty much how Windows kills a program if you 'End Process'
; from the Task Manager. Note that this is 'unfriendly'!

Procedure KillProcess (pid)
    phandle = OpenProcess_ (#PROCESS_TERMINATE, #FALSE, pid)
    If phandle <> #NULL
        If TerminateProcess_ (phandle, 1)
            result = #TRUE
        EndIf
        CloseHandle_ (phandle)
    EndIf
    ProcedureReturn result
EndProcedure

; Enter process ID here! I suggest going to Task Manager,
; making sure PIDs are shown (try View menu -> Select columns if
; they are not listed), then run a program and enter its number here...

Debug KillProcess ( x )

_________________
James Boyd
http://www.hi-toro.com/
Death to the Pixies!


Last edited by Hi-Toro on Fri Aug 05, 2005 2:28 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Aaaand this...
PostPosted: Fri Aug 05, 2005 2:23 am 
Offline
Enthusiast
Enthusiast

Joined: Sat Apr 26, 2003 3:23 pm
Posts: 263
You can also get the process name from the window handle using this code. Simple usage: Gosub GetProcessList (see explanation at top of program) and call FindWindowProcessName (window)...

Code:
; -----------------------------------------------------------------------------
; Public domain -- Hi-Toro 2003
; -----------------------------------------------------------------------------
; Return a window's process name from its handle...
; -----------------------------------------------------------------------------

; IMPORTANT! You must paste the following section of code (from here to the
; demo section) at the top of your code, AND paste the part at the bottom
; (the 'GetProcessList' sub-routine) at the bottom of your code. The reason the
; sub-routine is required (rather than a procedure) is that the Win32 function
; 'Process32Next' seems to fail on Windows 9x when called from inside a procedure...

; -----------------------------------------------------------------------------
; Paste at top of your code...
; -----------------------------------------------------------------------------

#TH32CS_SNAPHEAPLIST = $1
#TH32CS_SNAPPROCESS = $2
#TH32CS_SNAPTHREAD = $4
#TH32CS_SNAPMODULE = $8
#TH32CS_SNAPALL = #TH32CS_SNAPHEAPLIST | #TH32CS_SNAPPROCESS | #TH32CS_SNAPTHREAD | #TH32CS_SNAPMODULE
#TH32CS_INHERIT = $80000000
#INVALID_HANDLE_VALUE = -1
#MAX_PATH = 260
#PROCESS32LIB = 9999

Structure PROCESSENTRY32
    dwSize.l
    cntUsage.l
    th32ProcessID.l
    *th32DefaultHeapID.l
    th32ModuleID.l
    cntThreads.l
    th32ParentProcessID.l
    pcPriClassBase.l
    dwFlags.l
    szExeFile.b [#MAX_PATH]
EndStructure

; List used to store processes on 'Gosub GetProcessList'...

NewList Process32.PROCESSENTRY32 ()

; Returns process name from window handle...
; IMPORTANT! You should 'Gosub GetProcessList' before calling this!

Procedure.s FindWindowProcessName (window)
    ResetList (Process32 ())
    While NextElement (Process32 ())
        GetWindowThreadProcessId_ (window, @pid)
        If pid = Process32 ()\th32ProcessID
            exe$ = GetFilePart (PeekS (@Process32 ()\szExeFile))
            LastElement (Process32 ())
        EndIf
    Wend
    ProcedureReturn exe$
EndProcedure

; Returns Process ID from window handle...

Procedure.l FindWindowProcessID (window)
    GetWindowThreadProcessId_ (window, @pid)
    ProcedureReturn pid
EndProcedure

; -----------------------------------------------------------------------------
; D E M O...
; -----------------------------------------------------------------------------

window = OpenWindow (0, 0, 0, 320, 200, #PB_Window_SystemMenu | #PB_Window_ScreenCentered, "Test window")

Gosub GetProcessList

SetTimer_ (WindowID (), 0, 100, 0)

Repeat
    Select WaitWindowEvent ()
        Case #PB_Event_CloseWindow
            End
        Case #WM_TIMER
            Gosub GetProcessList
            GetCursorPos_ (@p.POINT)
            over = WindowFromPoint_ (p\x, p\y)
            SetWindowText_ (window, FindWindowProcessName (over))
    EndSelect
ForEver

; -----------------------------------------------------------------------------
; Paste at bottom of your code...
; -----------------------------------------------------------------------------

End ; Leave this here!

GetProcessList:

    ClearList (Process32 ())

    ; Add processes to Process32 () list...

    If OpenLibrary (#PROCESS32LIB, "kernel32.dll")

        snap = CallFunction (#PROCESS32LIB, "CreateToolhelp32Snapshot", #TH32CS_SNAPPROCESS, 0)

        If snap

            DefType.PROCESSENTRY32 Proc32
            Proc32\dwSize = SizeOf (PROCESSENTRY32)
           
            If CallFunction (#PROCESS32LIB, "Process32First", snap, @Proc32)

                AddElement (Process32 ())
                CopyMemory (@Proc32, @Process32 (), SizeOf (PROCESSENTRY32))
               
                While CallFunction (#PROCESS32LIB, "Process32Next", snap, @Proc32)
                    AddElement (Process32 ())
                    CopyMemory (@Proc32, @Process32 (), SizeOf (PROCESSENTRY32))
                Wend
               
            EndIf   
            CloseHandle_ (snap)
       
        EndIf

        CloseLibrary (#PROCESS32LIB)
       
    EndIf

Return

_________________
James Boyd
http://www.hi-toro.com/
Death to the Pixies!


Top
 Profile  
Reply with quote  
 Post subject: Re: Aaaand this...
PostPosted: Sun May 07, 2017 8:23 am 
Offline
User
User

Joined: Thu Mar 02, 2017 11:15 pm
Posts: 14
Location: Barcelona
Can this code know which order the buttons on the taskbar are, so I can sort them in an array with index 0 being the first button (leftmost, next to "Start") on the taskbar, and index N being the last button (rightmost, next to the clock) on the taskbar? That would be handy.


Top
 Profile  
Reply with quote  
 Post subject: Re: Aaaand this...
PostPosted: Thu May 17, 2018 3:32 am 
Offline
Addict
Addict

Joined: Mon Feb 16, 2015 2:49 pm
Posts: 1893
+1, in case anyone knows how to sort by actual taskbar order.


Top
 Profile  
Reply with quote  
 Post subject: Re: Aaaand this...
PostPosted: Mon Jun 03, 2019 3:56 am 
Offline
Enthusiast
Enthusiast

Joined: Thu Apr 18, 2019 8:17 am
Posts: 193
Note: The code in the first post no longer works with Windows 10, and will list taskbar buttons for Windows 10 UWP windows that aren't actually visible (such as Settings, Xbox, and so on). To fix this, use my IsHwndVisible() code from viewtopic.php?f=12&t=72941 and replace the following line in the first post above:

Code:
;If IsWindowVisible_ (window) ; Old.
If IsHwndVisible (window) ; New.


Top
 Profile  
Reply with quote  
 Post subject: Re: List Taskbar buttons
PostPosted: Mon Jun 03, 2019 5:42 pm 
Offline
Enthusiast
Enthusiast

Joined: Sat Apr 26, 2003 3:23 pm
Posts: 263
Thanks, BarryG, have updated first post with your code.

(It works on WIndows 7 too!)

Interestingly, while running a quick test, I immediately found a window that doesn't get picked up with either version: the main window of the Reaper DAW (https://reaper.fm/), which appears otherwise standard, appears in the taskbar, but not in the results here.

I won't get to look into it for a while, but will try remember later in the week...

_________________
James Boyd
http://www.hi-toro.com/
Death to the Pixies!


Top
 Profile  
Reply with quote  
 Post subject: Re: List Taskbar buttons
PostPosted: Tue Jun 04, 2019 12:42 am 
Offline
Enthusiast
Enthusiast

Joined: Thu Apr 18, 2019 8:17 am
Posts: 193
Hi-Toro wrote:
(It works on WIndows 7 too!)

Yes, my IsHwndVisible() procedure is backwards-compatible. Everyone needs to STOP using IsWindowVisible_() immediately if their app is intended for use with Windows 10 - it returns incorrect results.

As for your Reaper problem, I checked and it's getting marked as "visible = #False" in your EditPad test:

Code:
If flags & #WS_EX_CONTROLPARENT ; For EditPad, but Reaper's window also matches this condition.
  visible = #False
EndIf

I don't have EditPad to test, but I suggest you test the above condition with EditPad's class name (not window title) as well, so it doesn't trigger for other windows.


Top
 Profile  
Reply with quote  
 Post subject: Re: List Taskbar buttons
PostPosted: Tue Jun 04, 2019 4:02 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Apr 12, 2009 6:27 am
Posts: 3351
Modified and added BarryG snippet to list the actual and visible Running applications
Tested with Windows 10

Code:
Prototype DwmGetWindowAttribute(hWnd,dwAttribute.l, *pvAttribute,cbAttribute.l)
Global DwmGWA.DwmGetWindowAttribute,hWnd,title${#MAX_PATH}, class${#MAX_PATH}
#DWMWA_CLOAKED = 14

Procedure EnumRunningWindows()
  hWnd = FindWindow_(0, 0)
  Repeat
    hWnd = GetWindow_(hWnd, #GW_HWNDNEXT)
    If hWnd And IsWindowVisible_(hWnd) And GetWindowLongPtr_(hWnd, #GWL_HWNDPARENT) = 0
      GetWindowText_(hWnd, @title$, #MAX_PATH)
      GetClassName_(hWnd,@class$,#MAX_PATH)
      If class$ = "ApplicationFrameWindow" Or class$ = "Windows.UI.Core.CoreWindow"
        dll = OpenLibrary(#PB_Any,"DWMAPI.DLL")
        If dll
          DwmGWA = GetFunction(dll,"DwmGetWindowAttribute")
          If DwmGWA
            DwmGWA(hWnd,#DWMWA_CLOAKED,@Cloaked,SizeOf(Cloaked))
            If Cloaked = 0
              Debug title$ + "   " + Str(hwnd)
            EndIf
          EndIf
          CloseLibrary(dll)
        EndIf
      ElseIf title$ <> "" And title$ <> "Start" And title$ <> "Program Manager"
        Debug title$ + "   " + Str(hwnd)
      EndIf
    EndIf
  Until hWnd = 0
  ProcedureReturn #False
EndProcedure

EnumRunningWindows()

_________________
Egypt my love


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye