Systray Access

Is there a way (in C #) to access systray? I'm not talking about creating a notification icon. I want to iterate over the items in the tray (I would have guessed through the processes, but I don’t know how to determine what is actually in the tray and what is just a process), and also present the items with their icons in my own u.

+7
c #
source share
4 answers

How do you feel about Win32 interaction? I found C / Win32 code that could do the trick for you. (Actually, this looks like an interesting problem, so I can try to solve it myself, just not now).

It seems like magic gets into the system tray window:

NotifyWnd = FindWindowEx(SysTray, 0, "TrayNotifyWnd", 0); 

He then sets the hook on his message pump:

 hHook=SetWindowsHookEx(WH_CALLWNDPROC,HOOKPROC(MsgProc), hInstance,dwExplorerThreadId); 

Then, during the callback hook of the message message, it receives a link to some window pointer data:

 TWDataT* twd=(TWDataT*)GetWindowLong(NotifyWnd,0); 

Mystery is his loop:

  pTWIconDataT p=COMCTL32_332(twd->iconsInfo,i); 

COMCTL32_332 is determined using GetProcAddress and points to serial number 332 Comctl32.dll - according to my test using the dependency viewer, DPA_GetPtr , which receives data from an array of dynamic pointers. I am not familiar with what is happening there, behind the curtains, but this does not seem completely out of the question.

I'm going to play with this a bit, but hopefully this is a good place to get you started. :)

+5
source share

Mathias Rauen madCollection (for Delphi not C #) can list tray icons .

And there is a command line tool: Windows Trap Scan Utility

I also wrote (did not release) my own program in Delphi (not Delphi.NET), without using madCollection, which displays tray icons, process names, tooltips and other information, but this is not ideal. There are several icons that it cannot display (even if it contains other information), and it cannot display any icons under 9x windows. I have not tested it at all under Vista.

+1
source share

In Windows 2000, the icons in the system bar are in the form of a simple toolbar element (the window class is ToolbarWindow32), which is a child of the TrayNotifyWnd window, so you can send toolbar messages such as TB_BUTTONCOUNT and TB_GETBUTTON .

You have to be careful: messages like TB_GETBUTTON that require a pointer to a buffer in which to store the results, this buffer must be in the SysTray process itself . This requires that you have rights and you use VirtualAllocEx to allocate memory.

I have not tried this on XP or Vista. I expect that everything will change.

+1
source share

This was quite possible in Windows 2000 / Xp. Unfortunately, in Windows 7 this is no longer feasible.

The trick was simple: you need to find the handle of the tray window:

  static IntPtr GetSystemTrayHandle() { IntPtr hWndTray = FindWindow("Shell_TrayWnd", null); if (hWndTray != IntPtr.Zero) { hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "TrayNotifyWnd", null); if (hWndTray != IntPtr.Zero) { hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "SysPager", null); if (hWndTray != IntPtr.Zero) { hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "ToolbarWindow32", null); return hWndTray; } } } return IntPtr.Zero; } 

Since this window is ToolbarWindow32, you need to list all the buttons using WinAPI. The only problem is that all the structures used for this should be allocated to the address space of the target process, so you need to use something like this:

  private 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; } 

Unfortunately, in Windows 7, tbButton.dwData is 0, so you cannot find any connection between NotifyIcon and the target process.

0
source share

All Articles