Windows的消息沟子

复制代码
using System.Windows.Interop;

public partial class MainWindow : Window
{
    private const int WM_LBUTTONDOWN = 0x0201;
    private IntPtr _hookID = IntPtr.Zero;

    // 钩子回调函数
    private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    // 创建一个成员委托,将其用作成员以防止被垃圾收集
    private HookProc _proc;

    // 注册指定窗口的鼠标钩子
    private void RegisterMouseHook()
    {
        var hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
        hwndSource.AddHook(WndProc);

        _proc = HookCallback;
        _hookID = SetWindowsHookEx(WH_MOUSE_LL, _proc, IntPtr.Zero, GetCurrentThreadId());
    }

    // 取消注册指定窗口的鼠标钩子
    private void UnregisterMouseHook()
    {
        if (_hookID != IntPtr.Zero)
        {
            UnhookWindowsHookEx(_hookID);
            _proc = null;

            var hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
            hwndSource.RemoveHook(WndProc);
        }
    }

    // 窗口消息处理函数
    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        switch (msg)
        {
            case WM_DESTROY:
                UnregisterMouseHook();
                break;
        }

        return IntPtr.Zero;
    }

    // 钩子回调函数
    private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_LBUTTONDOWN)
        {
            // 处理鼠标左键按下事件
        }

        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
}

// 声明 Windows API 函数和常量
private const int WH_MOUSE_LL = 14;

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern uint GetCurrentThreadId();
复制代码

注册沟子简单,核心容易忘的一点就是全局沟子还是单窗口沟子

安装钩子和卸载钩子关键就是SetWindowsHookEx和UnhookWindowsHookEx方法。

 SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)  函数将钩子加入到钩子链表中,说明一下四个参数:

idHook 钩子类型,即确定钩子监听哪种消息, 可以监视窗口过程,也监视消息队列。上面的代码中设为2,即监听键盘消息并且是线程钩子,如果是全局钩子监听键盘消息应设为13,线程钩子监听鼠标消息设为7,全局钩子监听鼠标消息设为14。

代码为5,即C++中的WH_CBT (WH_CBT 当Windows激活、产生、释放(关闭)、最小化、最大化或改变窗口时都将触发此事件)

lpfn 钩子回调的地址指针。根据钩子类型,设置不同的回调函数。如果threadId参数为0 或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,lpfn可以指向当前进程的一段钩子回调代码。钩子函数的入口地址,当钩子钩到任何消息后立刻调用这个函数。

hInstance 应用程序(dll)实例的句柄。标识包含lpfn所指的回调的DLL。如果threadId 表示当前进程创建的一个线程,而且子程代码位于当前进程,hInstance必须为NULL(即线程钩子传null)。

threadId 设置钩子的线程ID,如果为0 则设置为全局钩子

上面代码中的SetWindowsHookEx方法安装的是线程钩子,用GetCurrentThreadId()函数得到当前的线程ID,钩子就只监听当前线程的键盘消息。

UnhookWindowsHookEx (int idHook) 函数用来卸载钩子,卸载钩子与加入钩子链表的顺序无关,并非后进先出

posted @   stweily  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示