Search code examples
c++explorernotifyicondll-injectioneasyhook

How to change NotifyIcon's context menu in explorer.exe?


I want to extend the default Speakers notificon's (tray icon) right-click context menu with a new item. Also, I want to handle the mouseclick using C++.

Illustration

enter image description here

What I know so far

I learned how to dll-inject using CreateRemoteThread(), because I think that's the way to go. My problem is: what to do inside the injected dll? For example, how to access the NotifyIcon object?

Maybe it is possible with a simple Windows API call, but I'm not familiar with it.


Solution

  • Thanks Luke for the hint. I got it working using EasyHook. I chose it, because it also supports 64-bit dll-inject.

    DLL to inject:

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Threading;
    using EasyHook;
    namespace InjectDLL
        {
        public class Main : EasyHook.IEntryPoint
        {
            LocalHook CreateTrackPopupMenuExHook;
            public Main(RemoteHooking.IContext InContext){}
            public void Run(RemoteHooking.IContext InContext)
            {
                try
                {
                    CreateTrackPopupMenuExHook = LocalHook.Create(
                        LocalHook.GetProcAddress("user32.dll", "TrackPopupMenuEx"),
                        new DTrackPopupMenuEx(TrackPopupMenuEx_Hooked),
                        this);
                    CreateTrackPopupMenuExHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
                }
                catch
                {
                    return;
                }
                while (true)
                {
                    Thread.Sleep(500);
                }
            }
    
            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern bool AppendMenu(IntPtr hMenu, long uFlags, int uIDNewItem, string lpNewItem);
    
            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
            static extern IntPtr TrackPopupMenuEx(
                IntPtr hMenu,
                uint fuFlags,
                int x,
                int y,
                IntPtr hwnd,
                IntPtr lptpm);
    
            [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
            delegate IntPtr DTrackPopupMenuEx(
                IntPtr hMenu,
                uint fuFlags,
                int x,
                int y,
                IntPtr hwnd,
                IntPtr lptpm);
    
            const long MF_STRING = 0x00000000L;
            const long MF_SEPARATOR = 0x00000800L;
    
            static IntPtr TrackPopupMenuEx_Hooked(
                IntPtr hMenu,
                uint fuFlags,
                int x,
                int y,
                IntPtr hwnd,
                IntPtr lptpm)
            {
                IntPtr returnValue = IntPtr.Zero;
                try
                {
                        //Separator
                        AppendMenu(hMenu, MF_SEPARATOR, 0, null);
                        //New menu item
                        AppendMenu(hMenu, MF_STRING, 40010, "TestMenuItem");
                        //call the default procedure
                        returnValue = TrackPopupMenuEx(hMenu, fuFlags, x, y, hwnd, lptpm);
                        //our menu item is selected
                        if (returnValue == (IntPtr)40010)
                        {
                            /* CODE HERE */
                            returnValue = IntPtr.Zero;
                        }
                        return returnValue;
                }
                catch
                {
                    return;
                }
                return returnValue;
            }
        }
    }