Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

AHKHID - An AHK implementation of the HID functions


  • Please log in to reply
456 replies to this topic
ddh819
  • Members
  • 22 posts
  • Last active: Aug 02 2009 05:21 AM
  • Joined: 27 Dec 2006
I have a Kensington Presentation remote that seems to output regular keyboard keys F5, PgUp, PgDn, and b. I wanted to see if I can get a script that can differentiate between the remote and the regular keyboard.


When I run the Example 1 script with and without the remote's usb receiver plugged in, this is the entry that seems to correspond to the remote, which shows up on the keyboard page:

\??\HID#Vid_047d&Pid_2012#8&39691a6&0&0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}

When I run the Example 2 script with the remote plugged in, I call UsagePage 1 and Usage 6 for keyboards, the results for the remote buttons and the corresponding keys on the keyboard look the same.

Is there any way to have a script distinguish between the keyboard keys and the remote buttons?

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
Hello ddh819,

First of all, are you sure there's nothing new that also shows up in the Other section of Example 1? As I said, an HID device can have multiple TLCs (with even some of the keyboard and mouse type).

If it doesn't appear in the Other tab, try this:
1. Plug in the device
2. Go to the Device Manager (one way to get there is Start>Run>type "devmgmt.msc" and Enter)
3. Right-click on the root item in the listview (which should be the name of your computer) and select "Scan for hardware changes"
4. After it is done scanning, launch Example 1 again and check if there's anything new in the Other section.

If after doing the steps above, it is still not appearing in the Other tab, then you can try to register the keyboard TLC and find a way to differentiate between your keyboard and the remote. To do this, check if in the Keyboards section of Example 1, there's anything (looking at each column) that sets your remote apart, like the Type, SubType, etc...

If there is something that you can use to differentiate your remote, then you'll be able to write a script (much like mine) which registers the keyboard TLC (UsPg 1, Us 6) and upon input (ie. upon receiving WM_INPUT messages), simply check whatever factor you decided upon by first retrieving the handle of the device with HID_GetInputInfo(lParam, II_DEVHANDLE), and then retrieving the factor you want to check with HID_GetDevInfo(handle, flag of the factor here - like DI_KBD_TYPE or DI_KBD_SUBTYPE etc..., True). Also, the more things you can use to narrow it down, the better.

For example, let's say you find out that your Kensington Presentation remote is of Type 4, which sets it apart from every other keyboard listed in the Keyboard tab of Example 1, then your InputMsg() function could look something like this:

InputMsg(wParam, lParam) {
    Local devh
   
    Critical
   
    ;Get handle of device
    devh := HID_GetInputInfo(lParam, II_DEVHANDLE)
   
    ;Check for error
    If (devh <> -1) ;Check that it is my Kensington Presentation remote
        And (HID_GetDevInfo(devh, DI_DEVTYPE, True) = RIM_TYPEKEYBOARD) ;Make sure it's a keyboard
        And (HID_GetDevInfo(devh, DI_KBD_TYPE, True) = 4) {             ;Check if it's of Type 4 which means it has to be my remote
        
        ;Now you know it comes from your remote. You can treat the message using HID_GetInputInfo() and II_KBD_VKEY to find out which button was pressed.
    }
}

