C# 系统存取
有没有办法(用C#)访问systray? 我不是说制作通知图标。C# 系统存取,c#,.net,C#,.net,有没有办法(用C#)访问systray? 我不是说制作通知图标。 我想遍历托盘中的项目(我想通过流程进行猜测,但我不知道如何确定托盘中的实际内容和流程),并在我自己的ui中用图标表示项目。Mathias Rauen的madCollection(适用于Delphi而非c#)可以列出 还有一个命令行工具: 我还用Delphi(不是Delphi.NET)编写(不是发布)了我自己的程序,没有使用madCollection,它显示托盘图标、进程名称、工具提示和其他信息,但并不完美。有几个图标它不能显示(即
我想遍历托盘中的项目(我想通过流程进行猜测,但我不知道如何确定托盘中的实际内容和流程),并在我自己的ui中用图标表示项目。Mathias Rauen的madCollection(适用于Delphi而非c#)可以列出 还有一个命令行工具:
我还用Delphi(不是Delphi.NET)编写(不是发布)了我自己的程序,没有使用madCollection,它显示托盘图标、进程名称、工具提示和其他信息,但并不完美。有几个图标它不能显示(即使它列出了其他信息),并且在Windows9x下它不能显示任何图标。我还没有在Vista下测试过它。你觉得Win32 interop怎么样?我发现这可能对你有用。(事实上,这看起来是一个有趣的问题,所以我可能会尝试自己解决它,而不是现在) 神奇的是,他获得了系统托盘窗口的手柄:
NotifyWnd = FindWindowEx(SysTray, 0, "TrayNotifyWnd", 0);
然后他在信息泵上设置了一个钩子:
hHook=SetWindowsHookEx(WH_CALLWNDPROC,HOOKPROC(MsgProc),
hInstance,dwExplorerThreadId);
然后,在消息pump hook回调期间,他获得了对有关窗口的一些指针数据的引用:
TWDataT* twd=(TWDataT*)GetWindowLong(NotifyWnd,0);
神秘的是他的循环:
pTWIconDataT p=COMCTL32_332(twd->iconsInfo,i);
COMCTL32_332是用GetProcAddress定义的,它指向COMCTL32.dll的第332个序号——根据我在Dependency Viewer中的检查,也就是说,它从动态指针数组中获取数据。我不熟悉那里的幕后情况,但这似乎不是完全不可能的
我将自己玩一玩,但希望这是一个让你开始的好地方。:) 在Windows 2000中,系统托盘图标位于普通工具栏控件(窗口类“ToolbarWindow32”)中,该控件是“TrayNotifyWnd”窗口的子窗口,因此您可以向其发送工具栏消息,例如
TB\u BUTTONCOUNT
和TB\u GETBUTTON
不过,您需要小心:诸如TB_GETBUTTON之类的消息需要指向用于存储结果的缓冲区的指针,需要该缓冲区位于SysTray进程本身中。这要求您拥有正确的权限,并且使用VirtualAllocEx
分配内存
我还没有在XP或Vista上试用过。我想事情已经改变了。在Windows 2000/Xp中实现它是完全可能的。 不幸的是,在Windows7中,这似乎不再可行 诀窍很简单:您必须找到托盘窗口句柄:
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与目标进程之间的任何关联