【问题标题】:Creating a Windows hook to detect menu clicks创建一个 Windows 挂钩来检测菜单点击
【发布时间】:2012-08-26 16:57:27
【问题描述】:

我想附加到单独的应用程序(例如 Microsoft Excel),并检测何时单击某个菜单项(或新版本中的功能区命令等)。

我以为我可以使用user32.dll 中的RegisterWindowMessage 来做到这一点,但我不知道要拦截哪些消息。理想情况下,我想对此进行概括并检测以下内容:

"menu item XXX was clicked in the app YYY"

我发现了这个CodeProject article,它展示了如何为创建控件、应用程序启动/停止等事件注册钩子,但我找不到如何获取按钮点击或菜单点击的示例。

这甚至可能吗?我是在正确的轨道上,还是我需要采取不同的方法?

【问题讨论】:

  • SetWindowsHookEx 允许您全局捕获鼠标事件。请参阅this post 上的最后一个答案。它有一个完整的代码示例。
  • @gideon:谢谢,但我相信这只会给我低级鼠标事件(如坐标)。我认为通过拦截 windows 消息,我可以确定被点击的实际控件。
  • 您可以查看无障碍 API。他们或许能够满足您的需求。
  • @Dilbert 我打算发布更新,但我只是在下面的答案中写下了整个内容。
  • @Dilbert 啊!对不起!看来我误读了您的帖子并编写了代码来检查右键单击:S 将其更改为左键单击应该不难,至于菜单项,PinPoint 有点困难,因为有时菜单是自定义实现的。您也许可以使用GetMenu 来获取菜单,但这可能不确定。

标签: c# .net windows hook


【解决方案1】:

好吧,所以我无法抗拒这里的挑战 :) 写了一个小程序,可以满足你的要求。

工作原理SetWindowsHookEx 允许您放置全局鼠标挂钩。 现在,您得到XY,然后使用WindowFromPoint 得到目标窗口的hWnd。从这里你可以做任何你想做的事情,在我的例子中,我发送了一个WM_GETTEXT 来获取它的标题。

这就是程序看起来像已实现的样子。单击Begin 后,它将全局查找右键单击事件,并将它们添加到列表框中。 注意:它需要是一个窗体应用程序,钩子不适用于控制台应用程序。

用法(只需创建一个默认的 WinForms 项目并将其更改为这个):

 public partial class MainForm : Form
 {
    public MainForm()
    {
        InitializeComponent();
    }
    RMouseListener _native;
    private void button1_Click(object sender, EventArgs e)
    {//start
        _native = new RMouseListener();
        _native.RButtonClicked += 
             new EventHandler<SysMouseEventInfo>(_native_RButtonClicked);
    }
    private void button2_Click(object sender, EventArgs e)
    {//stop
        _native.Close();
    }
    void _native_RButtonClicked(object sender, SysMouseEventInfo e)
    {
        listBox1.Items.Add(e.WindowTitle);
    }

}

实现(有点代码;))

public class SysMouseEventInfo : EventArgs
{
    public string WindowTitle { get; set; }
}
public class RMouseListener
{
    public RMouseListener()
    {
        this.CallBack += new HookProc(MouseEvents);
        //Module mod = Assembly.GetExecutingAssembly().GetModules()[0];
        //IntPtr hMod = Marshal.GetHINSTANCE(mod);
        using (Process process = Process.GetCurrentProcess())
        using (ProcessModule module = process.MainModule)
        {
            IntPtr hModule = GetModuleHandle(module.ModuleName);
            _hook = SetWindowsHookEx(WH_MOUSE_LL, this.CallBack, hModule, 0);
            //if (_hook != IntPtr.Zero)
            //{
            //    Console.WriteLine("Started");
            //}
        }
    }
    int WH_MOUSE_LL = 14;
    int HC_ACTION = 0;
    HookProc CallBack = null;
    IntPtr _hook = IntPtr.Zero;

    public event EventHandler<SysMouseEventInfo> RButtonClicked;

    int MouseEvents(int code, IntPtr wParam, IntPtr lParam)
    {
        //Console.WriteLine("Called");

        if (code < 0)
            return CallNextHookEx(_hook, code, wParam, lParam);

        if (code == this.HC_ACTION)
        {
            // Left button pressed somewhere
            if (wParam.ToInt32() == (uint)WM.WM_RBUTTONDOWN)
            {
                MSLLHOOKSTRUCT ms = new MSLLHOOKSTRUCT();
                ms = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                IntPtr win = WindowFromPoint(ms.pt);
                string title = GetWindowTextRaw(win);
                if (RButtonClicked != null)
                {
                    RButtonClicked(this, new SysMouseEventInfo { WindowTitle = title });
                }
            }
        }
        return CallNextHookEx(_hook, code, wParam, lParam);
    }

    public void Close()
    {
        if (_hook != IntPtr.Zero)
        {
            UnhookWindowsHookEx(_hook);
        }
    }
    public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

    [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true)]
    public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    public static extern IntPtr GetModuleHandle(string lpModuleName);

    [DllImport("user32.dll")]
    static extern IntPtr WindowFromPoint(int xPoint, int yPoint);

    [DllImport("user32.dll")]
    static extern IntPtr WindowFromPoint(POINT Point);

    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [Out] StringBuilder lParam);

    public static string GetWindowTextRaw(IntPtr hwnd)
    {
        // Allocate correct string length first
        //int length = (int)SendMessage(hwnd, (int)WM.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
        StringBuilder sb = new StringBuilder(65535);//THIS COULD BE BAD. Maybe you shoudl get the length
        SendMessage(hwnd, (int)WM.WM_GETTEXT, (IntPtr)sb.Capacity, sb);
        return sb.ToString();
    }
}
[StructLayout(LayoutKind.Sequential)]
public struct MSLLHOOKSTRUCT
{
    public POINT pt;
    public int mouseData;
    public int flags;
    public int time;
    public UIntPtr dwExtraInfo;
}
enum WM : uint
{//all windows messages here
    WM_RBUTTONDOWN = 0x0204,
    WM_GETTEXT = 0x000D,
    WM_GETTEXTLENGTH = 0x000E
}

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public int X;
    public int Y;

    public POINT(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多