Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to automate right-click on icon in system tray using C#

Tags:

c#

contextmenu

I want to write automation framework for .NET application. To start application user should right- click on icon in system tray and choose option from context menu. I have done some resaerch and found only way how to know tooltip of icon in tray.:

    [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);

    [DllImport("user32.dll", SetLastError = true)]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);


    [DllImport("kernel32.dll")]
    static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);

    [Flags]
    enum ProcessAccessFlags : uint
    {
        All = 0x001F0FFF,
        Terminate = 0x00000001,
        CreateThread = 0x00000002,
        VMOperation = 0x00000008,
        VMRead = 0x00000010,
        VMWrite = 0x00000020,
        DupHandle = 0x00000040,
        SetInformation = 0x00000200,
        QueryInformation = 0x00000400,
        Synchronize = 0x00100000
    }


    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, FreeType dwFreeType);

    [Flags]
    public enum FreeType
    {
        Decommit = 0x4000,
        Release = 0x8000,
    }

    const uint TB_GETBUTTON = 1047;
    const uint TB_GETBUTTONTEXTW = 1099;
    const uint TB_BUTTONCOUNT = 1048;
    const uint TB_PRESSBUTTON = 1027;
    const uint TB_HIDEBUTTON = 1028;
    const uint VM_COMMAND = 273;
    const uint WM_RBUTTONDOWN = 0x204;
    const uint WM_RBUTTONUP = 0x205;
    const uint MK_RBUTTON = 2;
    const uint WM_COMMAND = 0x0111;
    const uint BM_CLICK = 245;
    const uint TB_HIGHLIGHTBUTTON = 0x0407;


    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
       uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);

    [DllImport("user32.dll", EntryPoint = "PostMessage")]
    public static extern int PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);


    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint dwSize, out IntPtr lpNumberOfBytesRead);

    [Flags]
    public enum AllocationType
    {
        Commit = 0x1000,
        Reserve = 0x2000,
        Decommit = 0x4000,
        Release = 0x8000,
        Reset = 0x80000,
        Physical = 0x400000,
        TopDown = 0x100000,
        WriteWatch = 0x200000,
        LargePages = 0x20000000
    }

    [Flags]
    public enum MemoryProtection
    {
        Execute = 0x10,
        ExecuteRead = 0x20,
        ExecuteReadWrite = 0x40,
        ExecuteWriteCopy = 0x80,
        NoAccess = 0x01,
        ReadOnly = 0x02,
        ReadWrite = 0x04,
        WriteCopy = 0x08,
        GuardModifierflag = 0x100,
        NoCacheModifierflag = 0x200,
        WriteCombineModifierflag = 0x400
    }


    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CloseHandle(IntPtr hObject);

    [StructLayout(LayoutKind.Sequential)]
    internal struct TBBUTTON
    {
        public Int32 iBitmap;
        public Int32 idCommand;
        public byte fsState;
        public byte fsStyle;
        public byte bReserved1;
        public byte bReserved2;
        public UInt32 dwData;
        public IntPtr iString;
    }

    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);


    //getting systemtray icon
    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;
    }

    public static List<string> texts = new List<string>();

    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 = GetWindowThreadProcessId(hToolbar, out processId);

        IntPtr hProcess = OpenProcess(ProcessAccessFlags.All, false, (int)processId);
        if (hProcess == IntPtr.Zero) { Debug.Assert(false); return false; }

        IntPtr ipRemoteBuffer = VirtualAllocEx(
            hProcess,
            IntPtr.Zero,
            (uint)new UIntPtr(BUFFER_SIZE),
            AllocationType.Commit,
            MemoryProtection.ReadWrite);

        if (ipRemoteBuffer == IntPtr.Zero) { Debug.Assert(false); return false; }

        // TBButton
        fixed (TBBUTTON* pTBButton = &tbButton)
        {
            IntPtr ipTBButton = new IntPtr(pTBButton);

            int b = (int)SendMessage(hToolbar, TB_GETBUTTON, (IntPtr)i, ipRemoteBuffer);
           // SendMessage(hToolbar, VM_COMMAND, (IntPtr)tbButton.idCommand, ipRemoteBuffer);


            if (b == 0) { Debug.Assert(false); return false; }

            // this is fixed
            Int32 dwBytesRead = 0;
            IntPtr ipBytesRead = new IntPtr(&dwBytesRead);

            bool b2 = ReadProcessMemory(
                hProcess,
                ipRemoteBuffer,
                ipTBButton,
                (uint)new UIntPtr((uint)sizeof(TBBUTTON)),
                out ipBytesRead);

            if (!b2) { Debug.Assert(false); return false; }
        }

        // button text
        fixed (byte* pLocalBuffer = localBuffer)
        {
            IntPtr ipLocalBuffer = new IntPtr(pLocalBuffer);

            int chars = (int)SendMessage(hToolbar, TB_GETBUTTONTEXTW, (IntPtr)tbButton.idCommand, ipRemoteBuffer);

            //SendMessage(hToolbar, BM_CLICK, (IntPtr)tbButton.idCommand, IntPtr.Zero);

           // int getmes = PostMessage(hToolbar, TB_HIDEBUTTON, (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 = ReadProcessMemory(
                hProcess,
                ipRemoteBuffer,
                ipLocalBuffer,
                (uint)new UIntPtr(BUFFER_SIZE),
                out ipBytesRead);

            if (!b4) { Debug.Assert(false); return false; }

            text = Marshal.PtrToStringUni(ipLocalBuffer, chars);

            if (text.Contains("Pen"))
            {
                int buttonid = tbButton.idCommand;
                SendMessage(hToolbar, TB_HIGHLIGHTBUTTON, (IntPtr)tbButton.idCommand, IntPtr.Zero);
            }
            texts.Add(text);

            if (text == " ") text = String.Empty;
        }

        VirtualFreeEx(
            hProcess,
            ipRemoteBuffer,
            (int)UIntPtr.Zero,
            FreeType.Release);

        CloseHandle(hProcess);

        return true;
    }



    static void Main(string[] args)
    {

        IntPtr _ToolbarWindowHandle = GetSystemTrayHandle();

        UInt32 count = (uint)SendMessage(_ToolbarWindowHandle, TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero);

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

        }

        foreach (var item in texts)
        {
            Console.WriteLine(item);
        }
    }
}

Is there any way to right-click onsystem tray icon using C#? Would be thankful for answers.......

like image 681
Kateryna Mudrikuva Avatar asked Oct 11 '22 04:10

Kateryna Mudrikuva


1 Answers

There is no programmatic way to access the tray icons of other programs (source: Raymond Chen). Anything you try is nothing but a hack that is likely to break and fail.

Try to find a way that does not involve having to automate right clicks on tray icons. Maybe the program in question can be controlled via some API or via command line parameters.

like image 179
mafu Avatar answered Oct 14 '22 04:10

mafu