Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

System Tray - List applications 2

Status
Not open for further replies.

Loomah

Technical User
Mar 4, 2002
1,911
IE
I've looked through many sites for what I thought would be an easy thing to find but it seems it isn't so common! Plenty on how to add, change etc apps in system tray but not list.

So, is it possible to list applications currently in the system tray (visible or not) inluding where the target file is located??

I'm not a VB programmer, as such. Comfortable with the VB environment and the idea of API. I'm used to VBA and have VB5.

Any help or pointers are greatly appreciated!

My reason for asking is quite irritating. I have 2 pieces of adware/spyware running in the system tray ([almost] amusingly advertising anti-adware/spyware products!) Full system scans (using Norton Internet Security(!) 2005) reveal nothing even with the absolute latest definitions. I'd just like to be able to contact syantec and ask "how do i remove (application filename) from my system?"

;-)
If a man says something and there are no women there to hear him, is he still wrong? [ponder]
How do I get the best answers?
 
Try using another anti-spyware program. Some viruses and spyware target certain "popular" programs and are able to hide from them. I had one computer I was working on that had a similar problem. I ran AVAST on it and found over 30 other viruses and then ran their program and found several that AVAST had missed.


It's free for home use.
 
CaptainD
That appears to have done the trick though I still have doubts about the full cleanliness of my pc. Home page has been hijacked and despite running Avast a couple of times at boot up and quarantining found threats I can still find the same problems if I run it again!!

The absolute best bit is that the irritating sys tray popups are gone though I'm in danger of letting that lull me into a false sense of security.

Have a purple pointy pip for the suggestion. Obviously the adware was pointing to anti spy/ad ware products ut the method of advertising put me off!! Avast seems sucessful.

Out of interest, 2 things

1. A warning to all. I spent about an hour chatting online to Symantec "technical support" last night. All the guy wanted to really do was get me to pay $70 (a sum that is meanigless to me, I'm afraid!) to us their consultation facility to remove the adware. Basically doing the same thing as the adware was doing in the first place!!!

2. Is it possible to do, in VB, what I ws asking in the original thread ie list all apps/icons running in the system tray?

Happy weekends to all!!

;-)
If a man says something and there are no women there to hear him, is he still wrong? [ponder]
How do I get the best answers?
 
>Is it possible to ... list all apps/icons running in the system tray?
Not that I've found. But I'd be happy to be proved wrong ...
 
I've found the Itty Bitty Process Manager to be very useful in determining what was running on my PC.

Tracy Dryden

Meddle not in the affairs of dragons,
For you are crunchy, and good with mustard. [dragon]
 
Sure, there are loads of ways of determining what is running on a PC. But it is tricky to determine which of those processes have an icon in the system tray.
 
Google search revealed the following article on top regarding this topic.
Shell Tray Info - Arrange your system tray icons

It uses some undocumented features in Windows Shell to retrieve this information. The following VB code I just wrote is based on this article.
___
[tt]
Option Explicit
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 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 FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId 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 CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, ByRef dwSize As Long, ByVal dwFreeType As Long) As Long
Private Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, ByRef dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, Optional lpNumberOfBytesWritten As Long) As Long
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, Optional lpNumberOfBytesWritten As Long) As Long
Private Declare Function EnumProcessModules Lib "psapi" (ByVal hProcess As Long, ByRef lphModule As Long, ByVal cb As Long, ByRef lpcbNeeded As Long) As Long
Private Declare Function GetModuleFileNameEx Lib "psapi" Alias "GetModuleFileNameExA" (ByVal hProcess As Long, ByVal hModule As Long, ByVal ModuleName As String, ByVal nSize As Long) As Long
Const PROCESS_QUERY_INFORMATION = (&H400)
Const PROCESS_VM_READ = (&H10)
Const PROCESS_VM_WRITE = (&H20)
Const PROCESS_VM_OPERATION = (&H8)
Const MEM_COMMIT = &H1000
Const MEM_RESERVE = &H2000
Const MEM_RELEASE = &H8000
Const PAGE_READWRITE = &H4
Const MAX_PATH = 260
Const WM_USER = &H400
Const TB_BUTTONCOUNT = (WM_USER + 24)
Const TB_GETBUTTON = (WM_USER + 23)
Const TB_GETBUTTONTEXTA = (WM_USER + 45)
Private Type TBBUTTON
iBitmap As Long
idCommand As Long
fsState As Byte
fsStyle As Byte
dwData As Long
iString As Long
End Type
Private Type TRAYDATA
hWnd As Long
uID As Long
uCallbackMessage As Long
Reserved1 As Long
Reserved2 As Long
hIcon As Long
End Type

