可以使用Winkey + L的低级键盘挂钩/ SendInput? (工作站锁定在Vista及更高版本中被截获)

我正在开展一个名为UAWKS (非官方Apple无线键盘支持)的项目,帮助Windows用户使用Apple的蓝牙键盘。 UAWKS的主要目标之一是使用Ctrl交换Cmd键(在Windows中表现为Winkey ),允许用户为复制执行Cmd + C ,为新选项卡执行Cmd + T等。

它目前使用AutoHotkey开发,在Windows XP下运行良好。 但是,在Vista和Windows 7上, Cmd + L会导致问题:

  • 无论低级键盘挂钩如何, Win + L总是被Windows拦截并且通常锁定工作站……
  • 您可以使用此注册表黑客禁用工作站锁定,但按下Win + L仍然无法在AHK中反弹
  • 按下Win + L将Winkey保持在Keydown状态,直到下一个(额外的)Winkey Up。 模拟Keyup事件似乎也不起作用!

似乎Win + L是一个特殊的和弦,让其他一切混乱。

我查看了AHK源代码,他们试图在keyboard_mouse.cpp中的SendKey()中解决这个问题(在SendKey()中的第883行附近),但它不起作用。 我在C#中编写了自己的低级键盘钩子应用程序,我也看到了同样的问题。

有没有其他人遇到这个? 有解决方法吗?

我想出了一种在C#中实现这一目标的方法。 可能的Win + L按键序列涉及四种状态(None, WinWin + LL )。 每当达到Win + L状态时,设置一个标志(下面的“winLSet”)。 每当所有的钥匙都被释放后,我们会检查这个标志并模拟按下它是否已经设定好。

最后一个难题是在CtrlL (无KeyDown)之前模拟WinKey的KeyUp。 我在AutoHotkey中尝试过类似的方法,它从来没有用过,但它似乎在这里工作得很好。

代码如下。 如果您打算使用此代码,请参阅底部的解释性说明。

 public partial class MainWindow : Window { LowLevelKeyboardHook hook; bool winKeyDown; bool lKeyDown; bool winLSet; public MainWindow() { InitializeComponent(); hook = new LowLevelKeyboardHook(); hook.KeyDown += OnKeyDown; hook.KeyUp += OnKeyUp; } void OnKeyDown(object sender, LowLevelKeyEventArgs e) { e.EventHandled = true; switch (e.Key) { case Key.L: lKeyDown = true; UpdateWinLState(); e.EventHandled = winKeyDown; break; case Key.LWin: winKeyDown = true; UpdateWinLState(); InputSimulator.SimulateKeyDown(VirtualKeyCode.LCONTROL); break; case Key.LeftCtrl: InputSimulator.SimulateKeyDown(VirtualKeyCode.LWIN); break; default: e.EventHandled = false; break; } } void OnKeyUp(object sender, LowLevelKeyEventArgs e) { e.EventHandled = true; switch (e.Key) { case Key.L: lKeyDown = false; UpdateWinLState(); e.EventHandled = winKeyDown; break; case Key.LWin: winKeyDown = false; UpdateWinLState(); InputSimulator.SimulateKeyUp(VirtualKeyCode.LCONTROL); break; case Key.LeftCtrl: InputSimulator.SimulateKeyUp(VirtualKeyCode.LWIN); break; default: e.EventHandled = false; break; } } void UpdateWinLState() { if (winKeyDown && lKeyDown) { winLSet = true; } else if (!winKeyDown && !lKeyDown && winLSet) { winLSet = false; InputSimulator.SimulateKeyUp(VirtualKeyCode.LWIN); InputSimulator.SimulateModifiedKeyStroke( VirtualKeyCode.LCONTROL, (VirtualKeyCode)'L'); } } } 

对于后代:请注意,此代码使用InputSimulator和LowLevelKeyboardHook,它们不是来自.NET Framework。 LowLevelKeyboardHook是我之前写的一个类,它将全局KeyDown和KeyUp事件公开为C#事件。 这里有类似的例子, 这里可以找到一堆。

还要注意我正在使用System.Windows.Input.Key,而不是System.Windows.Forms.Keys,这可能会让一些人感到困惑。 System.Windows.Input.Key是.NET 3.0及更高版本中新的键枚举,而System.Windows.Forms.Keys是Windows Forms中的旧枚举。

我试图使用Windows输入模拟器库中断Windows键。 这是我的回调:

 private static unsafe IntPtr HookCallback( int nCode, IntPtr wParam, IntPtr lParam ) { if( nCode >= 0 && ( wParam == (IntPtr)WM_KEYDOWN ) ) { var replacementKey = (KBDLLHOOKSTRUCT*)lParam; if( replacementKey->vkCode == (int)VirtualKeyCode.LWIN ) { InputSimulator.SimulateKeyDown( VirtualKeyCode.SHIFT ); return (IntPtr)1; } } return CallNextHookEx( m_HookID, nCode, wParam, lParam ); } 

使用此挂钩,我的左侧Windows键在Win XP下充当shift键(已实现和预期)。
WinKey + l只返回L

编辑 :但是,我可以确认您的观察,此代码在Windows 7下不再起作用:/抱歉,我无法再帮助您了。

如果您能够检测到键Cmd + L ,您可以直接锁定工作站而无需转发Winkey + L吗? 你可以用API LockWorkstation (或rundll32.exe user32.dll,LockWorkStation )来做到这rundll32.exe user32.dll,LockWorkStation