question

dxp avatar image
0 Votes"
dxp asked XiaopoYang-MSFT commented

Fixed, keyboard layout-agnostic virtual key with Win32's WM_KEYDOWN

I'm looking for a way to reproduce JavaScript's KeyboardEvent.code's behavior with Win32's WM_KEYDOWN:

This ignores the users keyboard layout, so that if the user presses the key at the "Y" position in a QWERTY keyboard layout (near the middle of the row above the home row), this will always return "KeyY", even if the user has a QWERTZ keyboard (which would mean the user expects a "Z" and all the other properties would indicate a "Z") or a Dvorak keyboard layout (where the user would expect an "F").

So far, I've come pretty close to my goal by mapping the scan-code using MapVirtualKeyEx() with the default keyboard layout:

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
static unsafe nint WindowProc(HWND hWnd, WindowMessage msg, WPARAM wparam, LPARAM lparam)
{
   if (msg is WindowMessage.WM_KEYDOWN)
   {
      var scanCode = (uint) (((nint) lparam) >> 16) & 0xFF;
      var keyboardLayout = User32.LoadKeyboardLayout("00000409", 0); // "00000409" is supposed to be the default layout.
      var inputDebug = ((VK) (nuint) wparam).ToString();
      var mappingDebug = ((Key) User32.MapVirtualKeyEx(scanCode, MapTypes.MAPVK_VSC_TO_VK_EX, keyboardLayout)).ToString();
      //                    `-> where "Key" is my own "VK" enum, for the sake of naming convensions.

      Console.WriteLine("Input:  " + inputDebug);
      Console.WriteLine("Mapped: " + mappingDebug);
   }
   else if (msg is WindowMessage.WM_CHAR)
   {
      Console.WriteLine("Char:   " + (char) (nuint) wparam);
   }
}


Which gives the following kind of log:

Input:  VK_UP
Mapped: ArrowUp

Input:  VK_CONTROL
Mapped: ControlLeft

Input:  VK_OEM_7
Mapped: Backquote
Char:   ²

Input:  A
Mapped: KeyQ
Char:   a


However, LoadKeyboardLayout(), which I use to get an HKL to the default layout, has an annoying propension to update the language input setting of the the entire system, which is defo not what I want...

119856-image.png

And, I'm blocked there...

Is there a way to get an HKL representing the agnostic keyboard layout without actually loading it and changing the system's settings?

Or am I even looking in the right direction here?



dotnet-csharpwindows-api
image.png (12.7 KiB)
· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I did a test with a Low Level Keyboard hook (about the same code inside) and it does not seem to change the Keyboard layout globally on my OS (Windows 10, french), even when my app has the focus, as mentioned in the doc
Otherwise there is UnloadKeyboardLayout (but it removes the HKL...)

0 Votes 0 ·

Using LowLevelKeyboardProc will give me a KBDLLHOOKSTRUCT, but are its vkCode and scanCode fields really different from the equivalent I get from the WM_KEYDOWN message?

Will I be able to prevent the default behavior on WM_SYSKEYDOWN if I use LowLevelKeyboardProc? So far, I've been a property on my "routed" event:

else if (msg == WindowsMessages.WM_SYSKEYDOWN) {
   var e = new KeyDownEvent(TranslateKey(wparam, lparam));

   WindowContentControl.OnKeyDown(e);

   if (e.IsHandled) // Sys' keys are sent to the default procedure if the events are not handled.
      return 0;
   else
      return User32.DefWindowProc(hWnd, msg, wparam, lparam);
}
0 Votes 0 ·

wParam of WM_KEYDOWN is the virtual-key code of the nonsystem key.
The system key behavior is supported by DefWindowProc.

0 Votes 0 ·

Can we implement our own MapVirtualKeyEx function?

0 Votes 0 ·

0 Answers