gpt4 book ai didi

c# - VSTO Windows Hook keydown 事件调用 10 次

转载 作者:可可西里 更新时间:2023-11-01 09:23:12 27 4
gpt4 key购买 nike

因此,我一直在开发一个类来处理 VSTO 加载项中的 Kwyboard 输入,到目前为止,我一直在使用 Windows Hook 来完成此操作并取得了相对成功。

有这段代码:

    //.....
private const int WH_KEYBOARD = 2;
private const int WH_MOUSE = 7;

private enum WM : uint {
KEYDOWN = 0x0100,
KEYFIRST = 0x0100,
KEYLAST = 0x0108,
KEYUP = 0x0101,
MOUSELEFTDBLCLICK = 0x0203,
MOUSELEFTBTNDOWN = 0x0201,
MOUSELEFTBTNUP = 0x0202,
MOUSEMIDDBLCLICK = 0x0209,
MOUSEMIDBTNDOWN = 0x0207,
MOUSEMIDBTNUP = 0x0208,
MOUSERIGHTDBLCLK = 0x0206,
MOUSERIGHTBTNDOWN = 0x0204,
MOUSERIGHTBTNUP = 0x0205
}

private hookProcedure proc;

private static IntPtr hookID = IntPtr.Zero;

//Enganches

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr SetWindowsHookEx(int hookId, hookProcedure proc, IntPtr hInstance, uint thread);

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool unHookWindowsHookEx(int hookId);

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CallNextHookEx(IntPtr hookId, int ncode, IntPtr wparam, IntPtr lparam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string name);

[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetCurrentThreadId();

public CPInputListener() {

proc = keyBoardCallback;

hookID = setHook(proc);
}


private IntPtr setHook(hookProcedure procedure){

ProcessModule module = Process.GetCurrentProcess().MainModule;
uint threadId = (uint)GetCurrentThreadId();

return SetWindowsHookEx(WH_KEYBOARD, procedure, IntPtr.Zero, threadId);
}

public void stopListeningAll() {
unHookWindowsHookEx(WH_KEYBOARD);//For now
}


private IntPtr keyBoardCallback(int ncode, IntPtr wParam, IntPtr lParam) {

if (ncode >= 0) {
//LPARAM pretty useless

Keys key = (Keys)wParam;

KeyEventArgs args = new KeyEventArgs(key);

onKeyDown(args);//for now

}
return CallNextHookEx(hookID, ncode, wParam, lParam);
}
//....

我确实成功接收了键盘输入,但这是个大谜团;每次按下一个键,无论它有多快,事件 (onKeyDown) 都会准确调用 10 次,不多也不少。

如果长按该键,该事件将继续被调用,但会调用 10 次 10 次,而不是只调用一次。

到目前为止我已经尝试过

  1. 使用 wParam 在 Key Up 上调用所需的事件:似乎不起作用,在我看到的所有处理 Key down 和 up 事件的代码中,都使用了 IntPtr wParam,但是来自该变量我只能检索没有帮助的键码。
  2. 使用 lParamnCode:这些变量在这 10 次调用之间给出了不一致的值,ncode 倾向于检索 0 和 3,并且 lParam 一些值似乎是非托管内存地址...

我期待什么

我确实希望 onKeyDown 在按下键时只被调用一次,或者另一方面能够通过 on key up 调用该方法,我希望每次释放键时只调用一次。

如何绕过这个

如果我找不到合理的答案,我正在考虑使用自定义计时器来丢弃所有这些调用并仅使用最后一个,如果其他一切都失败了,你会推荐这个吗?

非常感谢!要快乐,要善良! :D

最佳答案

首先,您必须过滤正确的 ncode 以仅获取您应该处理的击键。 (例如,您不应该处理 HC_NOREMOVE。)
然后您必须使用 lParam 中的标志检查它是 KeyDown 还是 KeyUp 事件。

如果按键被长按,多个 KeyDown 事件已经被 Win32 合并为一个调用,因此您不必在这里做任何特殊的事情。但是,如果您只想获得最后一个 KeyUp 事件,那么您还必须检查 lParam 中的另一个标志。

因此,这是您需要更改的代码:

private IntPtr keyBoardCallback(int ncode, IntPtr wParam, IntPtr lParam)
{
// Feel free to move the const to a private field.
const int HC_ACTION = 0;
if (ncode == HC_ACTION)
{
Keys key = (Keys)wParam;
KeyEventArgs args = new KeyEventArgs(key);

bool isKeyDown = ((ulong)lParam & 0x40000000) == 0;
if (isKeyDown)
onKeyDown(args);
else
{
bool isLastKeyUp = ((ulong)lParam & 0x80000000) == 0x80000000;
if (isLastKeyUp)
onKeyUp(args);
}
}
return CallNextHookEx(hookID, ncode, wParam, lParam);
}

根据评论中的要求进行编辑:
不幸的是,这些参数的文档非常少。

可以找到一个“提示”不要处理除 HC_ACTION 之外的任何内容 here ,说明:

if (nCode < 0)  // do not process message
return ...;

// ...
switch (nCode)
{
case HC_ACTION:
// ... do something ...
break;

default:
break;
}
// ...
return CallNextHookEx(...);

这里还有一个支持声明:
Why does my keyboard hook receive the same key-up and key-down events multiple times?

lParam 的内容定义为as follows :

typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode;
DWORD scanCode;
DWORD flags;
DWORD time;
ULONG_PTR dwExtraInfo;
}

(提醒一下:DWORD 在 x86 和 x64 平台上的大小都是 4 bytes。)

可以找到lParam flags 的文档herehere .
在这个链接中描述了

  • 第 30 位 (=0x40000000) 是上一个键状态
    (1 如果键 按下,0 如果键 之前导致此调用的新关键状态)
  • bit 31 (=0x80000000) 是过渡状态
    (0 按键按下,1 按键释放现在)

术语“上一个关键状态”相当令人困惑,但实际上它恰好与当前状态相反(因为只有向上或向下,没有第三种状态)。

当“键盘的自动重复功能”被激活时,即当按键被按下足够长的时间时,转换状态尤其重要。

可以找到另一个示例(使用 VC7)here :

if (HIWORD (lParam) & 0xC000)
// Key up without autorepeat
else
// Key down

0xC000 就是 0x4000 || 0x8000 并定义键已释放并已创建键弹起事件。

总而言之,这很令人困惑,但仍然是事实。
也许还有其他链接可以更好地描述这种情况,但我想在这样的时代,新的应用程序开发“应该在小型沙箱(如 UWP)中完成”,而 VSTO 肯定会死去为newer Office add-ins写在HTML and JavaScript ,没有人再那么关心低级钩子(Hook)了。

关于c# - VSTO Windows Hook keydown 事件调用 10 次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40599162/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com