简体   繁体   English

Systray访问

[英]Systray Access

Is there a way (in C#) to access the systray? 有没有办法(在C#中)访问系统托盘? I am not talking about making a notify icon. 我不是在谈论制作通知图标。 I want to iterate through the items in the tray (I would guess 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 represent the items with their icons in my own ui. 我想迭代托盘中的项目(我会猜测过程,但我不知道如何确定托盘中的实际内容以及只是一个过程)并且还用我自己的图标表示项目UI。

How do you feel about Win32 interop? 你对Win32互操作有什么看法? I found C/Win32 code that might do the trick for you. 我找到了可能适合你的C / Win32代码 (Actually, it looks like an interesting problem so I might try to tackle it myself, just not now). (实际上,它看起来像一个有趣的问题所以我可能会尝试自己解决它,而不是现在)。

The magic appears to be that he gets a handle to the system tray's window: 神奇似乎是他获得了系统托盘窗口的句柄:

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

Then he sets a hook on its message pump: 然后他在其消息泵上设置了一个钩子:

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

Then during the message pump hook callback, he gets a reference to some pointer data about the window: 然后在消息泵钩子回调期间,他获得了关于窗口的一些指针数据的引用:

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

The mystery is then his loop: 那个谜就是他的循环:

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

COMCTL32_332 is defined with GetProcAddress and points to ordinal 332 of Comctl32.dll - according to my checking with Dependency Viewer, that's DPA_GetPtr , which gets data from a dynamic pointer array. COMCTL32_332使用GetProcAddress定义并指向Comctl32.dll的序号332 - 根据我对Dependency Viewer的检查,这是DPA_GetPtr ,它从动态指针数组中获取数据。 I'm not familiar with what's going on behind the scenes there, but it doesn't seem entirely out of the question. 我不熟悉那里幕后发生的事情,但这似乎并不是完全不可能的。

I'm going to play with this a bit myself, but hopefully it's a good place to get you started. 我自己会玩这个,但希望这是一个让你入门的好地方。 :) :)

Mathias Rauen's madCollection (for Delphi not c#) can list Tray Icons . Mathias Rauen的madCollection(对于Delphi而不是c#)可以列出Tray Icons

And there is a commandline tool: Windows System Tray Scan Utility 还有一个命令行工具: Windows系统托盘扫描实用程序

I have also written (not released) my own program in Delphi (not Delphi.NET), not using madCollection, which shows tray icons, process names, tooltips and other info but it's not perfect. 我还在Delphi(不是Delphi.NET)中编写(未发布)我自己的程序,不使用madCollection,它显示托盘图标,进程名称,工具提示和其他信息,但它并不完美。 There are a few icons it can't display (even though it lists other info), and it can't display any icons under windows 9x. 有一些图标无法显示(即使它列出了其他信息),也无法在Windows 9x下显示任何图标。 I haven't tested it at all under Vista. 我还没有在Vista下测试它。

In Windows 2000, the system tray icons are in a plain toolbar control (window class "ToolbarWindow32") which is a child of the "TrayNotifyWnd" window, so you can send it toolbar messages such as TB_BUTTONCOUNT and TB_GETBUTTON . 在Windows 2000中,系统托盘图标位于普通工具栏控件(窗口类“ToolbarWindow32”)中,该控件是“TrayNotifyWnd”窗口的子项,因此您可以发送工具栏消息,例如TB_BUTTONCOUNTTB_GETBUTTON

You need to be careful though: messages such as TB_GETBUTTON which require a pointer to a buffer in which to store the results need that buffer to be in the SysTray process itself . 但是你需要注意:TB_GETBUTTON之类的消息需要一个指向缓冲区的指针来存储结果,这个缓冲区需要在SysTray进程中 This requires that you have the right permissions, and that you use VirtualAllocEx to allocate the memory. 这要求您具有正确的权限,并且您使用VirtualAllocEx来分配内存。

I haven't tried it on XP or Vista. 我没有在XP或Vista上试过它。 I expect things have changed. 我希望事情发生了变化。

It was perfectly possible to achieve it in Windows 2000/Xp. 完全有可能在Windows 2000 / Xp中实现它。 Unfortunately in Windows 7 it seems to be not feasible any more. 不幸的是,在Windows 7中它似乎不再可行。

Trick was simple: you have to find tray window handle: 诀窍很简单:你必须找到托盘窗口把手:

        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 then you have to enumerate throughout buttons using WinAPI. 由于此窗口是ToolbarWindow32,因此您必须使用WinAPI枚举整个按钮。 The only problem is that all structures used for this should be allocated in target process address space, so you have 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 equal 0, so you can't find any association between NotifyIcon and a target process 不幸的是,在Windows 7中,tbButton.dwData等于0,因此您无法在NotifyIcon和目标进程之间找到任何关联

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM