我正试图在我的C#app中挂钩创建一个窗口。
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");
}
答案 0 :(得分:7)
与大多数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_CREATE
和EVENT_OBJECT_SHOW
事件。您可以侦听自己的进程/线程事件,也可以侦听当前桌面上的所有进程/线程。
这将为您提供各种“创建”和显示通知,包括对话框中的子HWND,甚至列表框中的项目等等;因此,您需要过滤以仅提取顶级HWND的那些:例如。检查idObject == OBJID_WINDOW和idChild == 0;该hwnd是可见的(IsVisible()
)并且是顶级的。
请注意,使用WinEvents要求调用SetWinEventHook的线程正在处理消息 - 如果它是带有UI的线程,则通常就是这种情况。如果没有,您可能需要手动添加消息循环(GetMessage / TranslateMessage)。此外,您还希望将GC.KeepAlive()与回调一起使用,以防止在您调用UnhookWinEvents之后收集它。
答案 1 :(得分:2)
您的代码存在的一个问题是hhookProc
可能会在您的本机代码仍然需要时进行垃圾回收。使用GC.KeepAlive
或输入静态变量。
hMod
param应该为null,因为你在自己的进程中指定了一个线程:
hMod [in]
类型:HINSTANCE
包含lpfn参数指向的钩子过程的DLL句柄。如果dwThreadId参数指定当前进程创建的线程,并且钩子过程位于与当前进程关联的代码中,则hMod参数必须设置为NULL。
但我认为这仅适用于您指定的主题上的Windows。
理论上,您可以在其他应用程序甚至全局钩子中指定线程。然后在相应的线程上调用指定的回调,即使该线程在另一个进程中,在这种情况下你的dll会被注入到该进程中(这就是你需要首先指定模块句柄的原因)。
但我相信用.net代码是不可能的,因为注入其他进程并在那里调用hook方法的机制不适用于JIT编译代码。
答案 2 :(得分:1)
这不适用于C#
范围:线程
如果应用程序为不同应用程序的线程安装了挂钩过程,则过程必须位于DLL 中。
(
SetWindowsHookEx
的文档)
范围:全球
要安装全局钩子,钩子必须具有本机DLL导出,以将自身注入另一个需要有效,一致的函数调用的进程中。此行为需要DLL导出。 .NET Framework不支持DLL导出。
(Source)
答案 3 :(得分: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();
在抓住手柄之前确保你在发射时抓住正确的手柄。
答案 4 :(得分:0)
我看到它是一个控制台应用程序,因此控制台应用程序不会进入Windows消息循环。
简单的解决方案是包含system.windows.forms
然后在主页中输入application.start() 事情会没事的。)