Search code examples
.netvb.netwinapiraw-inputkeyboard-layout

ToUnicodeEx function fails with any keyboard layout different than mine


I'm using ToUnicodeEx function to translate the virtual key code of a pressed key to its unicode character,

This is the function definition:

<DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True, CallingConvention:=CallingConvention.Winapi)>
Friend Shared Function ToUnicodeEx(
                       ByVal wVirtKey As UInteger,
                       ByVal wScanCode As UInteger,
                       ByVal lpKeyState As Byte(),
                       <Out, MarshalAs(UnmanagedType.LPWStr)>
                       ByVal pwszBuff As StringBuilder,
                       ByVal cchBuff As Integer,
                       ByVal wFlags As UInteger,
                       ByVal dwhkl As IntPtr
) As Integer
End Function

The function works perfect for my culture es-ES with my keyboard layout 3082, however, if instead specifying 3082 I try the code below specifying a 1033 keyboard layout to simulate a key pres for en-US culture, or whatever any other keyboard layout different than 3082, then ToUnicodeEx function will fail and returns 0, where a return value of 0 means this:

The specified virtual key has no translation for the current state of the keyboard. Nothing was written to the buffer specified by pwszBuff.

Dim buf As New StringBuilder(256)
Dim keyboardState As Byte() = New Byte(255) {}

If shift Then
    keyboardState(CInt(Keys.ShiftKey)) = &HFF
End If

If altGr Then
    keyboardState(CInt(Keys.ControlKey)) = &HFF
    keyboardState(CInt(Keys.Menu)) = &HFF
End If

InputDevice.NativeMethods.ToUnicodeEx(CUInt(Keys.A), 0UI, keyboardState,  buf,  256,  0UI, 
                                      New IntPtr(1033))

What I'm doing wrong?, How I can fix it?


Solution

  • Experimenting, reasearching, and with some fails, I found in the .Net framework class library the best and easier solution that does not involves P/Invoking.

    Just knowing a culturename, like en-US or es-ES with the Systwm.Windows.Forms.InputLanguage class we can retrieve the handle of the layout (the HKL)

    InputLanguage.FromCulture(New CultureInfo("en-US")).Handle
    

    Then, that value can be set for the dwHKL parameter of the ToUnicodeEx function, and it works perfect!.