- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在创建一个程序来监控全局控制 iTunes 的按键。它还有一些 WinForms(用于显示轨道信息和编辑选项)。
低级键盘 Hook 在一段时间内效果很好。如果我刚启动该程序,键盘 Hook 已设置并且 iTunes 打开。然后我打开记事本,可以非常快速地键入大量内容,每一个笔划都被捕获, Hook 函数最多花费 30 毫秒(并且大部分时间 <10 毫秒)。 Hook 函数只是将事件添加到队列中,该队列由另一个线程处理。它使用自己的 Application.Run() 在自己的高优先级线程上运行。
但是,如果我开始在 iTunes 中执行操作(例如在我的程序中生成事件的几次播放/暂停点击)或在程序中(例如打开选项窗口),则 Hook 函数将停止调用!即使从未使用过键盘(例如,启动,在 iTunes 中点击播放和暂停几次,然后按一个键),也会发生这种情况。
钩子(Hook)没有被调用的原因不是因为钩子(Hook)函数花费了太多时间。
当我调用 UnhookWindowsHookEx 时,它总是返回 true,无论是否仍在调用 Hook 函数。
那么,可能是什么原因呢?
一个想法(尽管我没有证据或解决方案)是托管线程不再是正确的 native 线程。我在我的程序中使用了许多(托管)线程,并且我读到单个 native 线程可以运行许多托管线程并且托管线程可以更改正在运行它的 native 线程。 Hook 是否可能仍在生成消息但将它们发送到错误的线程?如果是这种情况,我该如何解决?
编辑:钩子(Hook)和回调
我的 KeyMonitor 的一个略微精简的完成版本。为了清楚起见,它被剥离了。我删除了一些实用程序(如 Key 枚举的大部分值和 Keys 类的许多函数,如 ToString() 和 FromString())以及一些错误处理。
大部分重要的东西都在 KeyMonitor 类中。 KeyMonitor.Start() 为消息启动一个线程,KeyMonitor.HookThread() 是该线程并为消息循环创建 Hook 以及 Application.Run(),KeyMonitor.KeyboardHookProc() 是回调函数,而 KeyMonitor. HookEventDispatchThread() 用于调度回调记录的事件。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace KeyTest
{
enum Key : int
{
Shift = 0x10, Ctrl, Alt,
Left_Win = 0x5B, Right_Win,
Left_Shift = 0xA0, Right_Shift, Left_Ctrl, Right_Ctrl, Left_Alt, Right_Alt,
}
class Keys
{
[DllImport("user32.dll")]
private static extern int GetKeyboardState(byte[] pbKeyState);
public const int Count = 256; // vkCode are from 1 to 254, but GetKeyboardState uses 0-255
private readonly bool[] keys = new bool[Count];
public Keys() { }
private void DoModifier(Key x, Key l, Key r) { keys[(int)x] = keys[(int)l] || keys[(int)r]; }
private void DoModifiers()
{
DoModifier(Key.Shift, Key.Left_Shift, Key.Right_Shift);
DoModifier(Key.Ctrl, Key.Left_Ctrl, Key.Right_Ctrl);
DoModifier(Key.Alt, Key.Left_Alt, Key.Right_Alt);
}
private void DoModifier(Key x, Key l, Key r, Key k) { if (k == l || k == r) keys[(int)x] = keys[(int)l] || keys[(int)r]; }
private void DoModifiers(Key k)
{
DoModifier(Key.Shift, Key.Left_Shift, Key.Right_Shift, k);
DoModifier(Key.Ctrl, Key.Left_Ctrl, Key.Right_Ctrl, k);
DoModifier(Key.Alt, Key.Left_Alt, Key.Right_Alt, k);
}
public bool this[int i] { get { return this.keys[i]; } set { this.keys[i] = value; DoModifiers((Key)i); } }
public bool this[Key k] { get { return this.keys[(int)k]; } set { this.keys[(int)k] = value; DoModifiers(k); } }
public void LoadCurrentState()
{
byte[] keyState = new byte[Count];
if (GetKeyboardState(keyState) != 0)
for (int i = 0; i < Count; ++i)
keys[i] = (keyState[i] & 0x80) != 0;
DoModifiers();
}
}
static class KeyMonitor
{
#region Windows API
private delegate int HookProc(int nCode, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
private static extern int UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll")]
private static extern int CallNextHookEx(int idHook, int nCode, UIntPtr wParam, IntPtr lParam);
private const int WH_KEYBOARD_LL = 13;
private readonly static UIntPtr WM_KEYDOWN = new UIntPtr(0x100), WM_SYSKEYDOWN = new UIntPtr(0x104);
#endregion
public static event KeyEventHandler OverridingKeyChange;
public static event KeyEventHandler KeyChange;
private struct KeyEventData { public int vk; public bool down; }
private static int hook = 0;
private static Thread dispatchThread = null, hookThread = null;
private static Keys keys = new Keys();
private static Queue<KeyEventData> queue = new Queue<KeyEventData>();
private static void Enqueue(int vk, bool down)
{
lock (queue)
{
queue.Enqueue(new KeyEventData() { vk = vk, down = down });
Monitor.Pulse(queue);
}
}
public static Keys Keys { get { return keys; } }
public static void Start()
{
if (hook == 0)
{
dispatchThread = new Thread(HookEventDispatchThread);
hookThread = new Thread(HookThread);
hookThread.Priority = ThreadPriority.Highest;
dispatchThread.Start();
hookThread.Start();
}
}
public static void Stop()
{
if (hook != 0)
{
// Minimal cleanup...
UnhookWindowsHookEx(hook);
Application.Exit();
dispatchThread.Interrupt();
}
}
private static void HookThread()
{
hook = SetWindowsHookEx(WH_KEYBOARD_LL, new HookProc(KeyboardHookProc), IntPtr.Zero, 0);
if (hook == 0) { /* Handle error */ }
keys.LoadCurrentState();
Application.Run();
}
private static int KeyboardHookProc(int nCode, UIntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
Enqueue(Marshal.ReadInt32(lParam), wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN);
return CallNextHookEx(hook, nCode, wParam, lParam);
}
private static void HookEventDispatchThread()
{
for (; ; )
{
KeyEventData data;
lock (queue)
{
if (queue.Count == 0)
try
{
Monitor.Wait(queue);
}
catch (ThreadInterruptedException) { return; }
data = queue.Dequeue();
}
if (data.vk == -1)
{
// Done!
keys = new Keys();
queue.Clear();
return;
}
else if (keys[data.vk] == data.down)
continue;
keys[data.vk] = data.down;
KeyEventArgs e = new KeyEventArgs((System.Windows.Forms.Keys)data.vk);
if (OverridingKeyChange != null) OverridingKeyChange(null, e);
if (!e.Handled && KeyChange != null) KeyChange(null, e);
}
}
}
}
最佳答案
您需要将委托(delegate)保存到一个变量中,该变量将在您的应用程序期间继续存在。否则,委托(delegate)将被垃圾回收(奇怪的是应用程序没有崩溃!)。
static HookProc hookProc;
...
hookProc = new HookProc(KeyboardHookProc);
hook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, IntPtr.Zero, 0);
关于c# - 使用 SetWindowsHookEx 设置的低级键盘 Hook 停止在 C# 中调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6796795/
我遇到了一个问题。我正在使用 pyinstaller 将代码导出到 .exe。代码包括 tkinter、PIL 和 keyboard 模块。软件在我使用 Python 的 PC 上运行完美,而在没有
我正在尝试将 STM32F0-disco 用作 Windows PC 的键盘。正在打印的字符有问题。 下面的代码等到板载按钮被按下,然后应该打印一次这三个字符。 /* USER CODE BE
我想在单击键盘上的“完成”按钮时退出键盘。我怎样才能做到这一点?我有以下代码=> textView.returnKeyType = UIReturnKeyDone; 我有这个代码用于限制 TextVi
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 8 年前。 Improve this ques
我想调出一个用于输入电话号码的键盘。有没有办法在我自己的 Activity 中显示此对话框并捕获用户输入的内容。我需要能够控制用户点击调用时发生的情况。所以我很感兴趣自定义允许用户输入电话号码的 Ac
我希望软键盘位于我的布局之上,而不是在它弹出时四处移动我的布局。我该怎么做呢?我查看了 list ,但从未设置 android:windowSoftInputMode="adjustResize" 此
主题:将某些键替换为另一个键值。 例如如果我按 P,它应该是 F24。 当我尝试从 .ini 文件加载键值时, Hook 不再是全局的。它仅在 winapi 窗体处于焦点时才有效。 我的 DLL 代码
当我点击 EditText 时,是否可以用小写字母启动 android 键盘?基本上,我想要的是将 EditText 中的第一个字母小写,但也让用户可以根据需要将其变为大写...... (EditTe
我想监控 IOS 上的键盘隐藏按钮并触发一个事件。我在说: 我只想在用户按下实际按钮时进行监控。我不想要键盘隐藏时的事件。 最佳答案 用户键盘通知观察者.. 对于 swift NSNotificati
在经历了这一切之后就像认真的...... Easy way to dismiss keyboard? ...我有多个 TextFields 和一些 TextViews。有没有办法对所有文本字段进行批处
所以我想在我的应用程序中添加一个键盘,其中包含表情符号,就像 Whatsapp 或环聊一样。我怎样才能做到这一点?我想保留我的键盘,因为我只想添加标签来放置表情符号。我认为软键盘很容易支持它,但到目前
首先,我会让您知道,我在 StackOverflow 以及 Google 和 Github 上查看了很多很多不同的帖子。我已经到处寻找对我有帮助的任何。但是,似乎没有任何效果。它要么已经过时(超过 1
当按下相应的键时,我试图让我的游戏中的宇宙飞船 (PlayerShip.gif) 向左、向右、向上和向下移动。我知道我需要一个 keyboardListener 但我在弄清楚它的去向以及它是如何实际实
我正在尝试制作一个 HID USB 设备。我搜索了一下,发现键盘的输出有 8 个字节。第一个字节是修饰符,第二个字节是保留字节,其余 6 个字节是关键代码。我认为在某些情况下,例如“prtsc”,需要
我最近开始学习 Vim,在深入学习之前,我有一个问题需要回答。 使用 AZERTY 键盘,我是否应该重新映射命令和快捷方式的键以适应 QWERTY 键盘的键位置? 我之所以问这个,是因为显然在创建这些
我一直认为在使用 Dvorak 布局之前,我需要购买 Dvorak 键盘。但是我在亚马逊上找不到。仅仅是从 Qwerty 键盘上弹出键并移动它们吗? 最佳答案 为了帮助您了解键盘布局,您可以重新排列
我不敢相信我还没有找到任何关于此的文档,但我想知道如何命令键盘激活并接收来自它的输入。我可以找到在编辑文本字段时操作弹出键盘的所有示例。谢谢 最佳答案 您还可以使用 UIKeyInput 协议(pro
我有一个 UITextField,其中弹出的键盘已禁用其 Shift 键。键盘类型设置为 UIKeyboardTypeNamePhonePad,看起来应该允许大写。 如果我将键盘类型更改为“默认”但保
背景:我的表单有一个 TWebBrowser。我想用 ESC 关闭表单,但 TWebBrowser 吃掉了击键 - 所以我决定使用键盘 Hook 。 问题是表单可以同时在多个实例中打开。 无论我做什么
我需要(即客户要求)提供自定义键盘,供用户在文本字段和区域中输入文本。我已经有一些可以执行键盘操作并将测试附加到文本字段的东西,但是我想让它更通用并让它像标准的 iphone 键盘一样工作,即当用户选
我是一名优秀的程序员,十分优秀!