There is however a tradeoff in using the keyboard TLC for your device (which is why I wanted you to make sure you couldn't register it as an HID device instead). It's that you will not be able to stop the key presses the remote generates. This means that the buttons on the remote will always broadcast F5, PgDn, PgUp, and b even when you register it for your own purposes.

On the other hand, if you do end up finding an entry in the Other section of Example 1, then you can make the remote stop generating those keyboard presses by doing the following steps:
1. Go to Device Manager
2. Expand the "Keyboards" tree
3. You should find a device (ie. your remote registered as a keyboard) other than your usual keyboard there. To make sure it is indeed your remote, you can compare the name you found in Example 1 with the name you'll find when you right-click>Properties>Details on the keyboard item in Device Manager.
4. Once you found the keyboard entry associated to your Kensington Presentation remote, right-click the item and select Uninstall

After doing those steps, the Kensington remote will no longer generate keyboard presses and you then can concentrate on the data received through the entry you found in the Other section of Example 1.

Hope this helps.

ddh819
  • Members
  • 22 posts
  • Last active: Aug 02 2009 05:21 AM
  • Joined: 27 Dec 2006
Even after rescanning with the Device Manager, it doesn't come up on the Other tab.

The only thing different from the other keyboard devices in Example 1 is the name of the device and the Number of Keys Total. The remote's other settings look the same as the other keyboard devices, Type 81, SubType 0, KeyboardMode 1.

But as you guessed, I probably would want to prevent the regular remote keys from going through. Am I SOL?

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

Am I SOL?

Not necessarily.
There are two ways I can think of.

The first one, is if you disable (by setting them as hotkeys) the F5, PgDn, PgUp and b keys, and send them instead from your InputMsg() function only if the device is not your remote (make sure to use the dollar sign for the hotkeys so that SendInput isn't blocked by them). Theoretically, it should work...

Something like this:
$b::Return
$F5::Return
;etc...

InputMsg(wParam, lParam) {
    Local devh, key
   
    Critical
   
    ;Get handle of device
    devh := HID_GetInputInfo(lParam, II_DEVHANDLE)
   
    ;Check for error
    If (devh <> -1) ;Check that it is my Kensington Presentation remote
        And (HID_GetDevInfo(devh, DI_DEVTYPE, True) = RIM_TYPEKEYBOARD) ;Make sure it's a keyboard
        And (HID_GetDevInfo(devh, DI_KBD_TYPE, True) = 4) {             ;Check if it's of Type 4 which means it has to be my remote
       
        ;Now you know it comes from your remote. You can treat the message using HID_GetInputInfo() and II_KBD_VKEY to find out which button was pressed.
    } Else {
        
        ;Since it's not the remote, check (by using II_KBD_VKEY) if the key pressed is F5, PgDn, PgUp, or b and SendInput it here
        
        ;Get the keycode
        key := HID_GetInputInfo(lParam, II_KBD_VKEY)
        
        If (key = 66)   ;b
            SendInput b
        Else If (key = 116) ;F5
            SendInput {F5}
        ;etc...
    }
}

Edit: After testing it, the following method doesn't actually work.

The other way I can think of, is to capture the F5, PgDn, PgUp, and b keys so that they are enabled only if they are actually pressed. This should filter out remote presses. This one should (again, theoretically) work also...
Something like this:

$b::
    If GetKeyState("b")
        SendInput b
Return

$F5::
    If GetKeyState("F5")
        SendInput {F5}
Return

;etc...

You might also try GetKeyState("b", "P") to get the physical state instead of the logical state, if that doesn't work.

Let me know if it works!

ManaUser
  • Members
  • 1121 posts
  • Last active: Dec 07 2016 04:24 PM
  • Joined: 24 May 2007
Very nice. I've got a USB external HD, that has some buttons on it (for backup and restore, but I never installed the software that comes with it) so just as a test I decided to try detecting them with this script. It works perfectly. Not that I have any need to use those buttons... but it's nice to know I could. :)

Faolin
  • Members
  • 8 posts
  • Last active: Apr 25 2011 11:01 PM
  • Joined: 14 May 2009
Hi all,

I'm in a bit of a predicament, but I feel like I'm *so* close to getting some results. I've got a new Saitek Pro Flight Switch Panel... unfortunately the website I ordered it from didn't make it clear that it ONLY has built-in support for Microsoft Flight Sim X. The ONLY third party plugins I found for it were to support older versions of Flight Sim and another game called X Plane (none of which I'm really interested in).

It's just a simple-looking panel with a bunch of 2-state toggle switches and one 5-state knob. I bought it with the hopes of reprogramming the switches for any number of keystrokes for various games I play. My goal here is simply to translate each toggle switch into a keystroke (hopefully a different keystroke for each toggle position)....

But I haven't gotten that far yet. I've been searching around for various ways to do this over the past several days and AutoHotKey appears to be the simplest way of intercepting the device's output and translating that into keystrokes.

First, I came across Micha's tool. It really got my hopes up. I managed to ascertain the correct HID entry, but after pressing the Register button and flipping all of the switches, I saw no results =(

Now, I've found your script, but I seem to be running into the same results. I successfully used AHKHID example 1 to determine the values of the device:

Name: \??\HID#Vid_06a3&Pid_0d67#6&bd75ca7&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
Vendor ID: 1699
Product ID: 3431
Version Number: 272
Usage Page: 1
Usage: 0

So I plugged the UsagePage: 1 and Usage: 0 values into your AHKHID example 2 script and clicked ADD (not sure what I'm supposed to do with those Flags checkboxes...) and the entry shows up under the TLC Array. Then I click CALL, and tried flipping all the switches on the panel, but I don't see any results in the window below. Nothing ever even showed up in the Registered Devices section.

Nobody else seems to have posted about not seeing any results in the AHKHID window, so I'm hoping I'm just missing something really obvious. I'm brand new at these AHK scripts, but I have no problem getting my hands dirty, if that's what needs to happen.

Anyone have any guidance or ideas on how to get signals out of my device? It seems like I just have to figure out how to get it to register...

BTW I know the device works, because it comes with a little app that displays a picture of the device and shows the switches move on-screen as you flip them on the actual device. Every switch registers correctly in that app. It's taunting me!

Thanks!!!

Edit: I was looking in the example 2 script and reading some of the documentation from the AHKHID file and tried setting the RIDEV_PAGEONLY flag when hitting the CALL button in the script. This time, I actually got some info! The funny thing is that my mouse, keyboard, and joystick (and who knows what else...) all are also sending lots of info along with the switches on the panel. I guess they share some similar parameters? Now I'm trying to figure out how to isolate just the panel device so I can analyze the output more easily.

ManaUser
  • Members
  • 1121 posts
  • Last active: Dec 07 2016 04:24 PM
  • Joined: 24 May 2007
If it shows up similar to a keyboard, is there any chance that AutoHotkey can detect it without going though all that HID business? Give this a quick test.

#InstallKeybdHook
#InstallMouseHook
#Persistent
#KeyHistory 10
SetTitleMatchMode 2
SetTitleMatchMode Slow

KeyHistory
Loop
{
   Sleep 100
   IfWinNotExist AutoHotkey, Keybd hook: yes
      ExitApp
   KeyHistory
}


Faolin
  • Members
  • 8 posts
  • Last active: Apr 25 2011 11:01 PM
  • Joined: 14 May 2009

If it shows up similar to a keyboard, is there any chance that AutoHotkey can detect it without going though all that HID business? Give this a quick test.


I appreciate the try, but it didn't work. I flipped every switch on the device, but nothing showed up in the queue.


Using the RIDEV_PAGEONLY flag, as before, I've managed to map out every hex output from each of the switches. It turns out to be an interesting kind of state-machine whereby toggling on a switch adds a fixed hex value (unique to that switch) to the last hex state. Toggling the same switch off subtracts the same hex value from the last hex state. The funny part about this is that flipping the same switch will feed a different hex value to the pc if the other switches around it are set to a different configuration during each instance. It's gonna be pretty interesting to make this thing work the way I want =)

To give an example, the current hex value with all switches off/down is: 00002008. If I flip only the first toggle on, I get 00012008. If I reset everything off (00002008) and flip the 2nd toggle switch, it puts out 00022008. Now if I flip that same switch as the first time, it spits out 00032008 instead of the 00012008 I got last time. It hex-added 00010000 to the running value when I flipped the first toggle.

But I digress..... I think now I need to fiddle in the examples more to see how I can possibly register this specific device. After that, it should be easy enough to write up some more scripts to interpret the device output, right? That part doesn't do me much good if I can't isolate the device by itself, though, instead of using that RIDEV_PAGEONLY flag which takes output from all of my peripherals.

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
Hello Faolin,

I'm glad you figured out to interpret the hex data! (That's the most important step)

I find it strange that you're not able to receive any data with registering without any flags at all. But then again, I find it strange that the Usage is 0, which is rather unusual (at least, in my little experience I have with HID devices).

Nonetheless, if it works with RIDEV_PAGEONLY, that's very good news! As you guessed, registering the UsagePage 1 will also register the keyboard and mouse. But not to worry, you can easy diffeerientiate between the different devices upon receiving the messages. To see how, you can look at the sample script I posted in my "tutorial" (ie. the second post in this thread). The crucial part (ie. the part that triggers events) looks like this:

InputMsg(wParam, lParam) {
    Local devh, iKey, sLabel
   
    Critical
   
    ;Get handle of device
    devh := HID_GetInputInfo(lParam, II_DEVHANDLE)
   
    ;Check for error
    If (devh <> -1) ;Check that it is my HP remote
        And (HID_GetDevInfo(devh, DI_DEVTYPE, True) = RIM_TYPEHID)
        And (HID_GetDevInfo(devh, DI_HID_VENDORID, True) = 1118)
        And (HID_GetDevInfo(devh, DI_HID_PRODUCTID, True) = 109)
        And (HID_GetDevInfo(devh, DI_HID_VERSIONNUMBER, True) = 272) {
       
        ;Get data
        iKey := HID_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%
        }
    }
}

