About keyboard layouts

A colleague from the helpdesk team recently asked if I could report the keyboad layout set before users log onto the computer.

I started digging into WMI classes by typing

Get-CimClass -ClassName *Keyboard*

Then did:

Get-CimInstance Win32_Keyboard

… and noticed the Layout property.

Both the MSDN page about the WMI Win32_Keyboard class…

…and the following powershell commands…


…confirmed that the layout property is returned as a string value.

The problem with this value is that it’s not human readable and represents actually a hexadecimal value.
Although I can find the mapping of hexadecimal values to a user friendly value in the following registry key HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Keyboard Layout\DosKeybCodes, I decided to go another way.

First, do you know that you can use DISM.exe locally:

Dism /online /Get-Intl

As you can see, it displays both the keyboard and input language. The input language is 407 (German) and the keyboard layout is 809 (English UK). This corresponds to what you find under the HKU\.DEFAULT\Keyboard Layout registry key:

As you can see, DISM is precise but doesn’t really help on the readability issue.
Here’s what I propose that is based on the Win32_Keyboard WMI Class:

#Requires -Version 2            
Function Get-KeyboardLayout {            
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty            
Begin {}            
Process {            
  $ComputerName | ForEach-Object -Process {            
        $Computer = $_            
        Write-Verbose -Message "Processing computer $Computer"            
        # Prepare a hashtable for splatting            
        $WMIHT = @{            
            ErrorAction = "Stop" ;            
            ComputerName = $Computer ;            
        # Add creds to hastable...            
        if ($PSBoundParameters.ContainsKey('Credential')) {            
            Write-Verbose "Adding Credentials to hashtable"            
            $WMIHT += @{ Credential = $Credential}            
        # ...but avoid WMI error: User credentials cannot be used for local connections            
        'localhost','.','','::1',$env:COMPUTERNAME | ForEach-Object {            
            if ($Computer -eq $_) {            
                Write-Verbose "Removing credentials as localhost is targeted"            
                $WMIHT.Remove('Credential') | Out-Null            
                $WMIHT.Remove('ComputerName') | Out-Null            
        try {            
            $ok = $true            
            $KB = Get-WmiObject -Class Win32_Keyboard -Property Layout @WMIHT            
            Write-Verbose -Message "Performing WMI query of computer $Computer"            
        } catch {            
            Write-Warning -Message "WMI query for computer $Computer failed because $($_.Exception.Message)"            
            $ok = $false            
        # Send a PSObject through the pipeline            
        if ($ok) {            
            New-Object -TypeName PSObject -Property @{            
                PSComputerName = $Computer            
                Layout = [System.Globalization.CultureInfo]([int32]"0x$($KB.Layout)")            
                HexValue = $KB.Layout            
            Write-Verbose -Message "WMI query of computer $Computer completed"            
End {}            
} # end of function            

Let’s see what this function does by turning on its verbose mode.

As you can see, the WMI Win32_Keyboard class reports the keyboard layout correctly for my localhost that had German as input language and English UK as keyboard layout.

3 thoughts on “About keyboard layouts

  1. Running your function returns an error in my PS 3.0 prompt (Windows 7 SP1 x64, PS 3.0):

    Der Wert “0x00000407 00000407” kann nicht in den Typ “System.Int32” konvertiert werden. Fehler: “Additional non-parsable characters are at
    the end of the string.”
    In Zeile:46 Zeichen:73
    + Layout = [System.Globalization.CultureInfo]([int32]”0x$($KB.Layo …
    + ~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastFromStringToInteger

    • Hallo,
      Ich habe kein Problem auf Windows 7 PS1 x64 mit Powershell 3.0.



      ist evaluiert auf dein Computer, es gibt 2 Werte aus. Das ist nicht normal.
      Bist du sicher dass du auf PS 3.0 bist?
      Du kannst im jedensfalls die Linie 41

      $KB = Get-WmiObject -Class Win32_Keyboard -Property Layout @WMIHT            

      mit diese ersetzen:

      $KB = Get-WmiObject -Class Win32_Keyboard @WMIHT

  2. Hey Emin,

    I had the chance to try it on another Windows 7 SP1 machine with PS 3.0 and it worked. Your suggestions didn’t work either on my computer. I guess my PS has gotten corrupted somehow. I’ll try to reinstall the windows management framework and test it again.

    Thanks for taking your time to help me troubleshoot the problem.



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s