【问题标题】:SetWindowsHookEx does not work in C# .net core?SetWindowsHookEx 在 C# .net 核心中不起作用?
【发布时间】:2018-08-21 10:47:59
【问题描述】:

我正在使用以下代码尝试获取操作系统范围的键盘输入,但没有成功:

using System;

using System.Diagnostics;
using System.Runtime.InteropServices;

class InterceptKeys

{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private static LowLevelKeyboardProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static void Main()

    {
        _hookID = SetHook(_proc);

        while(true)
            continue;
    }

    private static IntPtr SetHook(LowLevelKeyboardProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())

        using (ProcessModule curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                GetModuleHandle(curModule.ModuleName), 0);
        }
    }
    private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            Console.WriteLine(vkCode);
        }

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

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc 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 IntPtr GetModuleHandle(string lpModuleName);

}

HookCallback 根本没有被调用。我怀疑它试图只监听一个不存在的表单,而不是在系统范围内运行。

【问题讨论】:

  • SetWindowsHookEx的返回值是多少?
  • 既然您的应用程序是特定于 Windows 的,为什么还要限制自己使用 .NET Core?
  • 所有这些函数在失败时返回IntPtr.Zerofalse。您可以使用throw new Win32Exception() 将错误返回转换为可读异常。请注意,至少可以说,来自托管代码的全局挂钩相当棘手(甚至来自非托管代码——SetWindowsHookEx 的“备注”部分有 8 个段落是有原因的)。那是在将 .NET Core 投入其中之前。
  • 您需要在调用 SetWindowsHookEx 的线程中运行消息循环。在您的代码中,将无限循环while(true) 替换为消息循环。

标签: c# windows winforms hook


【解决方案1】:

低级 Windows 挂钩在内部使用 Windows 消息传递。调用SetWindowsHookEx的线程最后必须有消息循环,允许调用HookCallback函数。在 C++ 中的消息循环是这样的:

MSG msg;
BOOL result;

for (;;)
{
    result = GetMessage(&msg, nullptr, 0, 0);

    if (result <= 0)
    {
        break;
    }

    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

找到GetMessageTranslateMessageDispatchMessageMSG 的所有必需的 PInvoke 定义,将此代码转换为 C# 并将其放置在您的无限循环 while(true) 中。您可以在 PInvoke.Net 上找到所有这些内容,另请参阅此 Microsoft 论坛讨论:

控制台键盘钩子没有被调用

https://social.msdn.microsoft.com/Forums/vstudio/en-US/ed5be22c-cef8-4615-a625-d05caf113afc/console-keyboard-hook-not-getting-called?forum=csharpgeneral

【讨论】:

    【解决方案2】:

    我显然已经很晚了,但只是希望我能提供帮助(如果 OP 还没有他/她需要的帮助)所以我发布了我的答案。

    在 MSDN 文档中说,当你想设置系统范围的钩子时,你必须给 hMod 参数 a

    包含 lpfn 指向的钩子过程的 DLL 句柄 参数

    如果 dwThreadId 参数为零或指定一个标识符 由不同进程创建的线程,lpfn参数必须指向 到 DLL 中的挂钩过程

    但是,看看这个:

    SetWindowsHookEx(2, kbdHookProc, GetModuleHandle("user32"), 0)

    kbdHookProc 是我的 C# winforms 应用程序中的一个函数,但我在 hMod 参数中给出的值是通过 GetModuleHandle 加载 user32.dll 获得的 hinstance。我正在使用键盘挂钩 (WH_KEYBOARD) 来监视大写锁定、数字锁定和滚动锁定键的锁定。不要问我为什么这样做,它会起作用,或者为什么会起作用,因为我不知道,但是,是的,它起作用了!

    【讨论】:

      猜你喜欢
      • 2018-04-04
      • 2017-06-03
      • 2021-01-30
      • 2021-06-18
      • 2023-03-07
      • 2019-12-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多