Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get ToolTip Text from Icon in System Tray

Tags:

c#

tooltip

I'm trying to read the ToolTip text from the system tray for an application that is not my own. Basically just what I figure will be the easiest way to pull some status information.

What would be the easiest way to pull the ToolTip text using C#?

like image 749
Adam Haile Avatar asked Jun 16 '11 02:06

Adam Haile


2 Answers

Let's start with finding systray window handle:

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

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

Systray window is a toolbar class, you need to get information for a single icon:

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

        Kernel32.VirtualFreeEx(
            hProcess,
            ipRemoteBuffer,
            UIntPtr.Zero,
            MemAllocationType.RELEASE);

        Kernel32.CloseHandle(hProcess);

        return true;
    }

Now, all you have to do is iterate through buttons and get data:

        IntPtr _ToolbarWindowHandle = GetSystemTrayHandle();
        UInt32 count = User32.SendMessage(_ToolbarWindowHandle, TB.BUTTONCOUNT, 0, 0);

        for (int i = 0; i < count; i++)
        {
            TBBUTTON tbButton = new TBBUTTON();
            string text = String.Empty;
            IntPtr ipWindowHandle = IntPtr.Zero;

            bool b = GetTBButton(_ToolbarWindowHandle, i, ref tbButton, ref text, ref ipWindowHandle);

        }
like image 120
michalczerwinski Avatar answered Oct 30 '22 00:10

michalczerwinski


In case anyone comes across this thread and has the same need, I posted a thread asking how to properly implement the code example and received a lot of help, and a working solution here:

Trouble implementing code example using PInvoke Declarations

like image 39
Jason O Avatar answered Oct 30 '22 02:10

Jason O