views:

1889

answers:

3

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;
+2  A: 

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.

Daniel LeCheminant
Would this work in the same fashion?[Flags()]public enum KeyModifiers{ None = 0, Alt = 1, Control = 2, Shift = 4, Windows = 8}The part that confuses me the most is the modifiers argument. How does fsModifiers know that 0x2 == the CTRL key? Is that an internal func. from the dll api?
Yes, that is internal to the API.
Daniel LeCheminant
And yes, that should work the same way.
Daniel LeCheminant
And does it only contain keys for 1, 2, 4, 8? What are the other keys in between like 3, 5, 6, and 7? What if I wanted to use the L Shift or Right Shift specifically? MSDN says: VK_LSHIFT (0xA0) == Left SHIFT key and VK_RSHIFT (0xA1) == Right SHIFT key;Is there any info for all possible flags?
@Mike: Remember, each key is mapped to a single bit. (1, 2, 4, 8 all have a single bit set) 3 has two bits set, and would represent ALT and CTRL being pressed together (3 == ALT | CTRL)
Daniel LeCheminant
A: 

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.

Tommy Hui
+1  A: 

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.

scurial