问题
I work on a project called UAWKS (Unofficial Apple Wireless Keyboard Support) that helps Windows users use Apple's bluetooth keyboard. One of the main goals of UAWKS is to swap the Cmd key (which behaves as Winkey in Windows) with Ctrl, allowing users to do Cmd+C for copy, Cmd+T for new tab, etc.
It is currently developed using AutoHotkey, which worked pretty well under Windows XP. However, on Vista and Windows 7, Cmd+L causes problems:
- Regardless of low-level keyboard hooks, Win+L is always intercepted by Windows and normally locks the workstation...
- You can disable workstation locking with this registry hack, but pressing Win+L still can't be rebound in AHK
- Pressing Win+L leaves Winkey in the Keydown state until the next (additional) Winkey Up. Simulating a Keyup event doesn't seem to work either!
It seems that Win+L is a special chord that messes everything else up.
I've looked through the AHK source code, and they try to address this problem in SendKey()
in keyboard_mouse.cpp (near line 883 in v1.0.48.05), but it doesn't work. I wrote up my own low-level keyboard hook application in C#, and I see the same problem.
Has anyone else run into this? Is there a workaround?
回答1:
I figured out a way to do this in C#. There are four states involved in a possible Win+L keypress sequence (None, Win, Win+L, L). Whenever the Win+L state is reached, set a flag ("winLSet" below). Whenever all of the keys have been released, we check for this flag and simulate the press if it's been set.
The final piece of the puzzle is to simulate the WinKey's KeyUp before the Ctrl-L (no KeyDown). I've tried similar approaches in AutoHotkey and it never worked, but it seems to work perfectly here.
The code is below. Please see explanatory notes at the bottom if you plan to use this code.
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');
}
}
}
For posterity: please note that this code uses InputSimulator and LowLevelKeyboardHook, which are not from the .NET Framework. LowLevelKeyboardHook is a class I wrote a while back that exposes global KeyDown and KeyUp events as C# events. There are similar examples here, here, and a bunch can be found here.
Also notice that I'm using System.Windows.Input.Key, not System.Windows.Forms.Keys, which could confuse some people. System.Windows.Input.Key is the new enumeration of keys in .NET 3.0 and above, while System.Windows.Forms.Keys is the old enumeration from Windows Forms.
回答2:
I tried to interrupt the windows key using the Windows Input Simulator library. This is my callback:
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 );
}
Using this hook my left windows key acts as a shift key (as implemented & expected) under Win XP.
Pressing WinKey + l returns just L
.
EDIT: However, I can confirm your observation, that this code does not work under Windows 7 anymore :/ Sorry, I can't help you any further.
回答3:
If you can detect the key Cmd+L could you just go ahead and lock the workstation without bothering to forward Winkey+L? you can do it with the API LockWorkstation (or rundll32.exe user32.dll,LockWorkStation
)
来源:https://stackoverflow.com/questions/2906179/low-level-keyboard-hooks-sendinput-with-winkeyl-possible-workstation-lockout