Click here to Skip to main content
Click here to Skip to main content

Using multiple keyboards with different layouts on the same machine

By , 23 Oct 2007
 

image1.png

Introduction

My laptop has a Portuguese keyboard which I use when I am at home, but at work, I use an external keyboard with a Belgian layout. This is annoying because every time I switch keyboards, I need to go to every open program and manually change its input language with the language bar. Worse, when I start a new program, it starts with the default language, which means that sometimes I have to switch input language also.

One way to work around this problem could be to buy a new keyboard, but I like the IBM model M keyboard and I have yet to find a better keyboard.

Instead of doing that, I wrote a program that switches to the correct input language when I start using a keyboard.

Background

Although more than one keyboard may be connected to the same computer, the Win32 API treats them as if there was only one keyboard. A program receives key strokes from every keyboard, but has no way of knowing which keyboard sent a particular stroke.

Usually, this is exactly what the program wants. It is the role of the Operating System to shield the program from the complexity of dealing with multiple input devices. But in this case, we need to know which device has been used in order to switch the input language. Luckily, the raw input API exposes which device is sending the messages, which solves our problem.

How it works

Describing the raw input API is outside of the scope of this article. The MSDN documentation already provides plenty of information about it. We will stick to the essential. The API allows a program to register to receive notifications each time a keyboard sends an event. When the notification is received, the program checks if it comes from a different device than the previous one. In that case, the program selects the appropriate input language and broadcasts the WM_INPUTLANGCHANGEREQUEST message so that open programs change their input language. Additionally, the default input language is updated using the SystemParametersInfo function, which causes new programs to start with the correct input language.

When a keyboard is used for the first time, the program asks the user which language is to be used. When the program is closed, a file is saved in %USERPROFILE%\Local Settings\Application Data\RightKeyboard that contains the associations between the device identifiers and the input languages. When the program starts, that information is loaded so that the program already knows the correct language for any previously used keyboard.

Using the program

The program is easy to use. Simply run it and an icon will appear in the notification area. The icon allows to close the program when right-clicked. The first time a keyboard is used, a popup will appear with a list of the available input languages. Select the correct language, then click OK. From now on, each time that keyboard is used, every application will switch to that language. More input languages can be added in the Regional and Language Options on the Control Panel.

image2.png

Known Limitations

There are some limitations that more work could probably overcome:

  • Console windows do not switch language when they receive the WM_INPUTLANGCHANGEREQUEST message. Any such window that is already open when the program switches the input language will keep the current language.
  • When the program switches the input language, the currently focused window receives the key before the change. Because of that, the first key that is pressed on another keyboard is always incorrect, unless that particular key is the same on the previous language.
  • After hibernating, the device handles become invalid, which makes the program ask the language for a previously known keyboard.

History

  • 2007/10/24 - First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

AntoineAubry
Web Developer
Unknown
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionDvorak [modified]memberMember 992866720 Mar '13 - 12:30 
I am currently using two standard qwerty keyboards and I would like to have one use Dvorak. Is it possible to use your program to do this? I understand that it detects the type of keyboard being used but it says i'm using a standard keyboard. Can I manually tell it that I want to use Dvorak?

-- modified 20 Mar '13 - 18:46.
BugManageable errormemberGenesisAria27 Dec '12 - 1:16 
When I use this program, it works like a charm; however whenever I type on the other keyboard I get a .NET Framework error... If I hit Continue it works fine. This dialogue can be annoying.
 
See the end of this message for details on invoking 
just-in-time (JIT) debugging instead of this dialog box.
 
************** Exception Text **************
System.Runtime.InteropServices.COMException (0x80070000): The operation completed successfully. (Exception from HRESULT: 0x80070000)
   at RightKeyboard.MainForm.SetDefaultLayout(UInt16 layout)
   at RightKeyboard.MainForm.CurrentDeviceChanged(IntPtr hCurrentDevice)
   at RightKeyboard.MainForm.ProcessInputMessage(Message message)
   at RightKeyboard.MainForm.WndProc(Message& message)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
 

************** Loaded Assemblies **************
mscorlib
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.4984 (win7RTMGDR.050727-4900)
    CodeBase: file:///C:/Windows/Microsoft.NET/Framework64/v2.0.50727/mscorlib.dll
----------------------------------------
RightKeyboard
    Assembly Version: 1.0.0.0
    Win32 Version: 1.0.0.0
    CodeBase: file:///D:/Program%20Files/RightKeyboard.exe
----------------------------------------
System.Windows.Forms
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.4977 (win7RTMGDR.050727-4900)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Windows.Forms/2.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
System
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.4984 (win7RTMGDR.050727-4900)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
System.Drawing
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.4980 (win7RTMGDR.050727-4900)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Drawing/2.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
System.Xml
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.4927 (NetFXspW7.050727-4900)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Xml/2.0.0.0__b77a5c561934e089/System.Xml.dll
----------------------------------------
 
