2012-08-26 57 views
3

我想附加到一個單獨的應用程序(例如Microsoft Excel),並檢測某個菜單項何時被點擊(或新版本中的功能區命令,無論什麼)。創建一個Windows掛鉤來檢測菜單點擊

我以爲我可以用RegisterWindowMessage來做user32.dll,但我不知道要截取哪些信息。理想情況下,我想概括這個和檢測是這樣的:

"menu item XXX was clicked in the app YYY" 

我發現這個CodeProject article它展示瞭如何像創作的控制,應用程序啓動/停止等註冊事件掛鉤,但我找不到一個如何獲得按鈕點擊或菜單點擊的例子。

這甚至可能嗎?我在正確的軌道上,還是我需要採取不同的方法?

+1

'SetWindowsHookEx'允許你全局捕獲鼠標事件。看到[這篇文章]上的最後一個答案(http://stackoverflow.com/questions/11607133/global-mouse-event-handler)。它有一個完整的代碼示例。 – gideon

+0

@gideon:謝謝,但我相信這隻會給我低級別的鼠標事件(如座標)。我認爲通過攔截Windows消息我可以確定被點擊的實際控制。 – Lou

+0

您可以查看可訪問性API。他們可能能夠做你需要的。 – CodesInChaos

回答

6

好吧,所以我無法抗拒這裏的挑戰:)寫了一個小程序,做你想做的。

它是如何工作的SetWindowsHookEx讓你放置一個全局鼠標鉤子。 現在,你得到X,Y,然後用WindowFromPoint得到你的目標窗口的hWnd。從這裏你可以做任何你喜歡的事情,就我而言,我發送了一個WM_GETTEXT來獲得它的頭銜。

這是程序看起來像實現的。一旦你點擊Begin,它將全局查找右鍵單擊事件,並將它們添加到列表框中。 注意:它需要一個窗口窗體應用程序,該鉤子將無法與控制檯應用程序一起使用。

enter image description here

使用(只要創建一個默認的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; 
    } 
}