Question

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;
Was it helpful?

Solution

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 enums 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:

  • The API doesn't have to take an individual boolean parameter for every modifier key
  • The API can be expanded to include additional modifier keys without changing the interface

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.

OTHER TIPS

1, 2, 4, 8 are used to represent single bits. 1 is the least significant bit will be on. Adding 2 will turn on the 2nd least significant bit, etc. Thus you can set multiple flags independently of each other.

1 = 0001
2 = 0010
4 = 0100
8 = 1000

Thus if you want to set the variables represented by 2 and 4 to be true you would use 0110 = 6.

If you want to know more about the RegisterHotKey API, take a look here:

API Help

Also, download the Platform SDK to look at WinUser.h for the definition values for MOD_ALT, MOD_CONTROL, etc.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top