2012-02-01 47 views
1

我想钩在我的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)时,我从来没有进入我的钩子,我做错了什么?

谢谢。


编辑

的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"); 
    } 
+0

为什么要使用一个键盘钩子?我想检测何时exe(即没有GUI)调用另一个exe(有一个GUI)。什么是键盘的链接? – 2012-02-01 21:35:13

+0

我误解了WH_CBT的含义。 – CodesInChaos 2012-02-01 21:37:55

+0

你为什么用'hwnd'作为模块句柄的前缀?这不是窗户把手。 – CodesInChaos 2012-02-01 21:40:21

回答

2

你的代码有一个问题,hhookProc可以被垃圾收集,而你的本地代码仍然需要它。使用GC.KeepAlive或放入一个静态变量。

hMod PARAM也许应该是空的,因为你在你自己的进程中指定线程:

HMOD [中]

类型:HINSTANCE

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


但我认为这只能用于指定的线程窗口。

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

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

+0

已经是这样了,我发布了所有的代码,看看我的编辑。 – 2012-02-01 21:54:45

+0

@Arnaud您的新代码仍然被破坏。您需要在自己的进程中指定一个'0' hModule和一个线程。在这种情况下,您只能在自己的过程中观察窗口。或者你指定'0'或一个外部线程和一个非空'hModule'。但正如我写的,第二种方法在.net中可能不可能。 – CodesInChaos 2012-02-01 21:56:42

+0

实际上,我不知道应该将哪个值传递给SetWindowsHookEx来挂接我从当前应用程序内部启动的应用程序... – 2012-02-01 22:02:27

0

我不熟悉你所引用的NativeMethod类,但我会做一些假设并尝试做一些理由。 我的猜测与你挂钩的手柄有关。该

dummy.MainWindowHandle

代表了最前端的窗口的句柄,这通常是你在找什么。但是,在这种情况下,您将打开一个MessageBox.Show(),它可能具有与您所连接的句柄不同的句柄。

我认为

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

很可能将返回相同的结果

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

所以我认为这是肯定地说,他们可能给你,你不是活得手柄寻找。

也许试着做一个测试WinForm应用程序。这样你可以抓住合适的手柄。只要确保抓住手柄,以确保您在启动时抓住右手柄使用前

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

+0

我修改了代码,但没有改变,无法钩住任何东西... – 2012-02-01 21:36:00

+0

@ArnaudF。那么我需要更多关于NativeMethod的东西或者项目的一个副本来进一步测试。我没有足够的工作。 – Corylulu 2012-02-01 21:38:24

+0

已添加全部代码 – 2012-02-01 21:51:41

7

与大多数SetWindowsHookEx钩子一样,WH_CBT钩子要求钩子回调存在于一个单独的Win32 DLL中,该DLL将被加载到目标进程中。这基本上要求钩子用C/C++编写,C#在这里不起作用。 (低级别的鼠标和键盘钩子是这个规则的两个例外,也可能在C#中使用其他钩子,但只有当你钩住你自己的线程时,所以dwThreadId是线程的标识符在目前的过程中,不是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之前收集它。

1

这将无法工作在C#

范围:线程

如果应用程序安装的不同应用程序的线程一个钩子程序,该程序必须是在一个DLL

(的SetWindowsHookEx文档)

范围:全球

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

Source

+0

在C#中实现的唯一可能的窗口钩子是_global低级别hooks_和_local线程hooks_。 – ordag 2012-02-01 22:30:16

0

我看到它是一个控制台应用程序,所以控制台应用程序不会进入窗口消息循环。

简单的方案是包括System.Windows.Forms的

,只需键入application.start()在主 ,事情都会好起来的:)