I am trying to create application that get the list of processes that have systray icon.
I searched alot and found number of references:
http://www.raymond.cc/blog/find-out-what-program-are-running-at-windows-system-tray/
Which Windows process is displaying a given taskbar system tray icon?
http://www.codeproject.com/Articles/10497/A-tool-to-order-the-window-buttons-in-your-taskbar
All of them are good resources but the most informative for me were 3 & 4.
In 1 we have an example for what I want.
I want the list of processes that have systray icon:
Example for application called "AnVir Task Manager"
Using the code from link 6 I succeed to iterate through the systray buttons and see the text of each button:
But I am not sure how I can find what process relates to each trayicon.
In code project he mentioned that the information that can help identify the process is the dwData
but the problem is that when I found button that appears in Systray, its dwData = 0
:
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SystrayIcons
{
public partial class Form1 : Form
{
public Form1()
{
Engine.findProcessInSystray();
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Engine.findProcessInSystray();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Common;
using System.Diagnostics;
using System.Collections;
namespace SystrayIcons
{
static class Engine
{
static public void findProcessInSystray()
{
IntPtr systemTrayHandle = GetSystemTrayHandle();
UInt32 count = User32.SendMessage(systemTrayHandle, TB.BUTTONCOUNT, 0, 0);
ArrayList tbButtons = new ArrayList();
List<TBBUTTON> tbButtons2 = new List<TBBUTTON>();
for (int i = 0; i < count; i++)
{
TBBUTTON tbButton = new TBBUTTON();
string text = String.Empty;
IntPtr ipWindowHandle = IntPtr.Zero;
bool b = GetTBButton(systemTrayHandle, i, ref tbButton, ref text, ref ipWindowHandle);
// if (tbButton.iBitmap != 0)
if(tbButton.dwData != 0)
{
tbButtons.Add(tbButton);
tbButtons2.Add(tbButton);
}
}
// CreateImageList();
System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcesses();
foreach (System.Diagnostics.Process process in processes)
{
if (process.MainWindowHandle == systemTrayHandle)
{
}
}
}
static IntPtr GetSystemTrayHandle()
{
IntPtr hWndTray = User32.FindWindow("Shell_TrayWnd", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = User32.FindWindowEx(hWndTray, IntPtr.Zero, "TrayNotifyWnd", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = User32.FindWindowEx(hWndTray, IntPtr.Zero, "SysPager", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = User32.FindWindowEx(hWndTray, IntPtr.Zero, "ToolbarWindow32", null);
return hWndTray;
}
}
}
return IntPtr.Zero;
}
public static unsafe bool GetTBButton(IntPtr hToolbar, int i, ref TBBUTTON tbButton, ref string text, ref IntPtr ipWindowHandle)
{
// One page
const int BUFFER_SIZE = 0x1000;
byte[] localBuffer = new byte[BUFFER_SIZE];
UInt32 processId = 0;
UInt32 threadId = User32.GetWindowThreadProcessId(hToolbar, out processId);
IntPtr hProcess = Kernel32.OpenProcess(ProcessRights.ALL_ACCESS, false, processId);
if (hProcess == IntPtr.Zero) { Debug.Assert(false); return false; }
IntPtr ipRemoteBuffer = Kernel32.VirtualAllocEx(
hProcess,
IntPtr.Zero,
new UIntPtr(BUFFER_SIZE),
MemAllocationType.COMMIT,
MemoryProtection.PAGE_READWRITE);
if (ipRemoteBuffer == IntPtr.Zero) { Debug.Assert(false); return false; }
// TBButton
fixed (TBBUTTON* pTBButton = &tbButton)
{
IntPtr ipTBButton = new IntPtr(pTBButton);
int b = (int)User32.SendMessage(hToolbar, TB.GETBUTTON, (IntPtr)i, ipRemoteBuffer);
if (b == 0) { Debug.Assert(false); return false; }
// this is fixed
Int32 dwBytesRead = 0;
IntPtr ipBytesRead = new IntPtr(&dwBytesRead);
bool b2 = Kernel32.ReadProcessMemory(
hProcess,
ipRemoteBuffer,
ipTBButton,
new UIntPtr((uint)sizeof(TBBUTTON)),
ipBytesRead);
if (!b2) { Debug.Assert(false); return false; }
}
// button text
fixed (byte* pLocalBuffer = localBuffer)
{
IntPtr ipLocalBuffer = new IntPtr(pLocalBuffer);
int chars = (int)User32.SendMessage(hToolbar, TB.GETBUTTONTEXTW, (IntPtr)tbButton.idCommand, ipRemoteBuffer);
if (chars == -1) { Debug.Assert(false); return false; }
// this is fixed
Int32 dwBytesRead = 0;
IntPtr ipBytesRead = new IntPtr(&dwBytesRead);
bool b4 = Kernel32.ReadProcessMemory(
hProcess,
ipRemoteBuffer,
ipLocalBuffer,
new UIntPtr(BUFFER_SIZE),
ipBytesRead);
if (!b4) { Debug.Assert(false); return false; }
text = Marshal.PtrToStringUni(ipLocalBuffer, chars);
if (text == " ") text = String.Empty;
}
// window handle
fixed (byte* pLocalBuffer = localBuffer)
{
IntPtr ipLocalBuffer = new IntPtr(pLocalBuffer);
// this is in the remote virtual memory space
IntPtr ipRemoteData = new IntPtr(tbButton.dwData);
// this is fixed
Int32 dwBytesRead = 0;
IntPtr ipBytesRead = new IntPtr(&dwBytesRead);
bool b4 = Kernel32.ReadProcessMemory(
hProcess,
ipRemoteData,
ipLocalBuffer,
new UIntPtr(4),
ipBytesRead);
if (!b4) { Debug.Assert(false); return false; }
if (dwBytesRead != 4) { Debug.Assert(false); return false; }
Int32 iWindowHandle = BitConverter.ToInt32(localBuffer, 0);
if (iWindowHandle == -1) { Debug.Assert(false); }//return false; }
ipWindowHandle = new IntPtr(iWindowHandle);
}
Kernel32.VirtualFreeEx(
hProcess,
ipRemoteBuffer,
UIntPtr.Zero,
MemAllocationType.RELEASE);
Kernel32.CloseHandle(hProcess);
return true;
}
}
}
using System;
using System.Runtime.InteropServices;
namespace Common
{
//-----------------------------------------------------------------------------
// Structures
[StructLayout(LayoutKind.Sequential)]
internal struct SYSTEM_INFO
{
public _PROCESSOR_INFO_UNION uProcessorInfo;
public uint dwPageSize;
public uint lpMinimumApplicationAddress;
public uint lpMaximumApplicationAddress;
public uint dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public uint dwProcessorLevel;
public uint dwProcessorRevision;
}
[StructLayout(LayoutKind.Explicit)]
internal struct _PROCESSOR_INFO_UNION
{
[FieldOffset(0)]
public uint dwOemId;
[FieldOffset(0)]
public ushort wProcessorArchitecture;
[FieldOffset(2)]
public ushort wReserved;
}
[ StructLayout( LayoutKind.Sequential )]
internal struct BY_HANDLE_FILE_INFORMATION
{
public UInt32 dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public UInt32 dwVolumeSerialNumber;
public UInt32 nFileSizeHigh;
public UInt32 nFileSizeLow;
public UInt32 nNumberOfLinks;
public UInt32 nFileIndexHigh;
public UInt32 nFileIndexLow;
}
[ StructLayout( LayoutKind.Sequential )]
internal class MEMORYSTATUSEX
{
public Int32 Length;
public Int32 MemoryLoad;
public UInt64 TotalPhysical;
public UInt64 AvailablePhysical;
public UInt64 TotalPageFile;
public UInt64 AvailablePageFile;
public UInt64 TotalVirtual;
public UInt64 AvailableVirtual;
public UInt64 AvailableExtendedVirtual;
public MEMORYSTATUSEX() { Length = Marshal.SizeOf( this ); }
private void StopTheCompilerComplaining()
{
Length = 0;
MemoryLoad = 0;
TotalPhysical = 0;
AvailablePhysical = 0;
TotalPageFile = 0;
AvailablePageFile = 0;
TotalVirtual = 0;
AvailableVirtual = 0;
AvailableExtendedVirtual = 0;
}
}
//-----------------------------------------------------------------------------
// Constants
internal class ProcessRights
{
public const UInt32 TERMINATE = 0x0001 ;
public const UInt32 CREATE_THREAD = 0x0002 ;
public const UInt32 SET_SESSIONID = 0x0004 ;
public const UInt32 VM_OPERATION = 0x0008 ;
public const UInt32 VM_READ = 0x0010 ;
public const UInt32 VM_WRITE = 0x0020 ;
public const UInt32 DUP_HANDLE = 0x0040 ;
public const UInt32 CREATE_PROCESS = 0x0080 ;
public const UInt32 SET_QUOTA = 0x0100 ;
public const UInt32 SET_INFORMATION = 0x0200 ;
public const UInt32 QUERY_INFORMATION = 0x0400 ;
public const UInt32 SUSPEND_RESUME = 0x0800 ;
private const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
private const UInt32 SYNCHRONIZE = 0x00100000;
public const UInt32 ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF;
}
internal class MemoryProtection
{
public const UInt32 PAGE_NOACCESS = 0x01 ;
public const UInt32 PAGE_READONLY = 0x02 ;
public const UInt32 PAGE_READWRITE = 0x04 ;
public const UInt32 PAGE_WRITECOPY = 0x08 ;
public const UInt32 PAGE_EXECUTE = 0x10 ;
public const UInt32 PAGE_EXECUTE_READ = 0x20 ;
public const UInt32 PAGE_EXECUTE_READWRITE = 0x40 ;
public const UInt32 PAGE_EXECUTE_WRITECOPY = 0x80 ;
public const UInt32 PAGE_GUARD = 0x100 ;
public const UInt32 PAGE_NOCACHE = 0x200 ;
public const UInt32 PAGE_WRITECOMBINE = 0x400 ;
}
internal class MemAllocationType
{
public const UInt32 COMMIT = 0x1000 ;
public const UInt32 RESERVE = 0x2000 ;
public const UInt32 DECOMMIT = 0x4000 ;
public const UInt32 RELEASE = 0x8000 ;
public const UInt32 FREE = 0x10000 ;
public const UInt32 PRIVATE = 0x20000 ;
public const UInt32 MAPPED = 0x40000 ;
public const UInt32 RESET = 0x80000 ;
public const UInt32 TOP_DOWN = 0x100000 ;
public const UInt32 WRITE_WATCH = 0x200000 ;
public const UInt32 PHYSICAL = 0x400000 ;
public const UInt32 LARGE_PAGES = 0x20000000 ;
public const UInt32 FOURMB_PAGES = 0x80000000 ;
}
[Flags]
public enum EFileAccess : uint
{
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
}
[Flags]
public enum EFileShare : uint
{
None = 0x00000000,
Read = 0x00000001,
Write = 0x00000002,
Delete = 0x00000004,
}
public enum ECreationDisposition : uint
{
New = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5,
}
[Flags]
public enum EFileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline= 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
//-----------------------------------------------------------------------------
// Functions
internal class Kernel32
{
[DllImport("kernel32.dll")]
public static extern void GetSystemInfo(
out SYSTEM_INFO lpSystemInfo );
[ DllImport( "Kernel32.dll" ) ]
public static extern bool GetFileInformationByHandle
(
IntPtr hFile,
out BY_HANDLE_FILE_INFORMATION lpFileInformation
);
[ DllImport( "kernel32.dll", SetLastError = true ) ]
public static extern IntPtr CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile );
[ DllImport( "Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode ) ]
public static extern bool CreateHardLink
(
string FileName,
string ExistingFileName,
IntPtr lpSecurityAttributes
);
[ DllImport( "Kernel32.dll" ) ]
public static extern bool Beep
(
UInt32 frequency,
UInt32 duration
);
[ DllImport( "Kernel32.dll", SetLastError = true ) ]
public static extern IntPtr OpenProcess(
uint dwDesiredAccess,
bool bInheritHandle,
uint dwProcessId );
[DllImport( "kernel32.dll", SetLastError = true ) ]
public static extern IntPtr VirtualAllocEx(
IntPtr hProcess,
IntPtr lpAddress,
UIntPtr dwSize,
uint flAllocationType,
uint flProtect);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
IntPtr lpBuffer,
UIntPtr nSize,
IntPtr lpNumberOfBytesRead );
[DllImport("kernel32.dll")]
public static extern bool VirtualFreeEx(
IntPtr hProcess,
IntPtr lpAddress,
UIntPtr dwSize,
UInt32 dwFreeType );
[DllImport("kernel32.dll")]
public static extern bool GlobalMemoryStatusEx(
MEMORYSTATUSEX buffer );
[ DllImport( "kernel32.dll", SetLastError = true ) ]
public static extern bool CloseHandle(
IntPtr hObject );
}
//-----------------------------------------------------------------------------
}
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Common
{
internal enum GW : uint
{
HWNDFIRST = 0,
HWNDLAST = 1,
HWNDNEXT = 2,
HWNDPREV = 3,
OWNER = 4,
CHILD = 5,
MAX = 6
}
internal class ICON
{
public const UInt32 SMALL = 0;
public const UInt32 BIG = 1;
public const UInt32 SMALL2 = 2; // XP+
}
internal enum MB : uint
{
SimpleBeep = 0xFFFFFFFF,
IconAsterisk = 0x00000040,
IconWarning = 0x00000030,
IconError = 0x00000010,
IconQuestion = 0x00000020,
OK = 0x00000000
}
internal class SW
{
public const int HIDE = 0;
public const int SHOWNORMAL = 1;
public const int NORMAL = 1;
public const int SHOWMINIMIZED = 2;
public const int SHOWMAXIMIZED = 3;
public const int MAXIMIZE = 3;
public const int SHOWNOACTIVATE = 4;
public const int SHOW = 5;
public const int MINIMIZE = 6;
public const int SHOWMINNOACTIVE = 7;
public const int SHOWNA = 8;
public const int RESTORE = 9;
public const int SHOWDEFAULT = 10;
public const int FORCEMINIMIZE = 11;
public const int MAX = 11;
}
internal class TB
{
public const uint GETBUTTON = WM.USER + 23 ;
public const uint BUTTONCOUNT = WM.USER + 24 ;
public const uint CUSTOMIZE = WM.USER + 27 ;
public const uint GETBUTTONTEXTA = WM.USER + 45 ;
public const uint GETBUTTONTEXTW = WM.USER + 75 ;
}
internal class TBSTATE
{
public const uint CHECKED = 0x01 ;
public const uint PRESSED = 0x02 ;
public const uint ENABLED = 0x04 ;
public const uint HIDDEN = 0x08 ;
public const uint INDETERMINATE = 0x10 ;
public const uint WRAP = 0x20 ;
public const uint ELLIPSES = 0x40 ;
public const uint MARKED = 0x80 ;
}
internal class WM
{
public const uint CLOSE = 0x0010;
public const uint GETICON = 0x007F;
public const uint KEYDOWN = 0x0100;
public const uint COMMAND = 0x0111;
public const uint USER = 0x0400; // 0x0400 - 0x7FFF
public const uint APP = 0x8000; // 0x8000 - 0xBFFF
}
internal class GCL
{
public const int MENUNAME = - 8;
public const int HBRBACKGROUND = -10;
public const int HCURSOR = -12;
public const int HICON = -14;
public const int HMODULE = -16;
public const int CBWNDEXTRA = -18;
public const int CBCLSEXTRA = -20;
public const int WNDPROC = -24;
public const int STYLE = -26;
public const int ATOM = -32;
public const int HICONSM = -34;
// GetClassLongPtr ( 64-bit )
private const int GCW_ATOM = -32;
private const int GCL_CBCLSEXTRA = -20;
private const int GCL_CBWNDEXTRA = -18;
private const int GCLP_MENUNAME = - 8;
private const int GCLP_HBRBACKGROUND = -10;
private const int GCLP_HCURSOR = -12;
private const int GCLP_HICON = -14;
private const int GCLP_HMODULE = -16;
private const int GCLP_WNDPROC = -24;
private const int GCLP_HICONSM = -34;
private const int GCL_STYLE = -26;
}
[ StructLayout( LayoutKind.Sequential ) ]
internal struct TBBUTTON
{
public Int32 iBitmap;
public Int32 idCommand;
public byte fsState;
public byte fsStyle;
// [ MarshalAs( UnmanagedType.ByValArray, SizeConst=2 ) ]
// public byte[] bReserved;
public byte bReserved1;
public byte bReserved2;
public UInt32 dwData;
public IntPtr iString;
};
internal class User32
{
private User32() {}
// public const UInt32 WM_USER = 0x0400;
// public const UInt32 WM_KEYDOWN = 0x0100;
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(
IntPtr hWnd,
UInt32 msg,
IntPtr wParam,
IntPtr lParam );
[DllImport("user32.dll")]
public static extern UInt32 SendMessage(
IntPtr hWnd,
UInt32 msg,
UInt32 wParam,
UInt32 lParam );
[ DllImport( "User32.dll" ) ]
public static extern bool PostMessage
(
IntPtr hWnd,
UInt32 Msg,
IntPtr wParam,
IntPtr lParam
);
[ DllImport( "User32.dll" ) ]
public static extern bool PostMessage
(
IntPtr hWnd,
UInt32 Msg,
UInt32 wParam,
UInt32 lParam
);
[ DllImport( "User32.dll" ) ]
public static extern bool MessageBeep
(
MB BeepType
);
[DllImport("user32.dll")]
public static extern bool ShowWindow
(
IntPtr hWnd,
int nCmdShow
);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow
(
IntPtr hWnd
);
[ DllImport( "User32.dll" ) ]
public static extern IntPtr GetDesktopWindow
(
);
[ DllImport( "user32.dll", CharSet = CharSet.Unicode ) ]
public static extern IntPtr FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow);
[ DllImport( "User32.dll" ) ]
public static extern IntPtr GetWindow
(
IntPtr hWnd,
GW uCmd
);
[ DllImport( "User32.dll" ) ]
public static extern Int32 GetWindowTextLength
(
IntPtr hWnd
);
[ DllImport( "User32.dll", SetLastError = true, CharSet = CharSet.Auto ) ]
public static extern Int32 GetWindowText
(
IntPtr hWnd,
out StringBuilder lpString,
Int32 nMaxCount
);
[ DllImport( "User32.dll", CharSet = CharSet.Auto ) ]
public static extern Int32 GetClassName
(
IntPtr hWnd,
out StringBuilder lpClassName,
Int32 nMaxCount
);
// [ DllImport( "user32.dll", EntryPoint = "GetClassLongPtrW" ) ]
[ DllImport( "user32.dll" ) ]
public static extern UInt32 GetClassLong
(
IntPtr hWnd,
int nIndex
);
[DllImport("user32.dll")]
public static extern uint SetClassLong
(
IntPtr hWnd,
int nIndex,
uint dwNewLong
);
[ DllImport( "User32.dll", CharSet=CharSet.Auto ) ]
public static extern UInt32 GetWindowThreadProcessId
(
IntPtr hWnd,
// [ MarshalAs( UnmanagedType.
out UInt32 lpdwProcessId
);
// Systray icons
//[DllImport("user32.dll", SetLastError = true)]
// public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
}
I checked the application on 32bit and I saw that the dwData != 0
.
This helped me to understand that the problem is when working on 64bit.
I replaced public UInt32 dwData;
with public UInt64 dwData;
.
[ StructLayout( LayoutKind.Sequential ) ]
internal struct TBBUTTON
{
public Int32 iBitmap;
public Int32 idCommand;
public byte fsState;
public byte fsStyle;
// [ MarshalAs( UnmanagedType.ByValArray, SizeConst=2 ) ]
// public byte[] bReserved;
public byte bReserved1;
public byte bReserved2;
// public UInt32 dwData;
public UInt64 dwData;
public IntPtr iString;
};
The dwData
is now larger than zero.
I succeed to get the windows handle of the button associated to the process and get the process pid:
// window handle
fixed (byte* pLocalBuffer = localBuffer)
{
...
ipWindowHandle = new IntPtr(iWindowHandle);
threadId = User32.GetWindowThreadProcessId(ipWindowHandle, out processId);
data.setProcessPid(processId);
}
This solution doesn't find processes that are associated with hidden system tray icons, this is a new problem that I will need to explore :) .
New refernces that helped me to find the idea to this solution:
http://www.codeproject.com/Articles/10807/Shell-Tray-Info-Arrange-your-system-tray-icons
Which was the comment of someone named "mklencke" that gave code for 64bit:
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
BOOL IsWow64()
{
static bool isset = false;
static BOOL bIsWow64 = FALSE;
if (isset) {
return bIsWow64;
}
//IsWow64Process is not available on all supported versions of Windows.
//Use GetModuleHandle to get a handle to the DLL that contains the function
//and GetProcAddress to get a pointer to the function if available.
LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
if(NULL != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
{
//TODO handle error?
return FALSE;
}
}
isset = true;
return bIsWow64;
}
typedef struct _TBBUTTON64 {
int iBitmap;
int idCommand;
BYTE fsState;
BYTE fsStyle;
BYTE bReserved[6];
DWORD64 dwData;
DWORD64 iString;
} TBBUTTON64, NEAR* PTBBUTTON64, *LPTBBUTTON64;
typedef const TBBUTTON64 *LPCTBBUTTON64;
bool EnumSystemTray() {
bool bFound = false;
// find system tray window
HWND trayWnd = FindWindow(_T("Shell_TrayWnd"), NULL);
if (trayWnd) {
trayWnd = FindWindowEx(trayWnd, NULL,_T("TrayNotifyWnd"), NULL);
if (trayWnd) {
trayWnd = FindWindowEx(trayWnd, NULL,_T("SysPager"), NULL);
if (trayWnd) {
trayWnd = FindWindowEx(trayWnd, NULL,_T("ToolbarWindow32"), NULL);
bFound = true;
}
}
}
ASSERT(bFound);
DWORD dwTrayPid;
GetWindowThreadProcessId(trayWnd, &dwTrayPid);
int count = (int) SendMessage(trayWnd, TB_BUTTONCOUNT, 0, 0);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTrayPid);
if (!hProcess) {
return true;
}
BOOL bIsWow64 = IsWow64();
SIZE_T dwSize = bIsWow64 ? sizeof(TBBUTTON64) : sizeof(TBBUTTON);
LPVOID lpData = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (!lpData) {
return true;
}
// Loop through all systray icons
for (int i = 0; i < count; i++) {
HWND hwnd32;
SendMessage(trayWnd, TB_GETBUTTON, i, (LPARAM)lpData);
if ( bIsWow64 ) {
// Try to read memory from 64-bit Explorer process. Hope the address of the traybar data is below 4GB
TBBUTTON64 tbb;
if (!ReadProcessMemory(hProcess, lpData, (LPVOID)&tbb, sizeof(TBBUTTON64), NULL)) {
continue;
}
DWORD64 hwnd;
// First member of TRAYDATA structure is HWND, so we can just use the address of the struct to read the member
if (!ReadProcessMemory(hProcess, (LPCVOID)tbb.dwData, (LPVOID)&hwnd, sizeof(DWORD64), NULL)) {
continue;
}
// Hope this does not get truncated, but we shouldn't have that many windows
hwnd32 = (HWND)hwnd;
} else {
TBBUTTON tbb;
if (!ReadProcessMemory(hProcess, lpData, (LPVOID)&tbb, sizeof(TBBUTTON), NULL)) {
continue;
}
DWORD32 hwnd;
// First member of TRAYDATA structure is HWND, so we can just use the address of the struct to read the member
if (!ReadProcessMemory(hProcess, (LPCVOID)tbb.dwData, (LPVOID)&hwnd, sizeof(DWORD32), NULL)) {
continue;
}
hwnd32 = (HWND)hwnd;
}
DWORD dwProcessId = 0;
GetWindowThreadProcessId(hwnd32, &dwProcessId);
// XXX - DO SOMETHING WITH dwProcessId
}
VirtualFreeEx(hProcess, lpData, NULL, MEM_RELEASE);
return true;
}
User contributions licensed under CC BY-SA 3.0