• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 3442
  • Last Modified:

List all System Tray Icons and the owning process

Dear Experts,

I would like to make or have a utility to list of all the icons in the system tray, showing the process that owns each one.
e.g.  ACTUAL ICON, PROCESS ID, PROCESS NAME

Why? Well partly out of interest i.e. can it be done? Also, sometimes you get an icon in the tray but it doesn't indicate what application it belongs to. Strange, but occasionally true. So you might want to stop the app loading but don't know which one it is. You can of course kill every running process, one-by-one, til the icon disappears - not such a good method!.

From what I have googled, doing  this may not be possible due to Windows limitations!
There was a related question back in 2000:
(http://www.experts-exchange.com/Programming/Languages/Visual_Basic/Q_10674601.html)
Maybe there is more information about doing this now?

BTW I use a tray manager (TrayMan from PC Mag) to hide some icons. It's list of tray icons sometimes shows entries that don't have an icon! Is that possible?

The fact that TrayMan can list the icons shows that that part is at least possible.

Anyway, a working binary or  Delphi code (full app, or code fragments that demonstrate how to do it) will get you the points. VB code is less desirable, but I do have a VB6 compiler around.

Thanks!
0
WinRat
Asked:
WinRat
1 Solution
 
John HurstBusiness Consultant (Owner)Commented:
Windows Defender -> Tools -> Software Explorer will give you a very good list of startup programs, the details about each program and a good way to temporarily (or even permanently) disable a program from starting up.  .... T
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Ark worked alot on a similar project quite awhile ago and ran into many problems (including differences in OS versions).  Read the ENTIRE post here and see if any of his work helps you out:
http://www.experts-exchange.com/Programming/Languages/Visual_Basic/Q_21145831.html
0
 
nffvrxqgrcfqvvcCommented:
I have written a small example in VB that works for Window XP as it's the only OS I am using. This will basically show you how to do it and it's not very hard the only concern would be to test it on other operating systems 2000, Vista . I would suspect that for each OS you may need to change how you find the handle to the tray window but the remaining should work fine. I would also enable SeDebugPrivliges if you want to work with PROCESS_ALL_ACCESS depending on what you need to do with the process handles that own the tray icons.

You can get all the information for each tray icon process owner this includes.

ThreadID
ProcessID
WindowHandle
ProcessName
Icon

You can extract the icon by using the path to the process using ExtractIcon, DrawIcon, DestroyIcon.

This is about as small as you can get the code, although it's in VB I can't convert it do delphi so I will leave that part up to you or another expert may be able to convert it.

Option Explicit
 
' Systemtray information.
 
Private Const PROCESS_QUERY_INFORMATION     As Long = 1024
Private Const PROCESS_VM_READ               As Long = &H10
 
Private Const MEM_COMMIT                    As Long = &H1000
Private Const MEM_RELEASE                   As Long = &H8000
Private Const PAGE_READONLY                 As Long = &H2
 
Private Const WM_USER                       As Long = &H400
Private Const TB_BUTTONCOUNT                As Long = (WM_USER + 24)
Private Const TB_GETBUTTON                  As Long = (WM_USER + 23)
 
Private Const TBSTATE_HIDDEN                As Long = &H8
 
Private Type TrayMore
  SyshWnd       As Long
  SysIconId     As Long
End Type
 
Private Type TBBUTTON
  iBitmap      As Long
  idCommand    As Long
  fsState      As Byte
  fsStyle      As Byte
  bReserved1   As Byte
  bReserved2   As Byte
  dwData       As Long
  iString      As Long
End Type
 
Private Declare Function FindWindowW Lib "user32" (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
Private Declare Function FindWindowExW Lib "user32" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As Long, ByVal lpsz2 As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, ByVal lpBuffer As Long, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function GetModuleFileNameExW Lib "Psapi.dll" (ByVal hProcess As Long, ByVal hModule As Long, ByVal lpFileName As Long, ByVal nSize As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
 
Public Function GetProcessNameFromhWnd(ByVal dwhWnd As Long) As String
 
  ' Returns process name from hwnd.
  
  Dim Buffer(1024 - 1)  As Byte
  Dim hProcess          As Long
  Dim ThreadId          As Long
  Dim Pid               As Long
  Dim cbLen             As Long
  
  ThreadId = GetWindowThreadProcessId(dwhWnd, Pid)
  hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0, Pid)
  cbLen = GetModuleFileNameExW(hProcess, 0, VarPtr(Buffer(0)), UBound(Buffer))
  GetProcessNameFromhWnd = Left$(Buffer, cbLen)
  CloseHandle hProcess
  Erase Buffer
  
End Function
 
Public Sub GetTrayWindowInfo()
 
  Dim Tray            As TBBUTTON
  Dim TrayM           As TrayMore
  Dim ThreadId        As Long
  Dim hProcess        As Long
  Dim AddrPtr         As Long
  Dim Pid             As Long
  Dim cb              As Long
  Dim cbRead          As Long
  Dim TrayCount       As Long
  Dim hWindow         As Long
  
  ' * Get the tray window handle. ( Window XP).
  hWindow = FindWindowW(StrPtr("Shell_TrayWnd"), 0)
  hWindow = FindWindowExW(hWindow, 0, StrPtr("TrayNotifyWnd"), 0)
  hWindow = FindWindowExW(hWindow, 0, StrPtr("SysPager"), 0)
  hWindow = FindWindowExW(hWindow, 0, StrPtr("ToolbarWindow32"), 0)
  
  ' * Get the count of buttons in tray window.
  TrayCount = SendMessage(hWindow, TB_BUTTONCOUNT, 0, 0)
  
  ' Allocate memory
  ThreadId = GetWindowThreadProcessId(hWindow, Pid)
  hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0, Pid)
  AddrPtr = VirtualAllocEx(hProcess, 0, LenB(Tray), MEM_COMMIT, PAGE_READONLY)
  
  ' Read information into TBUTTON struct
  For cb = 0 To TrayCount - 1
  
    SendMessage hWindow, TB_GETBUTTON, cb, AddrPtr
    ReadProcessMemory hProcess, AddrPtr, VarPtr(Tray), LenB(Tray), cbRead
    ReadProcessMemory hProcess, ByVal Tray.dwData, VarPtr(TrayM), LenB(TrayM), cbRead
    
    ' * Show only the visible buttons(remove this line to show all)
    If Tray.fsState <> TBSTATE_HIDDEN Then
      Debug.Print GetProcessNameFromhWnd(TrayM.SyshWnd)
    End If
    
  Next cb
  
  ' * Cleanup
  VirtualFreeEx hProcess, AddrPtr, 0, MEM_RELEASE
  CloseHandle hProcess
     
End Sub

Open in new window

0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
WinRatAuthor Commented:
Thanks Experts:
I will need to look at the link. The VB code sounds promising. I  will try to convert to Delphi.
Also need to find the VB compiler I have somewhere, install it and test the example.
Will only be tomorrow - have been on Dial-Up for hours today with other outstanding questions.
Thanks!

0
 
WinRatAuthor Commented:
Hi All,

It took me some time to locate the CD's for VB that a friend had donated to me (no longer required)  - actually I see its Visual Studio 6.0a.
Unfortunately the first install CD has some read errors and I couldn't get past that tonight.
Will have to use a CD data extractor to get the files off to my HD, but its at work.
0
 
WinRatAuthor Commented:
egl1044:
The VS Cd was too corrupted so have abandoned that.
I checked through your code more careful.
It looks like all the Windows functions that you defined are already decalred in Delphi's Windows.pas unit.
I think I should be able to convert it as its not very long.
Just the memory allocation and variable types I am not so sure of.

0
 
WinRatAuthor Commented:
egl1044:

Hi not sure if you're still monitoring? Sorry been off at a tangent on other things.
Haven't yet been able to get your code ported to Delphi, but I got hold of a copy of VB6 and installed that. Unfortunately the MSDN CD was corrupted so it wouldn't install which means I get no help in VB6! Not even how to use it!

I then installed a copy of MSDN for .NET 2003 thinking VB6 would be able to access it but it still says no MSDN installed.

I've never used VB stand-alone and hadn't a clue how to get your code to work.
I have used VBA in Excel a bit, so I tried pasting it into a module and running it. At first I thought it wasn't working but then I found the Immediate Execute window and saw the output.

Well, it actually works! After all my other researching I thought this was kind of impossible. There are several Explorer icons (LAN, volume etc) and this is where having the icon in a list would be nice.

Since my knowledge of using vb to make an EXE is very limited may I ask you to improve it a bit?

Could you wrap your code into a project file with a form, listbox and Run button to fill the listbox. I'd like the listbox to display the (small) icon for the process first, followed by the process path. If you are a regular vb user I suppose this should be easy for you?

I will be happy to award you full points if you could do that. Actually even if you don't want to do the extra icon work, but could just adapt the example so I can run it in vb6 with the process path  output to a listbox that would be OK.

Thanks!

0
 
CastorixCommented:
Just use ICONDATA internal structure and sExeName member. C code posted > 10 years ago...
0
 
WinRatAuthor Commented:
With the help of some downloaded examples I have managed to create a working VB project using a listbox to hold the process paths from member "egl1044"s code.
I must say that VB6 visual components are not a patch on Delphi (unless its because I don't know what I'm doing!). For example I can't align the list box to the form so that if the text is too long for the listbox and the user drags the form bigger, then the listbox follows it.

So now I have a working EXE - except for the icons. I have a working VB example on extracting an icon.  Now I need to learn how to write icons to a listbox. I've never done it in Delphi but I do know you have to set the ListBox Style to an owner draw mode. Without any help I can't do it in vb6 and the listbox doesn't seem to have such a property.

From other related posts over the years it seem getting info on the tray icons and their owners has been asked a lot - with no real success. I'm sure other members will be interested in this. Congratulations to egl1044. I am going to award him full points as I did say that a code fragment and a VB solution were acceptable.

0
 
WinRatAuthor Commented:
Thanks egl1044. Rated partially complete as didn't  include code to extract icon.  Otherwise works well.
0
 
nffvrxqgrcfqvvcCommented:
Create the following on the Form1

1 Command Button
1 Listbox
1 Picturebox

Copy the attach code into Form1
Option Explicit
 
Private Const PROCESS_QUERY_INFORMATION     As Long = 1024
Private Const PROCESS_ALL_ACCESS            As Long = &H1F0FFF
Private Const PROCESS_VM_READ               As Long = &H10
Private Const MEM_COMMIT                    As Long = &H1000
Private Const MEM_RELEASE                   As Long = &H8000
Private Const PAGE_READONLY                 As Long = &H2
Private Const PAGE_READWRITE                As Long = &H4
Private Const WM_USER                       As Long = &H400
Private Const TB_BUTTONCOUNT                As Long = (WM_USER + 24)
Private Const TB_GETBUTTON                  As Long = (WM_USER + 23)
Private Const TBSTATE_HIDDEN                As Long = &H8
Private Const DI_MASK                       As Long = &H1
Private Const DI_IMAGE                      As Long = &H2
 
Private Type TRAYDATA
  hwnd          As Long
  uID           As Long
  uCallback     As Long
  Reserved1     As Long
  Reserved2     As Long
  hIcon         As Long
End Type
 
Private Type TBBUTTON
  iBitmap      As Long
  idCommand    As Long
  fsState      As Byte
  fsStyle      As Byte
  bReserved1   As Byte
  bReserved2   As Byte
  dwData       As Long
  iString      As Long
End Type
 
Private Declare Function FindWindowW Lib "user32" (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
Private Declare Function FindWindowExW Lib "user32" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As Long, ByVal lpsz2 As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function GetModuleFileNameExW Lib "Psapi.dll" (ByVal hProcess As Long, ByVal hModule As Long, ByVal lpFileName As Long, ByVal nSize As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Long, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cbCopy As Long)
Private Declare Function DrawIconEx Lib "user32" (ByVal hdc As Long, ByVal xLeft As Long, ByVal yTop As Long, ByVal hIcon As Long, ByVal cxWidth As Long, ByVal cyWidth As Long, ByVal istepIfAniCur As Long, ByVal hbrFlickerFreeDraw As Long, ByVal diFlags As Long) As Long
Private Declare Function ExtractIconA Lib "shell32" (ByVal hInst As Long, ByVal lpszExeFileName As String, ByVal nIconIndex As Long) As Long
Private Declare Function DestroyIcon Lib "user32" (ByVal hIcon As Long) As Long
 
Public Function GetProcessNameFromhWnd(ByVal dwhWnd As Long) As String
 
  ' Returns process name from hwnd.
  
  Dim Buffer(1024 - 1)  As Byte
  Dim hProcess          As Long
  Dim ThreadId          As Long
  Dim Pid               As Long
  Dim cbLen             As Long
  
  ThreadId = GetWindowThreadProcessId(dwhWnd, Pid)
  hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, Pid)
  cbLen = GetModuleFileNameExW(hProcess, 0, VarPtr(Buffer(0)), UBound(Buffer))
  GetProcessNameFromhWnd = Left$(Buffer, cbLen)
  CloseHandle hProcess
  Erase Buffer
  
End Function
    
Public Sub GetTrayWindowInfo()
 
  Dim tb            As TBBUTTON
  Dim tray          As TRAYDATA
  Dim ThreadId        As Long
  Dim hProcess        As Long
  Dim AddrPtr         As Long
  Dim AddrPtr2        As Long
  Dim Pid             As Long
  Dim cb              As Long
  Dim cbRead          As Long
  Dim TrayCount       As Long
  Dim hWindow         As Long
  Dim szName          As String
  Dim IconPos         As Long
  Dim hIcon           As Long
  
  ' * Get the tray window handle. ( Window XP).
  hWindow = FindWindowW(StrPtr("Shell_TrayWnd"), 0)
  hWindow = FindWindowExW(hWindow, 0, StrPtr("TrayNotifyWnd"), 0)
  hWindow = FindWindowExW(hWindow, 0, StrPtr("SysPager"), 0)
  hWindow = FindWindowExW(hWindow, 0, StrPtr("ToolbarWindow32"), 0)
  
  ' * Get the count of buttons in tray window.
  TrayCount = SendMessage(hWindow, TB_BUTTONCOUNT, 0, 0)
 
  ' Allocate memory
  ThreadId = GetWindowThreadProcessId(hWindow, Pid)
  hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, Pid)
  AddrPtr = VirtualAllocEx(hProcess, 0, LenB(tray), MEM_COMMIT, PAGE_READONLY)
  AddrPtr2 = AddrPtr
 
  ' Read information into TBUTTON
  For cb = 0 To TrayCount - 1
  
    SendMessage hWindow, TB_GETBUTTON, cb, AddrPtr2
    ReadProcessMemory hProcess, AddrPtr2, tb, LenB(tb), cbRead
    ReadProcessMemory hProcess, ByVal tb.dwData, tray, LenB(tray), cbRead
    
    ' Check if the button is hidden and only show visible tray buttons.
    If tb.fsState <> TBSTATE_HIDDEN Then
    ' Get the process associated with the button.
      szName = GetProcessNameFromhWnd(tray.hwnd)
    ' Add the process location to a listbox
      List1.AddItem szName
    ' Use Tray.hIcon to attemp to draw icon into picture box.
      If DrawIconEx(Picture1.hdc, IconPos, 0, tray.hIcon, 16, 16, 0, 0, DI_MASK Or DI_IMAGE) = False Then
    ' If the Tray.hIcon is invalid try to extract the first icon instead.
        hIcon = ExtractIconA(0, szName, 0)
        Call DrawIconEx(Picture1.hdc, IconPos, 0, hIcon, 16, 16, 0, 0, DI_MASK Or DI_IMAGE)
        DestroyIcon hIcon
      End If
      ' Move the icon position for next icon in pictureBox
      IconPos = IconPos + 16
    End If
    
  Next cb
  
  ' * Cleanup
  VirtualFreeEx hProcess, AddrPtr, 0, MEM_RELEASE
  CloseHandle hProcess
     
End Sub
 
Private Sub Command1_Click()
GetTrayWindowInfo
End Sub

Open in new window

0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now