如何挂钩应用程序?

我正试图在我的C#应用​​程序中挂钩创建一个窗口。

static IntPtr hhook = IntPtr.Zero; static NativeMethods.HookProc hhookProc; static void Main(string[] args) { // Dummy.exe is a form with a button that opens a MessageBox when clicking on it. Process dummy = Process.Start(@"Dummy.exe"); try { hhookProc = new NativeMethods.HookProc(Hook); IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); hhook = NativeMethods.SetWindowsHookEx(HookType.WH_CBT, hhookProc, hwndMod, (uint)AppDomain.GetCurrentThreadId()); Console.WriteLine("hhook valid? {0}", hhook != IntPtr.Zero); while (!dummy.HasExited) dummy.WaitForExit(500); } finally { if(hhook != IntPtr.Zero) NativeMethods.UnhookWindowsHookEx(hhook); } } static int Hook(int nCode, IntPtr wParam, IntPtr lParam) { Console.WriteLine("Hook()"); return NativeMethods.CallNextHookEx(hhook, nCode, wParam, lParam); } 

问题是,当点击我的按钮(在Dummy.exe中)时,我从不进入我的Hook,我做错了什么?

谢谢。


编辑

Program.cs中

 using System; using System.Diagnostics; namespace Hooker { class Program { static IntPtr hhook = IntPtr.Zero; static NativeMethods.HookProc hhookProc; static void Main(string[] args) { Process dummy = Process.Start(@"Dummy.exe"); try { hhookProc = new NativeMethods.HookProc(Hook); hhook = NativeMethods.SetWindowsHookEx(HookType.WH_CBT, hhookProc, IntPtr.Zero, 0); Console.WriteLine("hhook valid? {0}", hhook != IntPtr.Zero); while (!dummy.HasExited) dummy.WaitForExit(500); } finally { if(hhook != IntPtr.Zero) NativeMethods.UnhookWindowsHookEx(hhook); } } static int Hook(int nCode, IntPtr wParam, IntPtr lParam) { Console.WriteLine("Hook()"); return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam); } } } 

NativeMethods.cs

 namespace Hooker { using System; using System.Runtime.InteropServices; internal static class NativeMethods { public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, int dwThreadId); [DllImport("user32.dll")] public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", SetLastError = true)] public static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int pid); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName); } } 

对于虚拟,请使用以下命令执行新表单:

  public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { MessageBox.Show("CONTENT", "TITLE"); } 

您的代码的一个问题是,当您的本机代码仍然需要时,可以对hhookProc进行垃圾回收。 使用GC.KeepAlive或输入静态变量。

hMod参数应该为null,因为您在自己的进程中指定了一个线程:

hMod [in]

类型:HINSTANCE

DLL的句柄,包含lpfn参数指向的钩子过程。 如果dwThreadId参数指定由当前进程创建的线程,并且钩子过程在与当前进程关联的代码内,则必须将hMod参数设置为NULL。


但我认为这仅适用于您指定的线程上的Windows。

理论上,您可以在其他应用程序甚至全局钩子中指定线程。 然后在相应的线程上调用指定的回调,即使该线程在另一个进程中,在这种情况下你的dll会被注入到该进程中(这就是你需要首先指定模块句柄的原因)。

但我相信用.net代码是不可能的,因为注入其他进程并在那里调用hook方法的机制不适用于JIT编译代码。

与大多数SetWindowsHookEx挂钩一样, WH_CBT挂钩要求挂钩回调存在于一个单独的Win32 DLL中,该DLL将被加载到目标进程中。 这基本上要求钩子是用C / C ++编写的,C#在这里不起作用。

(低级鼠标和键盘挂钩是此规则的两个例外。也可能在C#中使用其他挂钩,但仅当您挂钩自己的一个线程时,所以dwThreadId是当前进程中线程的id但不是0.我还没有证实这一点。你需要确保你使用的是Win32 threadid,所以使用GetCurrentThreadId可能是最好的选择。)

如果你想要观察从另一个应用程序出现的新窗口,另一种C#友好的方法是使用SetWinEventHook API,指定WINEVENT_OUTOFCONTEXT(这是一个神奇的标志,可以将事件传递到您自己的进程,无需使用DLL和使C#在这里可用)并挂钩EVENT_OBJECT_CREATEEVENT_OBJECT_SHOW事件。 您可以侦听自己的进程/线程事件,也可以侦听当前桌面上的所有进程/线程。

这将为您提供各种“创建”和显示通知,包括对话框中的子HWND,甚至是列表框中的项目等等; 因此,您需要过滤以仅提取顶级HWND的那些:例如。 检查idObject == OBJID_WINDOW和idChild == 0; hwnd是可见的( IsVisible() )并且是顶级的。

请注意,使用WinEvents要求调用SetWinEventHook的线程正在处理消息 – 如果它是具有UI的线程,则通常就是这种情况。 如果没有,您可能需要手动添加消息循环(GetMessage / TranslateMessage)。 此外,您还希望将GC.KeepAlive()与回调一起使用,以防止在调用UnhookWinEvents之前收集它。

这在C#中不起作用

范围:线程

如果应用程序为不同应用程序的线程安装了挂钩过程,则该过程必须位于DLL中

SetWindowsHookEx文档)

范围:全球

要安装全局钩子,钩子必须具有本机DLL导出,以将自身注入另一个需要有效,一致的函数调用的进程中。 此行为需要DLL导出。 .NET Framework不支持DLL导出。

( 来源 )

我不熟悉你引用的NativeMethod类,但我会做一些假设并尝试做一些基础。 我的猜测这与你挂钩的处理方式有关。 该

dummy.MainWindowHandle

代表最前面的窗口的句柄,这通常是你正在寻找的。 但是,在这种情况下,您将打开一个MessageBox.Show(),它可能具有与您挂钩的句柄不同的句柄。

我想是的

 IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); 

可能会返回相同的结果

 dummy.Refresh(); IntPtr hwndMod = dummy.MainWindowHandle; 

所以我认为可以说他们可能会给你你不寻找的手柄。

也许尝试做一个测试WinForm应用程序。 这样你就可以抓住正确的手柄。 只要确保使用

 dummy.WaitForInputIdle(); dummy.Refresh(); 

在抓住手柄之前确保在发射时抓住正确的手柄。

我看到它是一个控制台应用程序,因此控制台应用程序不会进入Windows消息循环。

简单的解决方案是包含system.windows.forms

只需在你的主要类型中输入application.start()就可以了:)