TutorialHere is a real life example of how to use AHKHID.
When I bought my HP Media Center a few years ago, I also received this nice remote with a receiver which connects through USB. The remote is meant to be used with the
Media Center interface.
(image lost)
However, I eventually changed computer but still had the remote. So, I started wondering if I could find a way to detect all the button presses and assign labels to each of them. Although some of them triggered keyboard events (like the arrow buttons, the numbers, the OK button and the volume up/down button), I had no way of knowing if it came from the remote or from the keyboard.
So, these are the steps I used (and the steps you can use for any other HID device).
1. Figuring out the device specsFirst thing I had to figure out was to get the Vendor ID, Product ID, Version Number, and most importantly, the Usage Page and Usage the remote operates on. To do this, I fired up Example 1 and looked at the "Other" tab. The following picture is a screenshot of what I saw. In red are the "devices" registered to the remote (I know this because they only appeared after I plugged in the receiver).
(image lost)
As you can see, the three devices are from the remote. However, they all operate on a different Usage Page and/or Usage. Anyways, at least now I know what the Vendor ID, the Product ID and the Version Number are (which will be needed later on in the script in order to make sure the input comes from the remote).
2. Reverse engineering the dataSo now that I know on which Usage Page/Usage the remote operates on, I can fire up Example 2 and look at the data coming out of it. However, since the remote possesses three different TLCs (TLC means the Usage Page and Usage values), I had to register the three of them. Also, I modified Example 2 so that the output also shows the Usage Page/Usage values from which the data came from (so that I can distinguish between the three). Then, I clicked the Call button and started pressing buttons.
(image lost)
This is the part where I had to "reverse engineer" the data. I realized that
[*:hov57uc3] The TLC with UsPg 65468 and Us 136 showed down and up events only for some buttons.
[*:hov57uc3] Similarly, the TLC with UsPg 12 and Us 1 showed down and up events only for some (other) buttons.
[*:hov57uc3] Together, those two TLCs did not cover all the buttons.
[*:hov57uc3] And most importantly, the TLC with UsPg 65468 and Us 137 showed an event for every button pressed, but only on the down event.
3. Collecting dataI decided to therefore concentrate on the last TLC (the one with UsPg 65468 and Us 137) because it was the only one with access to all buttons and because I didn't really need the Up event. However, I obviously could have decided to support as many buttons with up/down events, but I just felt it would have been extra work for not much (for what I wanted to do with it).
So now, I had to make a list of the data received for each button press. I therefore created a nice spreadsheet where I typed out the name of all the buttons. I then modified Example 2 so that instead of adding data to the listbox, it would simply do SendInput % Bin2Hex(&uData, r) "{Enter}"
which means it would insert the data (in hex format) in the cell and then press Enter to go to the next cell. This is what I got:
(image lost)
As you can see, I noticed that the only thing that changed for each press was the value of the 6th byte (the 7th byte is also different, but it only alternates between 04 and 84, no matter the buttons pressed).
4. Creating a scriptAll I had left do to was to create a simple script which would register the remote and check the 6th byte on input to see which button was pressed. The goal of the script was to add remote functionality to VLC and MPC (Media Player Classic). Instead of creating a label for each button, I decided to create labels only when needed, such that the OnMessage function only had to check if the label existed before calling it. I also added a prefix to the label, such that the label to be called would depend on the program currently in focus. For example, if VLC is in focus, the prefix would be "VLC" such that when you press the button with value 22 (which is Play), the OnMessage function will call the label called VLC_22 (if it exists). This is the resulting script:
/*!
DVD Menu 36
Power 12
TV 70
Radio 80
Music 71
Pictures 73
Videos 74
Record 23
Rewind 21
Replay/Previous Track 27
Play 22
Pause 24
Stop 25
Forward 20
Skip/Next Track 26
Back 35
More/Information 15
Up Arrow 30
Left Arrow 32
Right Arrow 33
Down Arrow 31
OK 34
Volume Up 16
Volume Down 17
Windows Media Center 13
Mute 14
Channel/Page Up 18
Channel/Page Down 19
Live TV 37
Guide 38
Recorded TV 72
1 1
2/ABC 2
3/DEF 3
4/GHI 4
5/JKL 5
6/MNO 6
7/PQRS 7
8/TUV 8
9/WXYZ 9
* 29
0 0
# 28
Clear 10
Print 78
Enter 11
*/
;Must be in auto-execute section if I want to use the constants
#Include %A_ScriptDir%\AHKHID.ahk
;Create GUI to receive messages
Gui, +LastFound
hGui := WinExist()
;Intercept WM_INPUT messages
WM_INPUT := 0xFF
OnMessage(WM_INPUT, "InputMsg")
;Register Remote Control with RIDEV_INPUTSINK (so that data is received even in the background)
r := AHKHID_Register(65468, 137, hGui, RIDEV_INPUTSINK)
;Prefix loop
Loop {
Sleep 1000
If WinActive("ahk_class QWidget") Or WinActive("ahk_class VLC DirectX")
sPrefix := "VLC"
Else If WinActive("ahk_class Winamp v1.x") Or WinActive("ahk_class Winamp Video")
sPrefix := "Winamp"
Else If WinActive("ahk_class MediaPlayerClassicW")
sPrefix := "MPC"
Else sPrefix := "Default"
}
Return
InputMsg(wParam, lParam) {
Local devh, iKey, sLabel
Critical
;Get handle of device
devh := AHKHID_GetInputInfo(lParam, II_DEVHANDLE)
;Check for error
If (devh <> -1) ;Check that it is my HP remote
And (AHKHID_GetDevInfo(devh, DI_DEVTYPE, True) = RIM_TYPEHID)
And (AHKHID_GetDevInfo(devh, DI_HID_VENDORID, True) = 1118)
And (AHKHID_GetDevInfo(devh, DI_HID_PRODUCTID, True) = 109)
And (AHKHID_GetDevInfo(devh, DI_HID_VERSIONNUMBER, True) = 272) {
;Get data
iKey := AHKHID_GetInputData(lParam, uData)
;Check for error
If (iKey <> -1) {
;Get keycode (located at the 6th byte)
iKey := NumGet(uData, 5, "UChar")
;Call the appropriate sub if it exists
sLabel := sPrefix "_" iKey
If IsLabel(sLabel)
Gosub, %sLabel%
}
}
}
VLC_15: ;More
SendInput f ;Toggle fullscreen
Return
VLC_18: ;Channel Up
SendInput ^{Up}
Return
VLC_19: ;Channel Down
SendInput ^{Down}
Return
VLC_21: ;Rewind
SendInput !{Left}
Return
VLC_27: ;Previous Track
SendInput p
Return
VLC_22: ;Play
SendInput q
Return
VLC_24: ;Pause
SendInput {Space}
Return
VLC_25: ;Stop
SendInput s
Return
VLC_20: ;Forward
SendInput !{Right}
Return
VLC_26: ;Next Track
SendInput n
Return
MPC_15: ;More
SendInput !{Enter} ;Toggle fullscreen
Return
MPC_18: ;Channel Up
SendInput {Up}
Return
MPC_19: ;Channel Down
SendInput {Down}
Return
MPC_21: ;Rewind
SendInput !{Left}
Return
MPC_20: ;Forward
SendInput !{Right}
Return
MPC_27: ;Previous Track
SendInput !{End}
Return
MPC_26: ;Next Track
SendInput !{Home}
Return
MPC_22: ;Play
SendInput !p
Return
MPC_24: ;Pause
SendInput {Space}
Return
MPC_25: ;Stop
SendInput !s
Return
Extra: 5. Disabling/modifying original key functionsA lot of the buttons on my remote generated events when pressed. Some were keyboard events (e.g. the numbers 0 to 9 produced their respective numbers), and some were more advanced events (e.g. the power button would send the computer in standby). I wanted to disable these events so that they would not conflict with the new functions I've given the buttons. The user bidomo
here showed us a way on exactly how to do that, and I will just run through that methods here.
Note that you can also use this method to modify or create the default action of each key, without using AHKHID.You first have to navigate in the registry to the key
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HidIr\Remotes\745a17a0-74d3-11d0-b6fe-00a0c90f57da. If your remote does not produce that key, then the following will not work. You might have other keys in that same
Remotes key but as far as I understand, you cannot work with them. You can only work with the ones whose RemoteName value is
RC6 based MCE remote.
Now, export that key (right-click the key, select Export, and save the file somewhere). The only value we'll modify is the ReportMappingTable value. Therefore, you can erase all the other sections contained in the file. Mine looks like this (after doing some formatting so that 7 digits were on each line):
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HidIr\Remotes\745a17a0-74d3-11d0-b6fe-00a0c90f57da]
"ReportMappingTable"=hex:\
00,00,00,00,04,00,27,\
01,00,00,00,04,00,1e,\
02,00,00,00,04,00,1f,\
03,00,00,00,04,00,20,\
04,00,00,00,04,00,21,\
05,00,00,00,04,00,22,\
06,00,00,00,04,00,23,\
07,00,00,00,04,00,24,\
08,00,00,00,04,00,25,\
09,00,00,00,04,00,26,\
0a,00,00,00,04,00,29,\
0b,00,00,00,04,00,28,\
0c,00,00,00,03,82,00,\
0e,00,00,00,01,e2,00,\
0f,00,00,00,01,09,02,\
10,00,00,00,01,e9,00,\
11,00,00,00,01,ea,00,\
12,00,00,00,01,9c,00,\
13,00,00,00,01,9d,00,\
14,00,00,00,01,b3,00,\
15,00,00,00,01,b4,00,\
16,00,00,00,01,b0,00,\
17,00,00,00,01,b2,00,\
18,00,00,00,01,b1,00,\
19,00,00,00,01,b7,00,\
1a,00,00,00,01,b5,00,\
1b,00,00,00,01,b6,00,\
1c,00,00,00,04,02,20,\
1d,00,00,00,04,02,25,\
1e,00,00,00,04,00,52,\
1f,00,00,00,04,00,51,\
20,00,00,00,04,00,50,\
21,00,00,00,04,00,4f,\
22,00,00,00,04,00,28,\
23,00,00,00,01,24,02,\
26,00,00,00,01,8d,00,\
4e,00,00,00,01,08,02
I also sorted the lines, so that the first digit of each line went from smallest to largest. Before modifying any values, make a backup of this file (in case you want to restore the original key functions) and open the copy you wish to modify in a text editor. It might help you to do the same thing I did and separate the lines after every 7th digit.
Each line (or each consecutive 7 digits) represent a button and the default action associated with that button. To understand in details how it works, please read the PDF file to which bidomo linked in his post. The most important digit is the first one, as it tells you which key of the remote is associated with the following action (the following digits). For example, in the file I posted above, you can see that one of the lines is "0b,00,00,00,04,00,28". The digit 0b is the code representing the Enter button on my remote. How do I know this? There are two ways: you can either look at the PDF file, which will most likely tell you the correct value for each key on your remote,
or, if you did
Step 3, you can use the data there to know for sure which key on your remote is which line in the registry. For example, 0b in decimal is 11 which is, as reported in Step 3, the Enter key.
The advantage with the second method is that you will know exactly which button on your remote refers to which line in the registry. Also, the PDF file doesn't list all the keys. And most importantly, if your goal is to assign new events to each button, it's important for you to know that
not all your remote buttons are described in ReportMappingTable. Therefore, using the keycode found in Step 3, you can create new lines for buttons which don't yet have a given function in the table. An example of this in my case are the DVD Menu and Live TV buttons, which have a keycode of 24, and 25 (in hex), which as you can see, are not in the original ReportMappingTable.
If your goal is to simply disable ALL the orignal events of your keys, you simply have to replace all the digits that follow keycodes by 00, like this:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HidIr\Remotes\745a17a0-74d3-11d0-b6fe-00a0c90f57da]
"ReportMappingTable"=hex:\
00,00,00,00,00,00,00,\
01,00,00,00,00,00,00,\
02,00,00,00,00,00,00,\
03,00,00,00,00,00,00,\
04,00,00,00,00,00,00,\
05,00,00,00,00,00,00,\
06,00,00,00,00,00,00,\
07,00,00,00,00,00,00,\
08,00,00,00,00,00,00,\
09,00,00,00,00,00,00,\
0A,00,00,00,00,00,00,\
0B,00,00,00,00,00,00,\
0C,00,00,00,00,00,00,\
0D,00,00,00,00,00,00,\
0E,00,00,00,00,00,00,\
0F,00,00,00,00,00,00,\
10,00,00,00,00,00,00,\
11,00,00,00,00,00,00,\
12,00,00,00,00,00,00,\
13,00,00,00,00,00,00,\
14,00,00,00,00,00,00,\
15,00,00,00,00,00,00,\
16,00,00,00,00,00,00,\
17,00,00,00,00,00,00,\
18,00,00,00,00,00,00,\
19,00,00,00,00,00,00,\
1A,00,00,00,00,00,00,\
1B,00,00,00,00,00,00,\
1C,00,00,00,00,00,00,\
1D,00,00,00,00,00,00,\
1E,00,00,00,00,00,00,\
1F,00,00,00,00,00,00,\
20,00,00,00,00,00,00,\
21,00,00,00,00,00,00,\
22,00,00,00,00,00,00,\
23,00,00,00,00,00,00,\
24,00,00,00,00,00,00,\
25,00,00,00,00,00,00,\
26,00,00,00,00,00,00,\
46,00,00,00,00,00,00,\
47,00,00,00,00,00,00,\
48,00,00,00,00,00,00,\
49,00,00,00,00,00,00,\
4A,00,00,00,00,00,00,\
4E,00,00,00,00,00,00,\
50,00,00,00,00,00,00
And there you go. This is how to modify/disable the original function of your remote's buttons. This could, in theory, allow you to completely bypass the need for AHKHID by assigning key combinations to each remote button and catching those hotkeys in a regular AHK script.
Here's more reading material on the subject:
All About Media Center Remotes (advanced) - source of the informative PDF
Archive: Key Support, Keyboard Scan Codes, and Windows - information about HID Usage ID (needed to assign a certain keyboard event to a remote button)
Hope this helps!