Code: Select all
EnableExplicit
; TaskBar Icons enumerator
; By Luis
; PB 4.61 x86/x64
; Based on work from:
;
; Nish Sivakumar
; http://www.codeproject.com/Articles/10807/Shell-Tray-Info-Arrange-your-system-tray-icons
;
; Netmaestro
; http://www.purebasic.fr/english/viewtopic.php?p=302528#p302528
;
; and various people from http://social.msdn.microsoft.com/Forums/en/vbgeneral/thread/70d29077-84e6-43cd-b1b2-d84328276ae6
;
; Tested on Windows XP x86, Windows 7 x86, Windows 7 x64
; It's hackish, unsupported, dirty, etc., expect to see weird things.
; Probably it's more for educational purpose than real usage.
; DeviceNameToDosName() and NormalizeDevicePath() can be useful though.
; Known limitations:
; The bitmap of the icon is not always present, some programs update their icons dinamically in different ways (sendmessage, etc) and sometime the bitmap results blank.
; I'm not 100% sure why.
; The program must be compiled to 32 bit for 32 bit oses and to 64 bit for 64 bit oses.
; The 32 bit version cannot work on a 64 bit os (the 32 bit process cannot read the infos from the 64 bit shell).
; If you find problems and you are able to fix it, thanks.
; If you know a cleaner and less hackish way and want to share it, thanks.
#ICON_POSITION_TASKBAR = 0 ; icons in the notification area of the taskbar
#ICON_POSITION_OVERFLOW = 1 ; icons in the overflow window of Windows 7
; set to 1 to see the kernel-ish device name used in the path
; set to 0 to see the usual C:\ dos name instead (hopefully)
#SHOW_DEVICE_PATH = 0
Structure T_TASKBAR_PROGRAMS
Position.i ; #ICON_POSITION_TASKBAR or #ICON_POSITION_OVERFLOW
IconHandle.i ; the bitmap handle
WinHandle.i ; the handle of icon's window
PID.i ; the pid of the process
Image$ ; the image name of the process
Path$ ; path name of the executable
EndStructure
Structure NOTIFYICONDATA_EX
hWnd.i
uID.l
uFlags.l
uCallbackMessage.l
pad1.b[4]
hIcon.l
EndStructure
Procedure.s DeviceNameToDosName (Device$)
; [DESC]
; Return the DOS name for the specified kernel device mapping.
;
; [INPUT]
; Device$ : The kernel name (like "\Device\HarddiskVolume4", "\Device\Harddisk0\Partition1", etc.)
;
; [RETURN]
; The DOS logical name (A: - Z:)
;
; [NOTES]
; Windows XP
; Ascii/Unicode
; Debug DeviceNameToDosName("\Device\HarddiskVolume1") ; "C:"
Protected Buffer$ = Space(128 + 1)
Protected iNumChars, iCounter, CurrChar.c
Protected Dos$, Dev$
iNumChars = GetLogicalDriveStrings_(128, @Buffer$)
If iNumChars <= 128 And iNumChars > 0
Repeat
CurrChar = PeekC(@Buffer$ + iCounter * SizeOf(Character))
If CurrChar <> 0
Dos$ + Chr(CurrChar)
Else
If Right(Dos$, 1) = "\"
Dos$ = Left(Dos$, Len(Dos$) - 1)
EndIf
Dev$ = Space(#MAX_PATH + 1)
QueryDosDevice_(@Dos$, @Dev$, #MAX_PATH)
If Device$ = Dev$
ProcedureReturn Dos$
EndIf
Dos$ = ""
EndIf
iCounter + 1
Until iCounter >= iNumChars
EndIf
ProcedureReturn ""
EndProcedure
Procedure.s NormalizeDevicePath (DevicePath$)
; [DESC]
; Return the Windows path using drive letters from the specified kernel device path.
;
; [INPUT]
; DevicePath$ : The kernel pathname (like "\Device\HarddiskVolume2\foo\bar\prog.exe")
;
; [RETURN]
; The Windows path using drive letters.
;
; [NOTES]
; Windows XP
; Debug NormalizeDevicePath("\Device\HarddiskVolume2\foo\bar\prog.exe") ; "C:\foo\bar\prog.exe"
Protected iPos
Protected Dev$, Dos$
iPos = FindString(DevicePath$, "\Device\", 1)
If iPos
iPos = FindString (DevicePath$, "\", Len("\Device\") + 1)
If iPos
Dev$ = Left(DevicePath$, iPos - 1)
Dos$ = DeviceNameToDosName(Dev$)
DevicePath$ = ReplaceString(DevicePath$, Dev$, Dos$)
EndIf
EndIf
ProcedureReturn DevicePath$
EndProcedure
Procedure.s GetDeviceFileNameFromPID (iPID)
; [DESC]
; Return the path + filename (in device format) of the executable associated with the process id.
;
; [INPUT]
; The process id of the executable.
;
; [RETURN]
; The path + filename of the process.
;
; [NOTES]
; Returns something like "\Device\HarddiskVolume2\Windows\System32\taskmgr.exe"
Protected hDLL = OpenLibrary(#PB_Any, "psapi.dll")
Protected hProc, *fp
Protected fname$ = Space(#MAX_PATH + 1)
If hDLL
CompilerIf (#PB_Compiler_Unicode = 1)
*fp = GetFunction(hDLL, "GetProcessImageFileNameW")
CompilerElse
*fp = GetFunction(hDLL, "GetProcessImageFileNameA")
CompilerEndIf
If *fp
hProc = OpenProcess_(#PROCESS_QUERY_INFORMATION, #False, iPID)
If hProc
CallFunctionFast(*fp, hProc, @fname$, #MAX_PATH)
CloseHandle_(hProc)
EndIf
EndIf
CloseLibrary(hDLL)
EndIf
ProcedureReturn Trim(fname$)
EndProcedure
Procedure.s GetFileNameFromPID (iPID)
; [DESC]
; Return the path + filename of the executable associated with the process id.
;
; [INPUT]
; The process id of the executable.
;
; [RETURN]
; The path + filename of the process.
;
; [NOTES]
; Returns something like "C:\Windows\System32\taskmgr.exe"
ProcedureReturn NormalizeDevicePath(GetDeviceFileNameFromPID(iPID))
EndProcedure
Procedure.s GetImageNameFromPid (iPID)
; [DESC]
; Return only the filename of the executable associated with the process id.
;
; [INPUT]
; The process id of the executable.
;
; [RETURN]
; The filename of the process.
;
; [NOTES]
; Returns something like "taskmgr.exe"
; Replaced GetModuleBaseName(), see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683196%28v=vs.85%29.aspx
Protected iPos
Protected image$, path$ = GetDeviceFileNameFromPID(iPID)
If path$
path$ = ReverseString(Path$)
iPos = FindString(path$, "\", 1)
If iPos
image$ = ReverseString(Left(path$, iPos - 1))
EndIf
EndIf
ProcedureReturn image$
EndProcedure
Procedure.i FindTaskbarToolbarWindow()
Protected hWnd, hDLL = OpenLibrary(#PB_Any, "psapi.dll")
If hDLL
hWnd = FindWindow_("Shell_TrayWnd", #Null)
If hWnd
hWnd = FindWindowEx_(hWnd, #Null, "TrayNotifyWnd", #Null)
If hWnd
hWnd = FindWindowEx_(hWnd, #Null, "SysPager", #Null)
If hWnd
hWnd = FindWindowEx_(hWnd, #Null, "ToolbarWindow32", #Null)
EndIf
EndIf
EndIf
CloseLibrary(hDLL)
EndIf
ProcedureReturn hWnd
EndProcedure
Procedure.i FindTaskbarToolbarWindowOverflow()
Protected hWnd, hDLL = OpenLibrary(#PB_Any, "psapi.dll")
If hDLL
hWnd = FindWindow_("NotifyIconOverflowWindow", #Null)
If hWnd
hWnd = FindWindowEx_(hWnd, #Null, "ToolbarWindow32", #Null)
EndIf
CloseLibrary(hDLL)
EndIf
ProcedureReturn hWnd
EndProcedure
Procedure.i TaskBarCountIcons (hToolBarWindow)
ProcedureReturn SendMessage_(hToolBarWindow, #TB_BUTTONCOUNT, 0, 0)
EndProcedure
Procedure GetIconInfo (hProc, i, hToolBarWindow, *TBP.T_TASKBAR_PROGRAMS, iPosition)
Protected TBB.TBBUTTON
Protected NID.NOTIFYICONDATA_EX
Protected INFO.ICONINFO
Protected iBytesCount, *pdata
Protected fname$, iPID, image$
*pdata = VirtualAllocEx_(hProc, #Null, SizeOf(TBBUTTON), #MEM_COMMIT, #PAGE_READWRITE)
SendMessage_(hToolBarWindow, #TB_GETBUTTON, i, *pData)
ReadProcessMemory_(hProc, *pData, @TBB, SizeOf(TBBUTTON), @iBytesCount)
ReadProcessMemory_(hProc, TBB\dwData, @NID, SizeOf(NOTIFYICONDATA_EX), @iBytesCount)
GetWindowThreadProcessId_(NID\hWnd, @iPID)
CompilerIf #SHOW_DEVICE_PATH = 1
fname$ = GetDeviceFileNameFromPID(iPID)
CompilerElse
fname$ = GetFileNameFromPID(iPID)
CompilerEndIf
image$ = GetImageNameFromPid(iPID)
If GetIconInfo_(NID\hIcon, @INFO)
*TBP\IconHandle = NID\hIcon
Else
*TBP\IconHandle =0
EndIf
*TBP\WinHandle = NID\hWnd
*TBP\Position = iPosition
*TBP\Image$ = image$
*TBP\Path$ = fname$
*TBP\PID = iPID
VirtualFreeEx_(hProc, *pData, #Null, #MEM_RELEASE)
EndProcedure
Procedure GetIconizedPrograms (hToolBarWindow, iCount, List lstTBP.T_TASKBAR_PROGRAMS(), iPosition)
Protected iPID, hProc, i
Protected TBP.T_TASKBAR_PROGRAMS
GetWindowThreadProcessId_(hToolBarWindow, @iPID)
hProc = OpenProcess_(#PROCESS_VM_OPERATION | #PROCESS_VM_READ, #False, iPID)
For i = 0 To iCount - 1
AddElement(lstTBP())
GetIconInfo(hProc, i, hToolBarWindow, @lstTBP(), iPosition)
Next
CloseHandle_(hProc)
EndProcedure
Procedure.i GetTaskBarPrograms (List lstIcons.T_TASKBAR_PROGRAMS())
Protected hTaskBarToolBarWin = FindTaskbarToolbarWindow()
Protected hTaskBarToolBarOverflowWin = FindTaskbarToolbarWindowOverflow()
ClearList(lstIcons())
If hTaskBarToolBarWin
Protected iIconsCount = TaskBarCountIcons(hTaskBarToolBarWin)
If iIconsCount
GetIconizedPrograms(hTaskBarToolBarWin, iIconsCount, lstIcons(), #ICON_POSITION_TASKBAR)
EndIf
If hTaskBarToolBarOverflowWin
Protected iIconsOverflowCount = TaskBarCountIcons(hTaskBarToolBarOverflowWin)
If iIconsOverflowCount
GetIconizedPrograms(hTaskBarToolBarOverflowWin, iIconsOverflowCount, lstIcons(), #ICON_POSITION_OVERFLOW)
EndIf
EndIf
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
; *****************
; * USAGE EXAMPLE *
; *****************
Procedure Main()
Protected iEvent
Protected Row$
Protected NewList lstIcons.T_TASKBAR_PROGRAMS()
GetTaskBarPrograms(lstIcons())
If OpenWindow(0, 10, 10, 800, 600, "Taskbar Notification Area Icons", #PB_Window_SystemMenu)
ListIconGadget(0,0,0,800,600,"Icon", 100, #PB_ListIcon_GridLines)
AddGadgetColumn(0, 1, "Win Handle", 80)
AddGadgetColumn(0, 2, "OVF ?", 45)
AddGadgetColumn(0, 3, "PID", 50)
AddGadgetColumn(0, 4, "Image name", 150)
AddGadgetColumn(0, 5, "Path", 400)
ForEach lstIcons()
Row$ = " (" + Str(lstIcons()\IconHandle) + ")"
Row$ + Chr(10) + Str(lstIcons()\WinHandle)
If lstIcons()\Position = #ICON_POSITION_OVERFLOW
Row$ + Chr(10) + "Y"
Else
Row$ + Chr(10)
EndIf
Row$ + Chr(10) + Str(lstIcons()\PID)
Row$ + Chr(10) + lstIcons()\Image$
Row$ + Chr(10) + lstIcons()\Path$
If lstIcons()\IconHandle
AddGadgetItem(0,-1, Row$, lstIcons()\IconHandle)
Else
AddGadgetItem(0,-1, Row$)
EndIf
Next
Repeat
iEvent = WaitWindowEvent()
Select iEvent
Case #PB_Event_Gadget
Select EventGadget()
EndSelect
EndSelect
Until iEvent = #PB_Event_CloseWindow
EndIf
EndProcedure
Main()
EDIT: Replaced GetModuleBaseName() and made some small changes (comments, flags for openprocess, etc.)