title: SHIFT Shortcut being displayed as MAJ for culture "nl-BE" when it should be SHIFT (C#.NET)
body: <p>After a long discussion with Infragistics it appears that ShortCuts with SHIFT are displayed as MAJ in my culture "nl-BE". First of all, the culture "nl-BE" and AZERTY is somewhat of a strange thing. Read <a href="http://en.wikipedia.org/wiki/AZERTY" rel="nofollow">http://en.wikipedia.org/wiki/AZERTY</a> if want to know more. The important quote is:</p>
  
  <blockquote>
    <p>The other keys are identical, even though traditionally the names of
    special keys are printed on them in English. This is because Belgium
    is predominantly bilingual (French-Dutch) and officially trilingual (a
    third language, German, is spoken in the East Cantons).</p>
  </blockquote>
  
  <p>So MAJ is printed as SHIFT. In Office for instance, Shortcuts with SHIFT are displayed as SHIFT. In the Infragistics controls however they are displayed as MAJ. And this frustrates our customers.</p>
  
  <p>So, after a discussion with Infragistics they claim that it's a Windows Api call that is returning the MAJ instead of SHIFT. I have gotten a sample project from them which shows the behavior. So now my question is why the Windows Api call doesn't return SHIFT, and if it's normal, then how does Office do it to display it correct?</p>
  
  <p>The code to get the text of the key is :</p>
  
  <pre><code>NativeWindowMethods.GetKeyNameText((int)scanCode, sb, 256);
  </code></pre>
  
  <p>and</p>
  
  <pre><code>class NativeWindowMethods
  {
      #region MapVirtualKey
      [DllImport("user32.dll")]
      internal static extern int MapVirtualKey(uint uCode, uint uMapType);
      #endregion //MapVirtualKey
  
      #region GetKeyNameText
      [DllImport("user32.dll", CharSet = CharSet.Auto)]
      internal static extern int GetKeyNameText(
          int lParam, 
          [MarshalAs(UnmanagedType.LPWStr), Out]System.Text.StringBuilder str, 
          int size);
      #endregion //GetKeyNameText
  }
  </code></pre>
  
  <p>In case of the Shiftkey, the scancode is 2752512 (2a) and MAJ is returned.</p>
  
  <p>So, what are my questions?</p>
  
  <ul>
  <li>Is it normal that MAJ is returned for the culture "nl-BE"? Or is it a bug in user32.dll?</li>
  <li>If Office gets it right, isn't it up to Infragistics to also get it right?</li>
  <li>Does Infragistics use the correct user32.dll api call?</li>
  </ul>
  
  <p>For completeness I'll paste the full code for the Utilities class. From the Form next call is done:</p>
  
  <p>systemLocalizedString = Utilities.GetLocalizedShortcutString(shortcut);</p>
  
  <p>With shortcut = ShiftF12. After the call, systemLocalizedString is equal to "MAJ+F12".</p>
  
  <p>UPDATE: With the help of Hans Passant I downloaded the Microsoft Keyboard Layout Creator and exported my current Keyboard Layout. In the .klc file there's no MAJ to be found, only Shift (2a Shift  for instance). So why does the user32.dll return MAJ? Even weirder is that when I make a copy of the .klc file and install it as a new keyboard, then suddenly the user32.dll does return Shift for that newly installed keyboard (while it's an exact copy).</p>
  
  <p>Utilities.cs:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Windows.Forms;
  using System.Runtime.InteropServices;
  
  namespace WindowsFormsApplication1
  {
      class Utilities
      {
  
          #region GetLocalizedShortcutString
  
          /// &lt;summary&gt;
          /// Returns the localized string for the specified &lt;b&gt;Shortcut&lt;/b&gt;
          /// &lt;/summary&gt;
          /// &lt;param name="shortcut"&gt;Shortcut to localize&lt;/param&gt;
          /// &lt;param name="separator"&gt;Character used to separate multiple keys in the shortcut&lt;/param&gt;
          /// &lt;returns&gt;A string containing the localized description of the shortcut based on the currently mapped keyboard layout&lt;/returns&gt;
          public static string GetLocalizedShortcutString(Shortcut shortcut, char separator = '+')
          {
              if (shortcut == Shortcut.None)
                  return string.Empty;
  
              return GetLocalizedKeyString((Keys)shortcut, separator);
          }
          #endregion //GetLocalizedShortcutString
  
          #region GetLocalizedKeyString
  
          /// &lt;summary&gt;
          /// Returns the localized string for the specified &lt;b&gt;Keys&lt;/b&gt;
          /// &lt;/summary&gt;
          /// &lt;param name="keys"&gt;Keys to localize&lt;/param&gt;
          /// &lt;param name="separator"&gt;Character used to separate multiple keys&lt;/param&gt;
          /// &lt;returns&gt;A string containing the localized description of the keys based on the currently mapped keyboard layout&lt;/returns&gt;
          public static string GetLocalizedKeyString(Keys keys, char separator)
          {
              bool alt = ((long)keys &amp; (long)Keys.Alt) != 0;
              bool ctrl = ((long)keys &amp; (long)Keys.Control) != 0;
              bool shift = ((long)keys &amp; (long)Keys.Shift) != 0;
  
              // get the key involved
              long value = (long)keys &amp; 0xffff;
  
              Keys key = (Keys)Enum.ToObject(typeof(Keys), value);
              System.Text.StringBuilder sb = new System.Text.StringBuilder();
  
              if (alt &amp;&amp; key != Keys.Menu)
              {
                  sb.Append(GetLocalizedKeyStringHelper(Keys.Menu));
                  sb.Append(separator);
              }
  
              if (ctrl &amp;&amp; key != Keys.ControlKey)
              {
                  sb.Append(GetLocalizedKeyStringHelper(Keys.ControlKey));
                  sb.Append(separator);
              }
  
              if (shift &amp;&amp; key != Keys.ShiftKey)
              {
                  sb.Append(GetLocalizedKeyStringHelper(Keys.ShiftKey));
                  sb.Append(separator);
              }
  
              sb.Append(GetLocalizedKeyStringHelper(key));
              return sb.ToString();
          }
          #endregion //GetLocalizedKeyString
  
          #region GetLocalizedKeyStringHelper
          private static string GetLocalizedKeyStringHelper(Keys key)
          {
              string localizedKey = GetLocalizedKeyStringUnsafe(key);
  
              if (localizedKey == null || localizedKey.Length == 0)
                  return key.ToString();
  
              return localizedKey;
          }
          #endregion //GetLocalizedKeyStringHelper
  
          #region GetLocalizedKeyStringUnsafe
          private static string GetLocalizedKeyStringUnsafe(Keys key)
          {
              // strip any modifier keys
              long keyCode = ((int)key) &amp; 0xffff;
  
              System.Text.StringBuilder sb = new System.Text.StringBuilder(256);
  
              long scanCode = NativeWindowMethods.MapVirtualKey((uint)keyCode, (uint)0);
  
              // shift the scancode to the high word
              scanCode = (scanCode &lt;&lt; 16);
  
              if (keyCode == 45 ||
                  keyCode == 46 ||
                  keyCode == 144 ||
                  (33 &lt;= keyCode &amp;&amp; keyCode &lt;= 40))
              {
                  // add the extended key flag
                  scanCode |= 0x1000000;
              }
  
              NativeWindowMethods.GetKeyNameText((int)scanCode, sb, 256);
              return sb.ToString();
          }
          #endregion //GetLocalizedKeyStringUnsafe
      }
  
      class NativeWindowMethods
      {
          #region MapVirtualKey
          [DllImport("user32.dll")]
          internal static extern int MapVirtualKey(uint uCode, uint uMapType);
          #endregion //MapVirtualKey
  
          #region GetKeyNameText
          [DllImport("user32.dll", CharSet = CharSet.Auto)]
          internal static extern int GetKeyNameText(int lParam, [MarshalAs(UnmanagedType.LPWStr), Out]System.Text.StringBuilder str, int size);
          #endregion //GetKeyNameText
      }
  }
  </code></pre>