************** JIT Debugging **************
To enable just-in-time (JIT) debugging, the .config file for this
application or computer (machine.config) must have the
jitDebugging value set in the system.windows.forms section.
The application must also be compiled with debugging
enabled.
 
For example:
 
<configuration>
    <system.windows.forms jitDebugging="true" />
</configuration>
 
When JIT debugging is enabled, any unhandled exception
will be sent to the JIT debugger registered on the computer
rather than be handled by this dialog box.
I use Windows 7 Ultimate; I have thoroughly looked up how to deal with this kind of debugging, and the only solution seems to be enabling JIT debugging in Visual Studio (which I do not have and cannot install due to partition size restrictions)
I have also noticed that is only changes language (and the layout set as default for that language), which is fine.
❀桜舞う空~ ☯

QuestionVery Nice!membernever-met-me13 Sep '12 - 8:27 
This works quite well within the limitations.
Thanks for this little program.
 
One thing though I came across: Since I am in Austria my language was set to German (Austria) and when selecting that language the program crashed. I had to set it to German (Germany) for it to work.
GeneralMy vote of 3memberBurak Tunçbilek11 Aug '12 - 3:49 
thank you
QuestionA slight change?memberMember 833452930 Oct '11 - 16:15 
As far as I can tell, this code works by changing the language, not the layout -- the layout merely follows suit. For the situation you describe that's fine, but I wonder if you can help me in mine...
I have 2 keyboards, one in the 'United Kingdom' layout and one 'US International'. I would like to be able to use both while keeping my language set to English (United Kingdom). Is there any way of modifying this code to do that? An equivalent to the WM_INPUTLANGCHANGEREQUEST message, say, that changes specifically the keyboard layout rather than the language.
 
Thanks a lot.
GeneralAwesomememberkjansson26 May '11 - 4:24 
Just wanted to say thanks for this awesome tool. I was looking for something like this and this fit the glove perfectly!
GeneralRe: Awesomememberbrisemec27 May '11 - 14:01 
Thanks for your comment, I am glad you found this software useful. Best regards.
QuestionKnowing keyboard sourcemembermuneersn14 Dec '08 - 20:05 
Hi,
After searching long and far, i find your solution to be the closest to a challenge i am facing. I basically want the program to know which keyboard(same layouts) the keypress came from, and a seperate instance of the program must respond to each keyboard.
 
how can your program be tweaked to fit this scenario.
 
Thanks in advance.
Muneer.
 

 

 

more details: I have a situation wherein different instances of the same program(simple database application) running on the system are displayed on seperate monitors. Three keyboards are connected to the system via USB.
Each instance must respond to only one particular keyboard. Is it possible to use your logic here.
 
A simple solution is to use a server-client LAN model, but i want to use only one system.
AnswerRe: Knowing keyboard sourcememberbrisemec15 Dec '08 - 0:03 
Hello,
 
It is definitely possible to do do what you want. You should look at the source code of the main form, MainForm.cs. Essentially, you need to register your program as a raw input sink. Then process the WM_INPUT message to know from which keyboard the message comes from.
 
The code to register the sink is the following:
 
RAWINPUTDEVICE rawInputDevice = new RAWINPUTDEVICE(1, 6, API.RIDEV_INPUTSINK, this);
bool ok = API.RegisterRawInputDevices(rawInputDevice);
if(!ok) {
	throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
}
 
Then you need to override the WndProc method to be able to process the WM_INPUT message:
 
protected override void WndProc(ref Message message) {
	switch(message.Msg) {
		case API.WM_INPUT:
			ProcessInputMessage(message);
			break;
 
		default:
			base.WndProc(ref message);
			break;
	}
}
 
ProcessInputMessage is a method that you define that will be called whenever a key is pressed. You will obtain a RAWINPUTHEADER that contains information about the key that was pressed, as well as the handle of the keyboard that has produced the event.
 
private void ProcessInputMessage(Message message) {
	RAWINPUTHEADER header;
	uint result = API.GetRawInputData(message.LParam, API.RID_HEADER, out header);
	Debug.Assert(result != uint.MaxValue);
	if(result == uint.MaxValue) {
		throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
	}
 
	// Use header.hDevice to know which keyboard has been pressed.
}
 
You should be aware that the message processing is asynchronous. This means that the program that has the input focus will also receive the key press. You will need to prevent your programs from receiving normal input and then send them input messages that you generate yourself.
 
I hope this helps you, let me know if I can be of any assistance to you.
 
Antoine

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 23 Oct 2007
Article Copyright 2007 by AntoineAubry
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid