I reference to this post;
http://www.pinvoke.net/default.aspx/user32/RegisterHotKey.html
#region fields
public static int MOD_ALT = 0x1;
public static int MOD_CONTROL = 0x2;
public static int MOD_SHIFT = 0x4;
public static int MOD_WIN = 0x8;
public static int WM_HOTKEY = 0x312;
#endregion
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int keyId;
public static void RegisterHotKey(Form f, Keys key)
{
int modifiers = 0;
if ((key & Keys.Alt) == Keys.Alt)
modifiers = modifiers | WindowsShell.MOD_ALT;
if ((key & Keys.Control) == Keys.Control)
modifiers = modifiers | WindowsShell.MOD_CONTROL;
if ((key & Keys.Shift) == Keys.Shift)
modifiers = modifiers | WindowsShell.MOD_SHIFT;
Keys k = key & ~Keys.Control & ~Keys.Shift & ~Keys.Alt;
Func ff = delegate()
{
keyId = f.GetHashCode(); // this should be a key unique ID, modify this if you want more than one hotkey
RegisterHotKey((IntPtr)f.Handle, keyId, modifiers, (int)k);
};
f.Invoke(ff); // this should be checked if we really need it (InvokeRequired), but it's faster this way
}
My question is, how does RegisterHotKey
api know that 1
, 2
, 4
, 8
are windows keys? Because the key codes for ctrl, shift, and menu (alt) give back totally different values for the keys? And what exactly is going on in the RegisterHotKey
function where it's checking for:
if ((key & Keys.Control) == Keys.Control)
modifiers = modifiers | WindowsShell.MOD_CONTROL;
and what is it doing here?
Keys k = key & ~Keys.Control & ~Keys.Shift & ~Keys.Alt;
MOD_ALT
, MOD_CONTROL
, etc don't have any relationship to the keycodes of the associated keys.
You're seeing an enum
type used to represent a set of flags. This is a particularly compact way of representing states that represent combinations of things (like modifier keys being simultaneously pressed, file access permissions, etc)
When enum
s are used this way, each bit of a variable of the enumeration type can be used to indicate that a particular "flag" is set.
// Note that powers of 2 are used; each value has only a single bit set
public static int MOD_ALT = 0x1; // If bit 0 is set, Alt is pressed
public static int MOD_CONTROL = 0x2; // If bit 1 is set, Ctrl is pressed
public static int MOD_SHIFT = 0x4; // If bit 2 is set, Shift is pressed
public static int MOD_WIN = 0x8; // If bit 3 is set, Win is pressed
// If we wanted to represent a combination of keys:
int altAndControl = MOD_ALT | MOD_CONTROL; // == 3
int controlAndShift = MOD_CONTROL | MOD_SHIFT; // == 6
This has two advantages:
Bitwise &
s and |
s can be used to determine which flags are set in a value, and to set or unset a flag in a value.
The code you've asked about does exactly this:
if ((key & Keys.Control) == Keys.Control)
modifiers = modifiers | WindowsShell.MOD_CONTROL
is saying "If the key has the Control
bit set, then set the control bit in modifiers
"
Keys k = key & ~Keys.Control & ~Keys.Shift & ~Keys.Alt;
is saying "k
is assigned key
with the Control
, Shift
and Alt
flags cleared"
I'm not sure why the contributors to pinvoke chose to use constants; you can just as easily use a proper enum:
[Flags]
public enum Modifiers
{
None = 0,
Alt = 1,
Control = 2,
// ...
}
My answer to a similar question has some more details on how flags work, and more examples.