While working on my WPF PowerShell console, I’m working on implementing PSRawUserInterface, and had to implement a method called ReadKey which returns a KeyInfo object. KeyInfo is a pretty simple struct class with four properties:

  • VirtualKeyCode
  • Character
  • ControlKeyState
  • KeyDown

So it doesn’t seem like it would be a real problem … just handle the KeyDown event or PreviewKeyDown event on the WPF control, right? Well, no. Because all of these just use a KeyEventArgs parameter which has a Key property which doesn’t map to a virtual key code (why isn’t the WPF Key enumeration in the right order? It’s ridiculous), and there’s no character information at all.

Thankfully, it is possible to get this information using PInvoke, but I wouldn’t want to try to do this in Silverlight. ;)

Here’s how it goes:

First, you need to get the VirtualKey code. Thankfully, there’s a simple class called KeyInterop, which exposes a static method VirtualKeyFromKey that gets us this information.

Then, we need to get the character. This is much trickier. First we have to get the keyboard state and then we have to map that VirtualKey we got in the first step to a ScanCode, and finally, convert all of that to Unicode, because .Net doesn’t really speak ASCII ;)

The rest of the code is pretty clear, I hope… you can find the current version of Keyboard.cs on CodePlex in the PoshCode source repository, but to make this complete, here’s the version as it stands now:


using System.Management.Automation.Host;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Input;

namespace Huddled.Interop
{
   public static class Keyboard
   {

      /// <summary>The set of valid MapTypes used in MapVirtualKey
      /// </summary>
      /// <remarks></remarks>
      public enum MapType : uint
      {
         /// <summary>uCode is a virtual-key code and is translated into a scan code.
         /// If it is a virtual-key code that does not distinguish between left- and
         /// right-hand keys, the left-hand scan code is returned.
         /// If there is no translation, the function returns 0.
         /// </summary>
         /// <remarks></remarks>
         MAPVK_VK_TO_VSC = 0x0,

         /// <summary>uCode is a scan code and is translated into a virtual-key code that
         /// does not distinguish between left- and right-hand keys. If there is no
         /// translation, the function returns 0.
         /// </summary>
         /// <remarks></remarks>
         MAPVK_VSC_TO_VK = 0x1,

         /// <summary>uCode is a virtual-key code and is translated into an unshifted
         /// character value in the low-order word of the return value. Dead keys (diacritics)
         /// are indicated by setting the top bit of the return value. If there is no
         /// translation, the function returns 0.
         /// </summary>
         /// <remarks></remarks>
         MAPVK_VK_TO_CHAR = 0x2,

         /// <summary>Windows NT/2000/XP: uCode is a scan code and is translated into a
         /// virtual-key code that distinguishes between left- and right-hand keys. If
         /// there is no translation, the function returns 0.
         /// </summary>
         /// <remarks></remarks>
         MAPVK_VSC_TO_VK_EX = 0x3,

         /// <summary>Not currently documented
         /// </summary>
         /// <remarks></remarks>
         MAPVK_VK_TO_VSC_EX = 0x4,
      }

      /// <summary>
      /// The ToUnicode function translates the specified virtual-key code and keyboard state
      /// to the corresponding Unicode character or characters. To specify a handle to the keyboard layout
      /// to use to translate the specified code, use the ToUnicodeEx function.
      /// </summary>
      ///
 
Reblog this post [with Zemanta]