3

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

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

"menu item XXX was clicked in the app YYY"

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

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

4

1 回答 1

8

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

它是如何工作的: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;
    }
}
于 2012-08-27T07:06:21.497 回答