Yesterday I bought a replacement for my old Ergonomics 4000 from Microsoft (it was fine but after several years keys became tougher to press) – ErgoMedia 700 from Genius.

I was choosing between buying Ergonomics again or this ErgoMedia model and decided to try out something fresh – and the amount of buttons and a 4D-wheel have certainly largely contributed to this decision.

Imagine my er, disappointment after discovering that the native ErgoMedia program was only able to customize 15 buttons leaving other 21 overboard (that’s without the 4D-wheel that can’t be customized).

So I have attempted a quick reversing to see if this can be fixed. To my delight the answer was positive.

Download

Program and its sources – written in Delphi 7 using D7X – Delphi 7 eXtension library.

To use simply extract anywhere on your system and edit ErgoHook.ini. Keyboard driver and ErgoMedia program must be installed but the latter should not be running. You might want to disable ErgoMedia starting up with your system by deleting its entry in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run («ErgoMedia») or replacing it with the path to extracted ErgoHook.exe.

ErgoHook has no interface, all interaction is done via the tray icon (red rocket):

Updated 5th April 2014: added check for currently logged in user (launching multiple ErgoHook from various users will only trigger key presses on currently logged in user); changed right click action; fixed execution of programs with spaces in path (read the comment inside ErgoHook.ini for details); added alternative key actions like 0019+.

Updated 23rd January 2015: when running multiple instances under different users only the active user gets keypresses handled (new OnlyActiveUser setting in the INI file).

Configuration

All ErgoHook can do is either run a program/URL/e-mail/other standard path or issue a keystroke. The latter is useful if you want to do more sophisticated processing such as offered by AutoHotKey or another hotkey manager.

Once a scan code is mapped AHK can react to it using raw scancode specified in ErgoHook.ini (for example, 20=0241 will imitate 241 key press inside AHK when you press on the Messenger button – AHK’s scancode after = is arbitrary and can be any as long as it matches inside your .ahk script):

conf; YourScript.ahk
SC0F1::MsgBox 'Messenger button was pressed.'

Detailed description can be found in ErgoHook.ini.

Non-configurable keys

Some keys are handled by the driver and while it reports the keystroke it executes its own hardcoded action in parallel. Such keys are:

A bit of background

This wasn’t my first experience of reversing a keyboard manager. Roughly 4 years ago when I’ve buoght my first laptop (Acer Extensa 4130) I have made a key mapper for its Fn and some other custom keys. Here’s a post I’ve mode on ixbt.com on 26th February of 2009 (in Russian).

That was a dirty hack though – I’ve changed one bit in the manager’s DLL to prevent it from doing anything and used DeviceIOControl to read new key presses from the keyboard driver. It still needed that Launch Manager app running to properly initialize and maintain driver pipe and that was the reason I myself haven’t used that mapper as Acer’s LM was pretty heavy on resources (like most other «essential» apps notebook/printer/motherboard/etc. manufacturers tend to include with their products).

Just if someone is interested here is the archive with that old tool and its sources. I seem to have lost the LM’s DLL though.

Technical info

Now regarding ErgoMedia’s software. The system is split into 3 parts whcih are well abstracted:

  1. KBHook.dll – the low-level hooking DLL; from what I have seen it’s very thin and simply checks system characteristics, finds keyboard HID GUID and maps it to the window proc of the window passed as the second parameter to its HookKeyboard exported function. It uses RegisterDeviceNotification for this purpose.
  2. MMKBD39.dll – the core of the mapper that uses KBHook.dll to catch keystrokes and that actually executes actions assigned to them. It’s a complete surprise to me that some keys (like My Pictures) are not reassignable using the native program because they are processed exactly the same way as, say, Internet button is.
  3. SyTray.exe – interface to MMKBD39.dll that changes keyboard settings and highlights keys that were pressed when its window is visible.

Below this lies a driver that we don’t see and don’t really need as RegisterDeviceNotification works quite well delivering the data to control window we choose. That said, SyTray.exe uses its normal window (a dialog of SYTRAY class) for this purpose.

Once we’ve got our window registered for notifications we can manage the keypresses. There’s a huge routine in MMKBD39.dll that does so – it’s exported as MultiMediaKeyboardProc. It has the signature of normal WinProc.

Message codes

I have skimmed through the keyboard proc (that in most cases, if not always, returns 0) and identified the following messages (hex codes; some other messages are received but ignored):

802
A key has been pressed. wParam is the scancode and lParam can be 0x80000000 or 0. MMKBD39.dll acts twofold: if SyTray’s window is visible it sends received keystrokes to it but doesn’t execute anything so SyTray can display the keys that were pressed; if its window is hidden corresponding action will be executed. Essentially, window’s handle to report the keys to is given to MMKBD39.dll’s InitMultiMediaKeyboard procedure as its only argument.
47A
Controls if keystrokes should be reported to the window or executed. If wParam is zero commands are executed, otherwise the window MMKBD39.dll was initialized with will receive them via EM_FINDTEXTW with key code in wParam – see 802.
808
Calls KBHook.dll’s SystemSleepKeyProc with verbatim wParam and lParam. It’s not entirely clear what this is used for – presumably it puts the driver/hook into sleep mode or awakens it.
478
This is some sort of system function that sets some strings. wParam is the new string (PChar) and lParam specifies the type of the string to set (possible values: 6, 7, 10, 11, 13).
801
Switches internal window’s dialog proc between two versions (depending if lParam is 0x65 or not) and sends it EM_SETIMECOLOR with wParam’s value. Not sure what this is for or what’s the internal window purpose – maybe to deal with some multimedia (MediaPlayer, etc.) API.
10 (WM_CLOSE)
Destroys the internal window.
805
Unclear; sends several custom messages to initialized window (0x805 if lParam is non-zero, also 0x465), destroys some other window, sets internal window to foreground.
807
Unclear; similar to 805.