Notice how I actually check if the device is indeed my HP remote. You can do the same with your device by simply replacing the appropriate values (ie. the vendor ID, product ID, etc...).

Therefore, it won't conflict with the messages you receive from your mouse and keyboard (since they're not even of type RIM_TYPEHID, for which we check before proceeding).

If you're not too sure how to actually make a script that can make full use of your device, try following the broad steps I outlined in my tutorial. The sample script I provided should be a big help in getting you set up. Hope this helps!

Don't hesitate to ask any other questions. :D

Faolin
  • Members
  • 8 posts
  • Last active: Apr 25 2011 11:01 PM
  • Joined: 14 May 2009
That does help tons! I was actually just fiddling with the RIDEV_EXCLUDE flag in the example 2 in order to clean everything up so I was only detecting output from the switch panel (I managed to get rid of the keyboard/mouse/joystick clutter). I thought maybe I'd have to use that flag in my script, eventually, but it makes more sense to be able to filter the signals based on the specific device parameters (vendor code, etc...). I'll start looking back on the tutorial and see how far I can get. I have a really good feeling about this =)

Thanks!

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
No problem!
The idea that you have of using the RIDEV_EXCLUDE flag is actually a very good one! Although I showed you how to discriminate between the different devices, you should still use the RIDEV_EXCLUDE flag to exclude at least the keyboard and the mouse. This will greatly reduce the number of times the script receives messages for no reason (but obviously, you should still check if it's your specific device).

majkinetor!
  • Guests
  • Last active:
  • Joined: --
Very nice. Ty.

ManaUser
  • Members
  • 1121 posts
  • Last active: Dec 07 2016 04:24 PM
  • Joined: 24 May 2007
Does AHKHID have any feature for sending information to a device? e.g. setting the Caps Lock light? (I know AutoHotkey can do that anyway, just as an example.) I'm only asking out of curiosity, so don't take that as a feature request onles you feel like it. :)

But if that is possible, can you think of any way I could listen in on what another app is sending a device so I could learn the correct codes, the same is you do going the other way?

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

Very nice. Ty.

Glad you like it! :D

Does AHKHID have any feature for sending information to a device?

Not at all. The raw input API is only meant for receiving input from HIDs. In most ways, HID registration is kind of a substitute to using the built-in Windows messages used to notify applications of user input (like WM_KEYDOWN, WM_MOUSEMOVE, etc...). Obviously, HID registration has some advantages over Windows-treated messages (check out the link), but the idea is the same - i.e. provide a way for applications to receive data from various input devices. So, to answer your question, no, AHKHID cannot send information to a device. Not because I haven't developed it, but because it's not the idea behind HID functions. Hope this helps! :)

ManaUser
  • Members
  • 1121 posts
  • Last active: Dec 07 2016 04:24 PM
  • Joined: 24 May 2007
Huh. I'm sure you know much more about it than I but I'm a little surprised. Because I was investigating what kind of devices might be used this way and one of the things I found claiming to use HID drivers was only an indicator light. So... how could that work then?