Search code examples
c#enumspinvokeflags

When using RegisterHotKey api call, why does it accept 1, 2, 4, and 8 as modifiers?


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;

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.