【问题标题】:ExecutionEngineException on LowLevel Keyboard Hook低级键盘挂钩上的 ExecutionEngineException
【发布时间】:2021-09-08 11:55:41
【问题描述】:

我目前正在尝试使用 wpf 在 .net 5 中使用 Global LowLevel 键盘挂钩,但一段时间后我总是收到 ExecutionEngineException 并且我的应用程序崩溃了。 According to the docs,这个异常不应该再发生了,但对我来说确实如此。它也不是特定于我的项目,因为最小的 WPF .net 5 项目也会在一些关键混搭后崩溃。

有谁知道如何解决或解决这个问题?

主窗口的示例代码:

public partial class MainWindow : Window {
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_SYSKEYDOWN = 0x0104;

    private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr 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)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
        IntPtr wParam, IntPtr lParam);
    
    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
    static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);


    private IntPtr _hook;


    public bool Hook() {
        if (_hook == IntPtr.Zero) {
            var modPtr = LoadLibrary("user32.dll");

            _hook = SetWindowsHookEx(WH_KEYBOARD_LL, Handler, modPtr, 0);
        }

        return _hook != IntPtr.Zero;
    }

    private IntPtr Handler(int code, IntPtr param, IntPtr lParam) {
        return CallNextHookEx(_hook, code, param, lParam);
    }

    public MainWindow() {
        InitializeComponent();
        Hook();
    }
}

我得到的调用栈:

WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(ref System.Windows.Interop.MSG msg, System.IntPtr hwnd, int minMessage, int maxMessage)
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame)
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame)
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run()
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)
PresentationFramework.dll!System.Windows.Application.Run()
EngineExecutionError.dll!EngineExecutionError.App.Main()

【问题讨论】:

  • .NET 的默认调用约定是否与低级键盘挂钩 API 的约定所规定的调用约定相匹配?
  • 另外,将 user32.dll 的模块句柄作为第三个参数传递给SetWindowsHookEx 的原因是什么?
  • user32.dll 的基本原理:stackoverflow.com/questions/17897646/… 我之前在当前模块上使用了 GetModuleHandle,结果相同,它也“有效”,所以我没有改回来
  • 两者都是错误的。阅读documentation
  • 默认的调用约定是 WinApi,它指的是 windows 上的 stdcall,这是 win32 api 的默认调用 (docs.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-160)

标签: c# wpf winapi .net-5


【解决方案1】:

正如 cmets 所指出的,您不应将 lambda 或委托直接传递给 Win32 Api 调用(或任何 C api 调用)。解决方法是制作一个包含对该函数的引用的静态变量

private static LowLevelKeyboardProc _handler = Handler;
[...]
_hook = SetWindowsHookEx(WH_KEYBOARD_LL, _handler, modPtr, 0);

或者固定委托,所以 CLR 不会移动它,这是我选择的。

private LowLevelKeyboardProc _handler;
private GCHandle _gcHandler;

public KeyboardHook() {
    _handler = Handler;
    _gcHandler = GCHandle.Alloc(_handler);
}

[...]

_hook = SetWindowsHookEx(WH_KEYBOARD_LL, _handler, modPtr, 0);

对于低级挂钩,您似乎可以将modPtr 设置为LoadLibrary("user32.dll")GetModuleHandle(null)IntPtr.Zero。似乎与所有这些都有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-21
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多