Crash2100
asked on
Hide specific system tray icons
Is there any way I can use VB to hide a specific system tray icon? I found this API code that will hide the whole collection of tray icons, but I can't seem to figure out how to pull a single icon out of this so I can hide it.
Public Function HideTaskBarIcons()
Dim FindClass As Long, Handle As Long
FindClass& = FindWindow("Shell_TrayWnd" , "")
Handle& = FindWindowEx(FindClass&, 0, "TrayNotifyWnd", vbNullString)
ShowWindow Handle&, 0
End Function
I also found this code that I think I can modify to do what I want, but I haven't figured out how exactly to get the buttons to hide. I had to modify the API calls like this to get the list to work:
http://www.visualbasiccode.com/Asp/showsn.asp?theID=11610
hWndTray = FindWindow("Shell_TrayWnd" , vbNullString)
hWndTray2 = FindWindowEx(hWndTray, 0, "TrayNotifyWnd", vbNullString)
hWndTray3 = FindWindowEx(hWndTray2, 0, "SysPager", vbNullString)
hWndToolBar = FindWindowEx(hWndTray3, 0, "ToolbarWindow32", vbNullString)
I tried adding a For..Next loop to go through all the buttons, it finds the button I want to hide, but I haven't figured out how exactly to hide it. Either that, or the system tray needs to be refreshed or something after it's hidden.
Dim Ob As IAccessible
AccessibleObjectFromWindow ByVal hWndToolBar, OBJID_CLIENT, UID1, Ob
For iBtnIndex = 0 To iTrayButtonsCount
If Ob.accName(Av(iBtnIndex)) = "PeerGuardian" Then
SendMessage hWndToolBar&, TB_HIDEBUTTON, iBtnIndex, 0
SendMessage hWndToolBar&, TB_SAVERESTOREA, True, vbNull
End If
Next iBtnIndex
Public Function HideTaskBarIcons()
Dim FindClass As Long, Handle As Long
FindClass& = FindWindow("Shell_TrayWnd"
Handle& = FindWindowEx(FindClass&, 0, "TrayNotifyWnd", vbNullString)
ShowWindow Handle&, 0
End Function
I also found this code that I think I can modify to do what I want, but I haven't figured out how exactly to get the buttons to hide. I had to modify the API calls like this to get the list to work:
http://www.visualbasiccode.com/Asp/showsn.asp?theID=11610
hWndTray = FindWindow("Shell_TrayWnd"
hWndTray2 = FindWindowEx(hWndTray, 0, "TrayNotifyWnd", vbNullString)
hWndTray3 = FindWindowEx(hWndTray2, 0, "SysPager", vbNullString)
hWndToolBar = FindWindowEx(hWndTray3, 0, "ToolbarWindow32", vbNullString)
I tried adding a For..Next loop to go through all the buttons, it finds the button I want to hide, but I haven't figured out how exactly to hide it. Either that, or the system tray needs to be refreshed or something after it's hidden.
Dim Ob As IAccessible
AccessibleObjectFromWindow
For iBtnIndex = 0 To iTrayButtonsCount
If Ob.accName(Av(iBtnIndex)) = "PeerGuardian" Then
SendMessage hWndToolBar&, TB_HIDEBUTTON, iBtnIndex, 0
SendMessage hWndToolBar&, TB_SAVERESTOREA, True, vbNull
End If
Next iBtnIndex
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Yes, I forgot to add copyrights :)
All code is mine. This version (with icon extraction) works for Win ME/2000/XP/2003 OS only. You can find my previous version (works on all OS, but can not extract icons into ListView - it uses a trick with Tray tooltips) at http://www.freevbcode.com/ShowCode.Asp?ID=3291
All code is mine. This version (with icon extraction) works for Win ME/2000/XP/2003 OS only. You can find my previous version (works on all OS, but can not extract icons into ListView - it uses a trick with Tray tooltips) at http://www.freevbcode.com/ShowCode.Asp?ID=3291
ASKER
I managed to take the code you gave me and I came up with this function that shows and hides icons. I was wondering, is there any way I can do this more simply, without having to include all of those modules? Possibly with a SendMessage API call or something? If not, it's ok, I'm just curious.
Private bIsIconVisible As Boolean
Private Sub SetTrayIconVisible(strEXEN ame As String, bVisible As Boolean)
Dim ti As TRAY_INFO
Dim i As Long
Dim pic As StdPicture
Dim nIcon As Long
Dim idx As Long
Dim li As ListItem
idx = 1
ti = GetTrayInfo
For i = 0 To ti.nCount - 1
If LCase(ti.TII(i).sExecutabl e) Like "*" & strEXEName Then
Dim nid As NOTIFYICONDATA
nid.cbSize = Len(nid)
With ti.TII(i)
nid.hWnd = .hWnd
nid.uID = .uID
nid.uFlags = NIF_STATE
nid.dwState = 0
nid.dwStateMask = NIS_HIDDEN
End With
nid.dwState = IIf(bVisible = True, NIS_SHOWING, NIS_HIDDEN)
Shell_NotifyIcon NIM_MODIFY, nid
'MsgBox "Sysdoc is " & IIf(bVisible = True, "showing", "hidden")
End If
Next i
End Sub
Private bIsIconVisible As Boolean
Private Sub SetTrayIconVisible(strEXEN
Dim ti As TRAY_INFO
Dim i As Long
Dim pic As StdPicture
Dim nIcon As Long
Dim idx As Long
Dim li As ListItem
idx = 1
ti = GetTrayInfo
For i = 0 To ti.nCount - 1
If LCase(ti.TII(i).sExecutabl
Dim nid As NOTIFYICONDATA
nid.cbSize = Len(nid)
With ti.TII(i)
nid.hWnd = .hWnd
nid.uID = .uID
nid.uFlags = NIF_STATE
nid.dwState = 0
nid.dwStateMask = NIS_HIDDEN
End With
nid.dwState = IIf(bVisible = True, NIS_SHOWING, NIS_HIDDEN)
Shell_NotifyIcon NIM_MODIFY, nid
'MsgBox "Sysdoc is " & IIf(bVisible = True, "showing", "hidden")
End If
Next i
End Sub
Hi
Youn need
.hWnd, .uID and .sExecutable variables, so you can remove all other staff. Also, you can deal with NT OS only.
Youn need
.hWnd, .uID and .sExecutable variables, so you can remove all other staff. Also, you can deal with NT OS only.
ASKER
I don't care if it will only work with NT, I'm writing this for XP machines anyway.
What exactly do you mean remove everything but .hWnd, .uID and .sExecutable? Remove the things from the TRAY_ICON_INFO type?
Basically, I'm trying to figure out if there's another way to do this with just a few lines of code, rather than having all the code in those four modules included in the project. That's why I was wondering if you could do it with just a SendMessage API call or something. I did try removing the modules one by one, but the way the code in them is all tied together, and I kept getting errors when I was trying to figure out how to isolate the code so it wouldn't require what was in the other module.
What exactly do you mean remove everything but .hWnd, .uID and .sExecutable? Remove the things from the TRAY_ICON_INFO type?
Basically, I'm trying to figure out if there's another way to do this with just a few lines of code, rather than having all the code in those four modules included in the project. That's why I was wondering if you could do it with just a SendMessage API call or something. I did try removing the modules one by one, but the way the code in them is all tied together, and I kept getting errors when I was trying to figure out how to isolate the code so it wouldn't require what was in the other module.
Hi
Actually, all my code use SendMessage API. The problem with interprocess memory communication is in the 4th member of this API - lParam. When you don't use this member (TB_BUTTONCOUNT, for example) - everything is OK. But when you use it as a pounter to structure, which must be filled with this call (TB_GETBUTTONINFO) - there is a problem. The variable, holding this structure, declared in YOUR process and it's pointer belong to YOUR process memory space. Remote process (Shell in your case) have its own memory space, and same memory address may be empty or allocated with another variable/function code etc. So, SendMessage write TBBUTTON structure not in your process address, but in its own process. To use SendMessage in this case, you have to allocate enough memory in remote process, write initial structure there and pass this remote address to SendMessage. Then read structure back into your process and parse it parameters.
Pseudo code for your task for XP can be following:
1. Get Tray ToolBar hwnd:
hTrayWnd = FindWindow("Shell_TrayWnd" , vbNullString)
hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", vbNullString)
hTraySysPager = FindWindowEx(hTrayNotifyWn d, 0, "SysPager", vbNullString)
hTB = FindWindowEx(hTraySysPager , 0, "ToolbarWindow32", vbNullString)
and check if there is ANY icon:
nCount = SendMessage(hTB, TB_BUTTONCOUNT, 0, ByVal 0&) - exit from function if nCount = 0
2. Get Shell ProcessId:
tid = GetWindowThreadProcessId(h TB, pid)
3. Open this process with required rights:
hProcess = OpenProcess(PROCESS_VM_OPE RATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, False, pid)
4. Allocate memory for structures in remote process:
Private Type TBBUTTONINFO ''Toolbar button info
cbSize As Long
dwMask As Long
idCommand As Long
iImage As Long
fsState As Byte
fsStyle As Byte
cx As Integer
lParam As Long 'for TrayNotyfy it's a pointer to NOTIFYICONDATA structure
pszText As Long ' Not "As String" - Bug in previous declaration!
cchText As Long
End Type
Dim tbi As TBBUTTONINFO
tbiAddr = VirtualAllocEx(hProcess, 0, Len(tbi), MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
5. Initialize structure and copy it to remote address. In your case all you need from structure is LPARAM (you don't need icon,text etc.):
tbi.cbSize = Len(tbi)
tbi.dwMask = TBIF_LPARAM Or TBIF_BYINDEX
Call WriteProcessMemory(hProces s, ByVal tbiAddr, tbi, Len(tbi), lWritten)
6. In a For..Next loop for each button, call SendMessage, using REMOTE address and read info from remote address:
Call SendMessage(hTB, TB_GETBUTTONINFO, i, ByVal tbiAddr)
Call ReadProcessMemory(hProcess , ByVal tbiAddr, tbi, Len(tbi), lWritten)
7. As I found from memory investigation, tbi.lParam contain a pointer to NOTIFYICONDATA structure, so extract this structure from remote process:
Call ReadProcessMemory(hProcess , ByVal tbi.lParam, ii, Len(ii), lWritten)
8. Now ii(ICONINFO) structure contain all info you need. You can collect these info into array (like in my sample) or check for sExecutable just here:
sExecutable = TrimNULL(StrConv(ii.lpszIn fo, vbFromUnicode))
If LCase(ti.TII(i).sExecutabl e) Like "*" & strEXEName Then
' rest code
9. Free memory in remote process and close its handle:
VirtualFreeEx hProcess, ByVal tbiAddr, 0, MEM_RELEASE
CloseHandle hProcess
Actually, all my code use SendMessage API. The problem with interprocess memory communication is in the 4th member of this API - lParam. When you don't use this member (TB_BUTTONCOUNT, for example) - everything is OK. But when you use it as a pounter to structure, which must be filled with this call (TB_GETBUTTONINFO) - there is a problem. The variable, holding this structure, declared in YOUR process and it's pointer belong to YOUR process memory space. Remote process (Shell in your case) have its own memory space, and same memory address may be empty or allocated with another variable/function code etc. So, SendMessage write TBBUTTON structure not in your process address, but in its own process. To use SendMessage in this case, you have to allocate enough memory in remote process, write initial structure there and pass this remote address to SendMessage. Then read structure back into your process and parse it parameters.
Pseudo code for your task for XP can be following:
1. Get Tray ToolBar hwnd:
hTrayWnd = FindWindow("Shell_TrayWnd"
hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", vbNullString)
hTraySysPager = FindWindowEx(hTrayNotifyWn
hTB = FindWindowEx(hTraySysPager
and check if there is ANY icon:
nCount = SendMessage(hTB, TB_BUTTONCOUNT, 0, ByVal 0&) - exit from function if nCount = 0
2. Get Shell ProcessId:
tid = GetWindowThreadProcessId(h
3. Open this process with required rights:
hProcess = OpenProcess(PROCESS_VM_OPE
4. Allocate memory for structures in remote process:
Private Type TBBUTTONINFO ''Toolbar button info
cbSize As Long
dwMask As Long
idCommand As Long
iImage As Long
fsState As Byte
fsStyle As Byte
cx As Integer
lParam As Long 'for TrayNotyfy it's a pointer to NOTIFYICONDATA structure
pszText As Long ' Not "As String" - Bug in previous declaration!
cchText As Long
End Type
Dim tbi As TBBUTTONINFO
tbiAddr = VirtualAllocEx(hProcess, 0, Len(tbi), MEM_RESERVE Or MEM_COMMIT, PAGE_READWRITE)
5. Initialize structure and copy it to remote address. In your case all you need from structure is LPARAM (you don't need icon,text etc.):
tbi.cbSize = Len(tbi)
tbi.dwMask = TBIF_LPARAM Or TBIF_BYINDEX
Call WriteProcessMemory(hProces
6. In a For..Next loop for each button, call SendMessage, using REMOTE address and read info from remote address:
Call SendMessage(hTB, TB_GETBUTTONINFO, i, ByVal tbiAddr)
Call ReadProcessMemory(hProcess
7. As I found from memory investigation, tbi.lParam contain a pointer to NOTIFYICONDATA structure, so extract this structure from remote process:
Call ReadProcessMemory(hProcess
8. Now ii(ICONINFO) structure contain all info you need. You can collect these info into array (like in my sample) or check for sExecutable just here:
sExecutable = TrimNULL(StrConv(ii.lpszIn
If LCase(ti.TII(i).sExecutabl
' rest code
9. Free memory in remote process and close its handle:
VirtualFreeEx hProcess, ByVal tbiAddr, 0, MEM_RELEASE
CloseHandle hProcess
ASKER
Thanks for all the help!
ASKER