【问题标题】:Nunit not getting SetWindowsHookEx eventsNunit 没有收到 SetWindowsHookEx 事件
【发布时间】:2014-07-08 23:50:50
【问题描述】:

我有一个在生产中使用的 KeyboardHook。效果很好。昨天我想做一个单元测试来测试我的想法,我创建一个抽象类,该类具有与 HOOKPROC 委托匹配的虚拟方法。像这样..

public abstract class HookRuleBase : IDisposable
{
    private readonly IntPtr hookId;
    private static KeyboardProc thisDelegate;
    public HookRuleBase()
    {
        thisDelegate = new KeyboardProc(ProcessKey);
        IntPtr user = Kernel32.LoadLibrary("User32.dll");
        hookId = User32.SetWindowsHookEx(WH_KEYBOARD_LL, thisDelegate, user, 0);
        Console.WriteLine(hookId.ToInt32().ToString("X8"));
    }

    public void Dispose()
    {
        var disposed = User32.UnhookWindowsHookEx(hookId);
    }

    /// <summary>
    /// If your rule does not need to handle the key or the key's current state then call this base
    /// method.
    /// </summary>
    public virtual int ProcessKey(int code, System.UIntPtr wParam, ref KeyboardHookStruct lParam)
    {
        return User32.CallNextHookEx(hookId, code, wParam, ref lParam);
    }

    private const int WH_KEYBOARD_LL = 13;
}

我打算用它来替换我在课堂上的一些名为 KeyboardHook 的静态方法。我认为它遵循策略模式……我认为……任何人。所以我做了一个 NUnit 项目。做了一个非常简单的类来扩展我的 HookRuleBase

    private class HookRuleTest : HookRuleBase
    {
        public override int ProcessKey(int code, UIntPtr wParam, ref KeyboardHookStruct lParam)
        {
            Console.WriteLine("ProcessKey:code:{0}, wParam:{1:X8}, {2}", code, wParam.ToUInt32(), lParam.ToString());
            return base.ProcessKey(code, wParam, ref lParam);
        }
    }

我在测试中尝试了一些东西。首先我做了一个控制台哔声(我自己的一个音频队列。)然后做了一个线程睡眠 3 秒......什么都没有出现。所以我偷了一些我的代码来测试我的虚拟键盘类。它只不过是在另一个线程中启动的 Winforms 表单。所以我放入了创建所述钩子规则的加载事件,并在关闭时处理了它。运行我的测试输入 hello world 并且在控制台中没有任何内容:/ 所以作为最后的手段,我创建了一个新的 winforms 项目。复制了加载和关闭事件的代码,将项目更改为控制台项目,然后运行我的代码。开始打字,果然我开始将输出输出到我的控制台窗口。所以这让我发疯。我不知道它为什么这样做。该代码有效,但在 Nunit 中无效。有人可以指导我正确的方向,为什么它不起作用。它的作用就像钩子正在接收任何事件,但 SetWindowsHookEx 每次都返回一个有效指针。所以我不知道。请帮忙。

【问题讨论】:

    标签: c# winforms nunit keyboard-hook


    【解决方案1】:

    你犯了一个常见的错误,这个错误经常发生在低级钩子上。 Windows 不能随意回调您的 ProcessKey() 方法。您的线程必须处于明确定义的状态,在这种状态下,Windows 确信它是空闲的并且进行调用是安全的并且不会导致重入问题。

    这种情况最广为人知的是“泵送消息循环”。消息循环由 .NET 程序中的 Application.Run() 启动。并且启动它的线程不能被占用,它必须重新进入循环,等待 Windows 传递下一条消息。只有那么才能进行回调。

    您不会从 NUnit 获得消息循环,它会通过执行您的测试来占用线程。您不会从“在另一个线程中启动的 Winforms 表单”中获得它,那是错误的线程。你是从你的普通 Winforms 项目中得到它的,它的 UI 线程总是泵送一个消息循环,并且该线程花费 99.9% 的时间等待消息。

    因此,您所看到的完全是设计使然。您将在this answer 中找到一个线程的示例代码。在 Initialize() 方法的覆盖中创建您的 HookRuleTest 对象。

    【讨论】:

    • 一如既往,您的回答很棒。我发现了你谈论键盘钩子的另一篇文章,它让我改变了我在本机调用中定义的一些变量类型。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2021-01-05
    • 2013-11-25
    • 1970-01-01
    相关资源
    最近更新 更多