views:

458

answers:

2

I'm working on a program that needs to record and play back keystrokes. The main process runs as a service, so it needs a configuration program to record the keystrokes.

The problem comes when the system default keyboard layout is (say) English, and the user's keyboard layout of the moment is (say) German. The user enters a 'ü' character into the configuration interface, which is duly recorded and stored.

Now the user runs Notepad and tries to play the keystroke back, which is done by a child of the service. When transforming input to pass to SendInput, we call VkKeyScan, which then tells us there's no way to reproduce that character on this keyboard (remember the default keyboard layout is English).

What I'd really like to do is sync the keyboard layouts of the service and the current foreground application, so the service can reproduce any character the user can with their keyboard. GetKeyboardLayout only works in the current process. This has to run on both 32- and 64-bit, so a message hook is... not preferred :).

A: 

Why is this a service? If it requires a user to be logged in, then you can run it as a startup application and you'll be running in the same environment as the user.

jdigital
One reason is that the HID devices this service manages need to work at the logon screen. I know there's probably a better way, but the codebase dates from Windows 95. There's nearly a million lines of code, I don't want to rearchitect it just to fix this defect. :)
Ben Straub
Would switching to the user's desktop help? (http://msdn.microsoft.com/en-us/library/ms686347(VS.85).aspx)?
jdigital
At the point when I need this information, the user's desktop is already active. The problem is that the system-default keyboard layout may be different from the foreground process.
Ben Straub
Perhaps I'm misunderstanding your point, but of course the desktop is already active, there wouldn't be any way to switch to it if it weren't.
jdigital
Perhaps we're both misunderstanding. I'm not sure what switching desktops would get me here either. Keyboard layouts aren't per-desktop, they're per-thread.
Ben Straub
I made the incorrect assumption that you knew how to solve the problem if the app was running normally (not as a service), hence my suggestion.
jdigital
I can't see how to do this without a hook using the Win32 API (after switching desktops). Perhaps there's a driver level solution but that would place you back with the same 32/64 bit problem.
jdigital
+1  A: 

To retrieve the keyboard layout for the foreground window:

GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), NULL))

It sounds like you're recording characters instead of keystrokes. Instead of sending keystrokes when replaying, can you use SendMessage to send WM_CHAR messages with the recorded characters instead of generating keystrokes?

Bevan Collins
This won't work from a service (well. it would work from an interactive service but use of interactive services is not recommended and won't work under newer versions of Windows).
jdigital
I don't know why I didn't think of this. @jdigital, it actually will work, because the service has a worker process in the user's session. Thanks!
Ben Straub