Private Sub Form_Load()
Dim hSysTray As Long, pID As Long, hProcess As Long, N As Long, Y As Long, L As Long
Dim tbb As TBBUTTON, lptbb As Long, td As TRAYDATA, lptd As Long, S As String * 128
BackColor = vbWhite
AutoRedraw = True
hSysTray = GetNotificationWindow
GetWindowThreadProcessId hSysTray, pID
hProcess = OpenProcess(PROCESS_VM_READ Or PROCESS_VM_WRITE Or PROCESS_VM_OPERATION, 0, pID)
lptbb = VirtualAllocEx(hProcess, ByVal 0&, Len(tbb), MEM_COMMIT Or MEM_RESERVE, PAGE_READWRITE)
lptd = VirtualAllocEx(hProcess, ByVal 0&, Len(td), MEM_COMMIT Or MEM_RESERVE, PAGE_READWRITE)
L = VirtualAllocEx(hProcess, ByVal 0&, Len(S), MEM_COMMIT Or MEM_RESERVE, PAGE_READWRITE)
N = SendMessage(hSysTray, TB_BUTTONCOUNT, 0, ByVal 0&)
For N = 0 To N - 1
SendMessage hSysTray, TB_GETBUTTON, N, ByVal lptbb
SendMessage hSysTray, TB_GETBUTTONTEXTA, N, ByVal L
ReadProcessMemory hProcess, ByVal L, ByVal S, Len(S)
Debug.Print Split(S, vbNullChar)(0)
ReadProcessMemory hProcess, ByVal lptbb, tbb, Len(tbb)
ReadProcessMemory hProcess, ByVal tbb.dwData, td, Len(td)
If (td.Reserved1 And 1) = 0 Then
DrawIconEx hdc, 2, Y, td.hIcon, 16, 16, 0, 0, 3
CurrentX = 20 * Screen.TwipsPerPixelX
CurrentY = (Y + 2) * Screen.TwipsPerPixelY
Print GetProcessNameFromHwnd(td.hWnd)
Y = Y + 18
End If
Next
VirtualFreeEx 0, lptbb, 0, MEM_RELEASE
VirtualFreeEx 0, lptd, 0, MEM_RELEASE
CloseHandle hProcess
End Sub

Private Function GetNotificationWindow() As Long
Dim H As Long
H = FindWindowEx(0, 0, "Shell_TrayWnd", vbNullString)
H = FindWindowEx(H, 0, "TrayNotifyWnd", vbNullString)
H = FindWindowEx(H, 0, "SysPager", vbNullString)
GetNotificationWindow = FindWindowEx(H, 0, "ToolbarWindow32", vbNullString)
End Function

Private Function GetProcessNameFromHwnd(ByVal hWnd As Long) As String
Dim ProcessId As Long, hProcess As Long, hModule As Long, S As String * MAX_PATH
GetWindowThreadProcessId hWnd, ProcessId
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0, ProcessId)
EnumProcessModules hProcess, hModule, 1, ByVal 0
GetModuleFileNameEx hProcess, hModule, S, MAX_PATH
GetProcessNameFromHwnd = Left$(S, InStr(S, vbNullChar) - 1)
CloseHandle hProcess
End Function[/tt]
___

I tested it on Windows XP and it worked fine.

Note that the above code is in very crude form and may have errors. The reliability is also doubtful because it uses some undocumented features in Windows and is based on some assumptions which may go wrong in some cases.

It is not recommended for production purposes in present form.
 
Hypetia
I must have just put the wrong words into Google. Or I found the same thing you have and simply didn't understand!!

Even if I had found it I would have had no chance in putting this together. As I said in my original post, I have a litle VB but predominantly VBA experience. Luckily I have enough knowledge to iron out the only bug that came up when I ran this (Split function in VB5?)

Anyway this is an extremely wordy way of saying THANKS, this seems to do what I was after! I just wish I understood it!!

Thanks!!

;-)
If a man says something and there are no women there to hear him, is he still wrong? [ponder]
How do I get the best answers?
 
Just as a point of interest, I believe that the 'undocumented' data structure pointed to by dwData is actually a NOTIFYICON structure without the cbsize element
 
Loomah,
>I must have just put the wrong words into Google.
I searched for enumerate system tray icons.

>Split function in VB5
You really don't need that piece of code which tries to retrieve the "tooltips" associated with the icons.

I thought I could use TB_GETBUTTONTEXT message to retrieve them. Athough it worked, the resutls were odd. The tooltips did not seem to match the icons being queried. In fact, the order in which the tooltips were enumerated was reversed for some obscure reason. First tooltip was apparently associated with last icon in the control, and vice versa.

If you don't need tooltips (and just want to enumerate processes), remove the following lines from code.
[tt]
SendMessage hSysTray, TB_GETBUTTONTEXTA, N, ByVal L
ReadProcessMemory hProcess, ByVal L, ByVal S, Len(S)
Debug.Print Split(S, vbNullChar)(0)
[/tt]
>I just wish I understood it!!
See the article I mentioned above. It contains good information about the working of the code. See also the CProcessData class which is used to query information using SendMessage function by allocating memory in foreign processes.

strongm,
I also thought same about the TRAYDATA structure pointed by dwData. Although many members have one-to-one correspondance in TRAYDATA and NOTIFYICONDATA, they are still not exactly same. The order in which members are placed is different in two structures.
 
I have other references from that which you linked which, beyond the hWnd entry, differ in the order and meaning of the entries in the structure. They more closely match the NOTIFYICONDATA order. I currently don't have the time to dig into this myself, so can't comment on who is more correct ...
 
After further investigation, I found how to retrieve the correct tooltip associated with each icon which was not working correctly in my first post.

The reason for odd results was my misinterpretation of wParam parameter for TB_GETBUTTON and TB_GETBUTTONTEXT messages.

For TB_GETBUTTON, wParam is defined as follows.
wParam = iButton; Zero-based index of the button for which to retrieve information.

For TB_GETBUTTONTEXT, wParam is defined as follows.
wParam = idButton; Command identifier of the button whose text is to be retrieved.

The wrong tooltip returned by the TB_GETBUTTONTEXT was due to this difference. In Windows XP, icons in systray are added from right-to-left direction. First icon (which is the rightmost toolbar button) is assigned the command id 0 and this number is incremented by 1 for each icon that follows on the left.

The code in my post enumerates the system tray from left-to-right, with most recent icon first and oldest icon in the last. The leftmost icon has the index 0 but has the largest command id. The rightmost icon has command id zero but largest button index. Due to this reason the TB_GETBUTTONTEXT was returning tooltips in reverse direction.

To fix this problem, we need to send TB_GETBUTTONTEXT message using button's command identifier -- not the button index. Fortunately, the command id of the button is retrieved by TB_GETBUTTON message in idCommand member of TBBUTTON structure.

To put it all togther, just modify the For-Next loop in the above code as below.
___
[tt]
For N = 0 To N - 1
SendMessage hSysTray, TB_GETBUTTON, N, ByVal lptbb
ReadProcessMemory hProcess, ByVal lptbb, tbb, Len(tbb)
ReadProcessMemory hProcess, ByVal tbb.dwData, td, Len(td)

If (td.Reserved1 And 1) = 0 Then
SendMessage hSysTray, TB_GETBUTTONTEXTA, tbb.idCommand, ByVal L
ReadProcessMemory hProcess, ByVal L, ByVal S, Len(S)
Debug.Print Left$(S, InStr(S, vbNullChar) - 1); vbNewLine; "----------"
DrawIconEx hdc, 2, Y, td.hIcon, 16, 16, 0, 0, 3
CurrentX = 20 * Screen.TwipsPerPixelX
CurrentY = (Y + 2) * Screen.TwipsPerPixelY
Print GetProcessNameFromHwnd(td.hWnd)
Y = Y + 18
End If
Next[/tt]
___

This time, the tooltips retrieved in the Immediate window were all correct.
I hope this information regarding tooltips will be further useful in this context.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top