我想附加到一个单独的应用程序(例如Microsoft Excel),并检测何时单击某个菜单项(或新版本中的功能区命令,等等)。
我以为我可以使用RegisterWindowMessage
中的user32.dll
来做到这一点,但我不知道拦截哪些消息。理想情况下,我想概括一下并检测类似的内容:
"menu item XXX was clicked in the app YYY"
我发现这个CodeProject article显示了如何为控件创建,应用程序启动/停止等事件注册挂钩,但我找不到如何获取按钮点击或菜单点击的示例。 / p>
这甚至可能吗?我是在正确的轨道上,还是我需要采取不同的方法?
答案 0 :(得分:7)
好吧,所以我无法抗拒这里的挑战:)写了一个能做你想做的小程序。
工作原理:SetWindowsHookEx
可让您放置全局鼠标挂钩。 现在,您获得X
,Y
,然后使用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;
}
}