Systray访问

有没有办法(在C#中)访问系统托盘? 我不是在谈论制作通知图标。 我想迭代托盘中的项目(我会猜测过程但我不知道如何确定托盘中的实际内容以及只是一个过程)并且还用我自己的图标表示项目UI。

你对Win32互操作有什么看法? 我发现C / Win32代码可能会为您解决问题。 (实际上,它看起来像一个有趣的问题所以我可能会尝试自己解决它,而不是现在)。

神奇似乎是他获得了系统托盘窗口的句柄:

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

然后他在其消息泵上设置了一个钩子:

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

然后在消息泵钩子回调期间,他获得了关于窗口的一些指针数据的引用:

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

那个谜就是他的循环:

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

COMCTL32_332使用GetProcAddress定义并指向Comctl32.dll的序号332 – 根据我对Dependency Viewer的检查,即DPA_GetPtr ,它从动态指针数组中获取数据。 我不熟悉那里幕后发生的事情,但似乎并不是完全不可能的。

我自己会玩这个,但希望这是一个让你入门的好地方。 🙂

Mathias Rauen的madCollection(对于Delphi而不是c#)可以列出Tray Icons 。

还有一个命令行工具: Windows系统托盘扫描实用程序

我还在Delphi(不是Delphi.NET)中编写(未发布)我自己的程序,不使用madCollection,它显示托盘图标,进程名称,工具提示和其他信息,但它并不完美。 有一些图标无法显示(即使它列出了其他信息),也无法在Windows 9x下显示任何图标。 我还没有在Vista下测试它。

在Windows 2000中,系统托盘图标位于普通工具栏控件(窗口类“ToolbarWindow32”)中,该控件是“TrayNotifyWnd”窗口的子项,因此您可以发送工具栏消息,例如TB_BUTTONCOUNTTB_GETBUTTON

但是你需要注意:TB_GETBUTTON之类的消息需要一个指向缓冲区的指针来存储结果,这个缓冲区需要在SysTray进程中 。 这要求您具有正确的权限,并且您使用VirtualAllocEx来分配内存。

我没有在XP或Vista上试过它。 我希望事情发生了变化。

完全有可能在Windows 2000 / Xp中实现它。 不幸的是,在Windows 7中它似乎不再可行。

诀窍很简单:你必须找到托盘窗口手柄:

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

由于此窗口是ToolbarWindow32,因此您必须使用WinAPI枚举整个按钮。 唯一的问题是用于此目的的所有结构都应该分配在目标进程地址空间中,所以你必须使用这样的东西:

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

不幸的是,在Windows 7中,tbButton.dwData等于0,因此您无法在NotifyIcon和目标进程之间找到任何关联