Intercepting buttons on Microsoft Surface Pen (HID) to change their actions

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Bkid
Posts: 31
Joined: 13 Jun 2014, 12:19

Intercepting buttons on Microsoft Surface Pen (HID) to change their actions

Post by Bkid » 14 Jan 2022, 16:59

For a few weeks now, a user on Discord has had issues with configuring their Surface pen to work with a specific program and shortcuts in that program. The buttons on the pen are not easily reconfigurable via any official software, so AHK was looked into as a solution. A user in Discord (Cloaker) was able to get pretty far with reading the pen and intercepting its inputs, however they have been busy and unable to continue assisting, so I thought I would put this information on the forums to see if anyone has any idea of where to go next.

What we currently have:

Main.ahk - Used to grab HID data from the pen's actions (hovering the screen, touching, button pressing, etc):

Code: Select all

/*! TheGood
	AHKHID - An AHK implementation of the HID functions.
	AHKHID Example 2
	Last updated: August 22nd, 2010
	
	Registers HID devices and displays data coming upon WM_INPUT.
	This example shows how to use AHKHID_AddRegister(), AHKHID_Register(), AHKHID_GetInputInfo() and AHKHID_GetInputData().
	_______________________________________________________________
	1. Input the TLC (Usage Page and Usage) you'd like to register.
	2. Select any flags you want to associate with the TLC (see Docs for more info about each of them).
	3. Press Add to add the TLC to the array.
	3. Repeat 1, 2 and 3 for all the TLCs you'd like to register (the TLC array listview will get filled up).
	4. Press Call to register all the TLCs in the array.
	5. Any TLCs currently registered will show up in the Registered devices listview.
	6. Any data received will be displayed in the listbox.
	
	For example, if you'd like to register the keyboard and the mouse, put UsagePage 1 and check the flag RIDEV_PAGEONLY.
	Then press Add and then Call to register.
*/

SetWorkingDir, %A_ScriptDir%

SetBatchLines, -1

#Include AHKHID.ahk

;Check if the OS is Windows Vista or higher
bVista := (DllCall("GetVersion") & 0xFF >= 6)

;Create GUI
Gui +LastFound -Resize -MaximizeBox -MinimizeBox
Gui, +E0x02000000 +E0x00080000 ; WS_EX_COMPOSITED & WS_EX_LAYERED => Double Buffer
Gui, Add, Text, x6 y10 w80 h20, Usage&Page
Gui, Add, Edit, x86 y10 w100 h20 Number vtxtUsPg,
Gui, Add, Text, x6 y30 w80 h20, &Usage
Gui, Add, Edit, x86 y30 w100 h20 Number vtxtUs,
Gui, Add, GroupBox, x6 y60 w180 h210, &Flags
Gui, Add, CheckBox, x16 y80 w160 h20 vchkAPPKEYS, RIDEV_APPKEYS
Gui, Add, CheckBox, x16 y100 w160 h20 vchkCAPTUREMOUSE, RIDEV_CAPTUREMOUSE
Gui, Add, CheckBox, x16 y120 w160 h20 vchkEXCLUDE, RIDEV_EXCLUDE
Gui, Add, CheckBox, x16 y140 w160 h20 vchkINPUTSINK, RIDEV_INPUTSINK
Gui, Add, CheckBox, x16 y160 w160 h20 vchkNOHOTKEYS, RIDEV_NOHOTKEYS
Gui, Add, CheckBox, x16 y180 w160 h20 vchkNOLEGACY, RIDEV_NOLEGACY
Gui, Add, CheckBox, x16 y200 w160 h20 vchkPAGEONLY gPAGEONLY_Click, RIDEV_PAGEONLY
Gui, Add, CheckBox, x16 y220 w160 h20 vchkREMOVE, RIDEV_REMOVE
If bVista
	Gui, Add, CheckBox, x16 y240 w160 h20 vchkEXINPUTSINK, RIDEV_EXINPUTSINK
Gui, Add, Button, x196 y10 w40 h40 vbtnAdd gbtnAdd_Event, &Add
Gui, Add, Button, x196 y60 w40 h40 vbtnRem gbtnRem_Event, &Rem
Gui, Add, Text, x246 y10 w340 h20, TLC Array:
Gui, Add, ListView, x246 y30 w410 h70 vlvwTLC, Usage Page|Usage|Flags
Gui, Add, Button, x196 y110 w225 h40 vbtnCall gbtnCall_Event, &Call
Gui, Add, Button, x431 y110 w225 h40 vbtnRefresh gbtnRefresh_Event, Refresh &list
Gui, Add, Text, x196 y160 w390 h20, Registered devices:
Gui, Add, ListView, x196 y180 w460 h80 vlvwRegistered, Usage Page|Usage|Flags
Gui, Add, Text, x6 y276 w580 h20, To register keyboards, use Usage Page 1 and Usage 6. For mice, Usage Page 1 and Usage 2.
Gui, Font, w700 s8, Courier New
Gui, Add, ListBox, ReadOnly x6 y296 w650 h320 vlbxInput hwndhlbxInput glbxInput_Event,

Gui, Add, Text, x700 y6, Buttons:
Gui, Add, Edit, xp yp+14 w300 h290 HwndButtonDisplay
Gui, Add, Text, xp yp+314 section, Values:
Gui, Add, CheckBox, xs+90 ys HwndValuesAsHex, Hex?
Gui, Add, Edit, xs ys+14 w300 h290 HwndValueDisplay

;Keep handle
GuiHandle := WinExist()

;Set up the constants
AHKHID_UseConstants()

;Intercept WM_INPUT
OnMessage(0x00FF, "InputMsg")

;Show GUI
Gui, Show
Return

GuiClose:
ExitApp

btnAdd_Event:
	
	;Get vars
	Gui, Submit, NoHide
	
	;Set default listview
	Gui, ListView, lvwTLC
	
	;Prep flags
	iFlags := 0
	iFlags |= chkAPPKEYS      ? RIDEV_APPKEYS      : 0
	iFlags |= chkCAPTUREMOUSE ? RIDEV_CAPTUREMOUSE : 0
	iFlags |= chkEXCLUDE      ? RIDEV_EXCLUDE      : 0
	iFlags |= chkINPUTSINK    ? RIDEV_INPUTSINK    : 0
	iFlags |= chkNOHOTKEYS    ? RIDEV_NOHOTKEYS    : 0
	iFlags |= chkNOLEGACY     ? RIDEV_NOLEGACY     : 0
	iFlags |= chkPAGEONLY     ? RIDEV_PAGEONLY     : 0
	iFlags |= chkREMOVE       ? RIDEV_REMOVE       : 0
	If bVista
		iFlags |= chkEXINPUTSINK ? RIDEV_EXINPUTSINK : 0
	
	;Add item
	LV_Add("", txtUsPg, txtUs, iFlags)
	
Return

;Delete selected
btnRem_Event:
	Gui, ListView, lvwTLC
	LV_Delete(LV_GetNext())
Return

;Using RIDEV_PAGEONLY means Usage has to be 0
PAGEONLY_Click:
	Gui, Submit, NoHide
	If chkPAGEONLY
		GuiControl,, txtUs, 0
Return

;Clear on doubleclick
lbxInput_Event:
	If (A_GuiEvent = "DoubleClick") {
		GuiControl,, lbxInput,|
		iInputNum := 0
	}
Return

btnCall_Event:
	
	;Set default listview
	Gui, ListView, lvwTLC
	
	;Get count
	iCount := LV_GetCount()
	
	;Dimension the array
	AHKHID_AddRegister(iCount)
	
	Loop %iCount% {
		
		;Extract info from listview
		LV_GetText(iUsPg, A_Index, 1)
		LV_GetText(iUs,   A_Index, 2)
		LV_GetText(iFlag, A_Index, 3)
		
		;Add for registration
		r := AHKHID_AddRegister(iUsPg, iUs, GuiHandle, iFlag)
	}
	
	;Register
	AHKHID_Register()
	
	;Refresh list
	Gosub btnRefresh_Event
	
	;Clear listview
	Gui, ListView, lvwTLC
	LV_Delete()
	
Return

btnRefresh_Event:
	
	;Set default listview
	Gui, ListView, lvwRegistered
	
	;Clear listview
	LV_Delete()
	
	;Check the size of a pointer
	PtrSize := (A_PtrSize ? A_PtrSize : 4)
	
	;Get devs
	iCount := AHKHID_GetRegisteredDevs(uDev)
	If (iCount > 0)
		Loop %iCount% ;Add to listview
			LV_Add("", NumGet(uDev, ((A_Index - 1) * (8 + PtrSize)) + 0, "UShort")
			, NumGet(uDev, ((A_Index - 1) * (8 + PtrSize)) + 2, "UShort")
			, NumGet(uDev, ((A_Index - 1) * (8 + PtrSize)) + 4, "UInt"))
	
Return

#include CoolHID.ahk

InputMsg(wParam, lParam) {
	Local r, h
	Critical    ;Or otherwise you could get ERROR_INVALID_HANDLE
	
	static OnlyOnce := true

	;Get device type
	r := AHKHID_GetInputInfo(lParam, II_DEVTYPE) 
	If (r = -1)
		OutputDebug %ErrorLevel%
	If (r = RIM_TYPEMOUSE) {
		GuiControl,, lbxInput, % ""
		. " Flags: "       AHKHID_GetInputInfo(lParam, II_MSE_FLAGS) 
		. " ButtonFlags: " AHKHID_GetInputInfo(lParam, II_MSE_BUTTONFLAGS) 
		. " ButtonData: "  AHKHID_GetInputInfo(lParam, II_MSE_BUTTONDATA) 
		. " RawButtons: "  AHKHID_GetInputInfo(lParam, II_MSE_RAWBUTTONS) 
		. " LastX: "       AHKHID_GetInputInfo(lParam, II_MSE_LASTX)
		. " LastY: "       AHKHID_GetInputInfo(lParam, II_MSE_LASTY) 
		. " ExtraInfo: "   AHKHID_GetInputInfo(lParam, II_MSE_EXTRAINFO)
	} Else If (r = RIM_TYPEKEYBOARD) {
		GuiControl,, lbxInput, % ""
		. " MakeCode: "    AHKHID_GetInputInfo(lParam, II_KBD_MAKECODE)
		. " Flags: "       AHKHID_GetInputInfo(lParam, II_KBD_FLAGS)
		. " VKey: "        AHKHID_GetInputInfo(lParam, II_KBD_VKEY)
		. " Message: "     AHKHID_GetInputInfo(lParam, II_KBD_MSG) 
		. " ExtraInfo: "   AHKHID_GetInputInfo(lParam, II_KBD_EXTRAINFO)
	} Else If (r = RIM_TYPEHID) {
		h := AHKHID_GetInputInfo(lParam, II_DEVHANDLE)
		dataSize := AHKHID_GetInputData(lParam, uData)

		if (OnlyOnce) {
			OnlyOnce := false
			
			static Device := ""

			Device := new HID(h)

			GuiControl,, lbxInput, % "Usage: " Device.Capabilities.Usage
			GuiControl,, lbxInput, % "UsagePage: " Device.Capabilities.UsagePage
			GuiControl,, lbxInput, % "InputReportByteLength: " Device.Capabilities.InputReportByteLength
			GuiControl,, lbxInput, % "OutputReportByteLength: " Device.Capabilities.OutputReportByteLength
			GuiControl,, lbxInput, % "FeatureReportByteLength: " Device.Capabilities.FeatureReportByteLength
			GuiControl,, lbxInput, % "Reserved: " Device.Capabilities.Reserved
			GuiControl,, lbxInput, % "NumberLinkCollectionNodes: " Device.Capabilities.NumberLinkCollectionNodes
			GuiControl,, lbxInput, % "NumberInputButtonCaps: " Device.Capabilities.NumberInputButtonCaps
			GuiControl,, lbxInput, % "NumberInputValueCaps: " Device.Capabilities.NumberInputValueCaps
			GuiControl,, lbxInput, % "NumberInputDataIndices: " Device.Capabilities.NumberInputDataIndices
			GuiControl,, lbxInput, % "NumberOutputButtonCaps: " Device.Capabilities.NumberOutputButtonCaps
			GuiControl,, lbxInput, % "NumberOutputValueCaps: " Device.Capabilities.NumberOutputValueCaps
			GuiControl,, lbxInput, % "NumberOutputDataIndices: " Device.Capabilities.NumberOutputDataIndices
			GuiControl,, lbxInput, % "NumberFeatureButtonCaps: " Device.Capabilities.NumberFeatureButtonCaps
			GuiControl,, lbxInput, % "NumberFeatureValueCaps: " Device.Capabilities.NumberFeatureValueCaps
			GuiControl,, lbxInput, % "NumberFeatureDataIndices: " Device.Capabilities.NumberFeatureDataIndices

			for k, Button in Device.ButtonCapabilities.Input {
				GuiControl,, lbxInput, % "`tButton Index " k ": "
				GuiControl,, lbxInput, % "`t UsagePage: " Button.UsagePage
				GuiControl,, lbxInput, % "`t ReportID: " Button.ReportID
				GuiControl,, lbxInput, % "`t IsAlias: " Button.IsAlias
				GuiControl,, lbxInput, % "`t BitField: " Button.BitField
				GuiControl,, lbxInput, % "`t LinkCollection: " Button.LinkCollection
				GuiControl,, lbxInput, % "`t LinkUsage: " Button.LinkUsage
				GuiControl,, lbxInput, % "`t LinkUsagePage: " Button.LinkUsagePage
				GuiControl,, lbxInput, % "`t IsRange: " Button.IsRange
				GuiControl,, lbxInput, % "`t IsStringRange: " Button.IsStringRange
				GuiControl,, lbxInput, % "`t IsDesignatorRange: " Button.IsDesignatorRange
				GuiControl,, lbxInput, % "`t IsAbsolute: " Button.IsAbsolute
				GuiControl,, lbxInput, % "`t ReportCount: " Button.ReportCount

				if (Button.IsRange) {
					GuiControl,, lbxInput, % "`t Range:"
					GuiControl,, lbxInput, % "`t  UsageMin: " Button.UsageMin
					GuiControl,, lbxInput, % "`t  UsageMax: " Button.UsageMax
					GuiControl,, lbxInput, % "`t  StringMin: " Button.StringMin
					GuiControl,, lbxInput, % "`t  StringMax: " Button.StringMax
					GuiControl,, lbxInput, % "`t  DesignatorMin: " Button.DesignatorMin
					GuiControl,, lbxInput, % "`t  DesignatorMax: " Button.DesignatorMax
					GuiControl,, lbxInput, % "`t  DataIndexMin: " Button.DataIndexMin
					GuiControl,, lbxInput, % "`t  DataIndexMax: " Button.DataIndexMax
				}
				else {
					GuiControl,, lbxInput, % "`t NotRange:"
					GuiControl,, lbxInput, % "`t  Usage: " Button.Usage
					GuiControl,, lbxInput, % "`t  Reserved1: " Button.Reserved1
					GuiControl,, lbxInput, % "`t  StringIndex: " Button.StringIndex
					GuiControl,, lbxInput, % "`t  Reserved2: " Button.Reserved2
					GuiControl,, lbxInput, % "`t  DesignatorIndex: " Button.DesignatorIndex
					GuiControl,, lbxInput, % "`t  Reserved3: " Button.Reserved3
					GuiControl,, lbxInput, % "`t  DataIndex: " Button.DataIndex
					GuiControl,, lbxInput, % "`t  Reserved4: " Button.Reserved4
				}
			}

			for k, Value in Device.ValueCapabilities.Input {
				GuiControl,, lbxInput, % "Value Index " k ": "
				GuiControl,, lbxInput, % "`tUsagePage: " Value.UsagePage
				GuiControl,, lbxInput, % "`tReportID: " Value.ReportID
				GuiControl,, lbxInput, % "`tIsAlias: " Value.IsAlias
				GuiControl,, lbxInput, % "`tBitField: " Value.BitField
				GuiControl,, lbxInput, % "`tLinkCollection: " Value.LinkCollection
				GuiControl,, lbxInput, % "`tLinkUsage: " Value.LinkUsage
				GuiControl,, lbxInput, % "`tLinkUsagePage: " Value.LinkUsagePage
				GuiControl,, lbxInput, % "`tIsRange: " Value.IsRange
				GuiControl,, lbxInput, % "`tIsStringRange: " Value.IsStringRange
				GuiControl,, lbxInput, % "`tIsDesignatorRange: " Value.IsDesignatorRange
				GuiControl,, lbxInput, % "`tIsAbsolute: " Value.IsAbsolute
				GuiControl,, lbxInput, % "`tHasNull: " Value.HasNull
				GuiControl,, lbxInput, % "`tReserved: " Value.Reserved
				GuiControl,, lbxInput, % "`tBitSize: " Value.BitSize
				GuiControl,, lbxInput, % "`tReportCount: " Value.ReportCount
				GuiControl,, lbxInput, % "`tReserved2: " Value.Reserved2
				GuiControl,, lbxInput, % "`tUnitsExp: " Value.UnitsExp
				GuiControl,, lbxInput, % "`tUnits: " Value.Units
				GuiControl,, lbxInput, % "`tLogicalMin: " Value.LogicalMin
				GuiControl,, lbxInput, % "`tLogicalMax: " Value.LogicalMax
				GuiControl,, lbxInput, % "`tPhysicalMin: " Value.PhysicalMin
				GuiControl,, lbxInput, % "`tPhysicalMax: " Value.PhysicalMax

				if (Value.IsRange) {
					GuiControl,, lbxInput, % "`t Range:"
					GuiControl,, lbxInput, % "`t  UsageMin: " Value.UsageMin
					GuiControl,, lbxInput, % "`t  UsageMax: " Value.UsageMax
					GuiControl,, lbxInput, % "`t  StringMin: " Value.StringMin
					GuiControl,, lbxInput, % "`t  StringMax: " Value.StringMax
					GuiControl,, lbxInput, % "`t  DesignatorMin: " Value.DesignatorMin
					GuiControl,, lbxInput, % "`t  DesignatorMax: " Value.DesignatorMax
					GuiControl,, lbxInput, % "`t  DataIndexMin: " Value.DataIndexMin
					GuiControl,, lbxInput, % "`t  DataIndexMax: " Value.DataIndexMax
				}
				else {
					GuiControl,, lbxInput, % "`t NotRange:"
					GuiControl,, lbxInput, % "`t  Usage: " Value.Usage
					GuiControl,, lbxInput, % "`t  Reserved1: " Value.Reserved1
					GuiControl,, lbxInput, % "`t  StringIndex: " Value.StringIndex
					GuiControl,, lbxInput, % "`t  Reserved2: " Value.Reserved2
					GuiControl,, lbxInput, % "`t  DesignatorIndex: " Value.DesignatorIndex
					GuiControl,, lbxInput, % "`t  Reserved3: " Value.Reserved3
					GuiControl,, lbxInput, % "`t  DataIndex: " Value.DataIndex
					GuiControl,, lbxInput, % "`t  Reserved4: " Value.Reserved4
				}
			}

			;GuiControl,, lbxInput, % Message  

			SendMessage, 0x018B, 0, 0,, ahk_id %hlbxInput%
			SendMessage, 0x0186, ErrorLevel - 1, 0,, ahk_id %hlbxInput%  
		}
		
		;GuiControl,, lbxInput, % ""
		;. " Vendor ID: "   AHKHID_GetDevInfo(h, DI_HID_VENDORID,     True)
		;. " Product ID: "  AHKHID_GetDevInfo(h, DI_HID_PRODUCTID,    True)
		;. " UsPg/Us: " AHKHID_GetDevInfo(h, DI_HID_USAGEPAGE, True) . "/" . AHKHID_GetDevInfo(h, DI_HID_USAGE, True)
		;. " Data: " Bin2Hex(&uData, dataSize)

		Report := Device.AddReport(&uData, dataSize)

		NewButtonText := ""

		for UsagePage, Usages in Report.Buttons {
			;NewButtonText .= "Button Page " Format("{:x}", UsagePage) "`r`n"

			for k, Usage in Usages {
				NewButtonText .= "`t " Device.GetUsageName(UsagePage, Usage) "`r`n"
			}
		}

		GuiControl,, %ButtonDisplay%, %NewButtonText%

		GuiControlGet, UseHex,, %ValuesAsHex%

		ValueFormat := UseHex ? "{:x}" : "{:}"

		NewValueText := ""

		for UsagePage, Usages in Report.Values {
			;NewValueText .= "Value Page " Format("{:x}", UsagePage) "`r`n"
			NewValueText .= Device.GetUsagePageName(UsagePage) "`r`n"

			for Usage, Values in Usages {
				;NewValueText .= "`t Usage " Format("{:x}", Usage) "`r`n"
				NewValueText .= "`t " Device.GetUsageName(UsagePage, Usage) "`r`n"

				for k, Value in Values {
					NewValueText .= "`t`t " Format(ValueFormat, Value) "`r`n"
				}
			}
		} 

		GuiControl,, %ValueDisplay%, %NewValueText%
	}
	;SendMessage, 0x018B, 0, 0,, ahk_id %hlbxInput%
	;SendMessage, 0x0186, ErrorLevel - 1, 0,, ahk_id %hlbxInput%
}

;By Laszlo, adapted by TheGood
;http://www.autohotkey.com/forum/viewtopic.php?p=377086#377086
Bin2Hex(addr,len) {
	Static fun, ptr 
	If (fun = "") {
		If A_IsUnicode
			If (A_PtrSize = 8)
				h=4533c94c8bd14585c07e63458bd86690440fb60248ffc2418bc9410fb6c0c0e8043c090fb6c00f97c14180e00f66f7d96683e1076603c8410fb6c06683c1304180f8096641890a418bc90f97c166f7d94983c2046683e1076603c86683c13049ffcb6641894afe75a76645890ac366448909c3
			Else h=558B6C241085ED7E5F568B74240C578B7C24148A078AC8C0E90447BA090000003AD11BD2F7DA66F7DA0FB6C96683E2076603D16683C230668916240FB2093AD01BC9F7D966F7D96683E1070FB6D06603CA6683C13066894E0283C6044D75B433C05F6689065E5DC38B54240833C966890A5DC3
		Else h=558B6C241085ED7E45568B74240C578B7C24148A078AC8C0E9044780F9090F97C2F6DA80E20702D1240F80C2303C090F97C1F6D980E10702C880C1308816884E0183C6024D75CC5FC606005E5DC38B542408C602005DC3
		VarSetCapacity(fun, StrLen(h) // 2)
		Loop % StrLen(h) // 2
			NumPut("0x" . SubStr(h, 2 * A_Index - 1, 2), fun, A_Index - 1, "Char")
		ptr := A_PtrSize ? "Ptr" : "UInt"
		DllCall("VirtualProtect", ptr, &fun, ptr, VarSetCapacity(fun), "UInt", 0x40, "UInt*", 0)
	}
	VarSetCapacity(hex, A_IsUnicode ? 4 * len + 2 : 2 * len + 1)
	DllCall(&fun, ptr, &hex, ptr, addr, "UInt", len, "CDecl")
	VarSetCapacity(hex, -1) ; update StrLen
	Return hex
}

AHKHID by TheGood - What I assume is the original HID to AHK implementation:

Code: Select all

/*! TheGood
    AHKHID - An AHK implementation of the HID functions.
    Last updated: August 22nd, 2010

USING THE CONSTANTS:

If you explicitly #include AHKHID in your script, you will have all the constants available to you. Otherwise, if AHKHID is
in your library folder and you do not wish to explicitly #include it, you can call AHKHID_UseConstants() to have the
constants available to you.

FUNCTION LIST:
_____________________
AHKHID_UseConstants()

See the section above titled "USING THE CONSTANTS"
___________________________________
AHKHID_Initialize(bRefresh = False)

You don't have to call this function manually. It is automatically called by other functions to get the pointer of the
RAWINPUTDEVICELIST struct array. However, if a new device is plugged in, you will have to refresh the listing by calling it
with bRefresh = True. Returns -1 on error (with error message in ErrorLevel).
____________________
AHKHID_GetDevCount()

Returs the number of HID devices connected to this computer.
Returns -1 on error (with error message in ErrorLevel).
______________________
AHKHID_GetDevHandle(i)

Returns the handle of device i (starts at 1).
Mostly used internally for API calls.
__________________________
AHKHID_GetDevIndex(Handle)

Returns the index (starts at 1) of the device in the enumeration with matching handle.
Returns 0 if not found.
______________________________________
AHKHID_GetDevType(i, IsHandle = False)

Returns the type of the device. See the RIM_ constants for possible values.
If IsHandle is false, then i is considered the index (starts at 1) of the device in the enumeration.
Otherwise it is the handle of the device.
______________________________________
AHKHID_GetDevName(i, IsHandle = False)

Returns the name of the device (or empty string on error, with error message in ErrorLevel).
If IsHandle is false, then i is considered the index (starts at 1) of the device in the enumeration.
Otherwise it is the handle of the device.
____________________________________________
AHKHID_GetDevInfo(i, Flag, IsHandle = False)

Retrieves info from the RID_DEVICE_INFO struct. To retrieve a member, simply use the corresponding flag. A list of flags
can be found at the top of the script (the constants starting with DI_). Each flag corresponds to a member in the struct.
If IsHandle is false, then i is considered the index (starts at 1) of the device in the enumeration. Otherwise it is the
handle of the device. Returns -1 on error (with error message in ErrorLevel).

See Example 1 for an example on how to use it. 
_______________________________________________________________________________
AHKHID_AddRegister(UsagePage = False, Usage = False, Handle = False, Flags = 0)

Allows you to queue up RAWINPUTDEVICE structures before doing the registration. To use it, you first need to initialize the
variable by calling AHKHID_AddRegister(iNumberOfElements). To then add to the stack, simply call it with the parameters you
want (eg. AHKHID_AddRegister(1,6,MyGuiHandle) for keyboards). When you're finally done, you just have to call
AHKHID_Register() with no parameters. The function returns -1 if the struct is full. Redimensioning the struct will erase
all previous structs added. On success, it returns the address of the array of structs (if you'd rather manipulate it
yourself).

See Example 2 for an example on how to use it.

You will need to do this if you want to use advance features of the RAWINPUTDEVICE flags. For example, if you want to
register all devices using Usage Page 1 but would like to exclude devices of Usage Page 1 using Usage 2 (keyboards), then
you need to place two elements in the array. The first one is AHKHID_AddRegister(1,0,MyGuiHandle,RIDEV_PAGEONLY) and the
second one is AHKHID_AddRegister(1,2,MyGuiHandle,RIDEV_EXCLUDE).

Tip: Have a look at all the flags you can use (see the constants starting with RIDEV_). The most useful is RIDEV_INPUTSINK.
Tip: Set Handle to 0 if you want the WM_INPUT messages to go to the window with keyboard focus.
Tip: To unregister, use the flag RIDEV_REMOVE. Note that you also need to use the RIDEV_PAGEONLY flag if the TLC was
registered with it.
____________________________________________________________________________
AHKHID_Register(UsagePage = False, Usage = False, Handle = False, Flags = 0)

This function can be used in two ways. If no parameters are specified, it will use the RAWINPUTDEVICE array created through
AHKHID_AddRegister() and register. Otherwise, it will register only the specified parameters. For example, if you just want
to register the mouse, you can simply do AHKHID_Register(1,2,MyGuiHandle). Returns 0 on success, returns -1 on error (with
error message in ErrorLevel).

See Example 2 for an example on how to use it with the RAWINPUTDEVICE.
See Example 3 for an example on how to use it only with the specified parameters.
____________________________________
AHKHID_GetRegisteredDevs(ByRef uDev)

This function allows you to get an array of the TLCs that have already been registered.
It fills uDev with an array of RAWINPUTDEVICE and returns the number of elements in the array.
Returns -1 on error (with error message in ErrorLevel).

See Example 2 for an example on how to use it.
______________________________________
AHKHID_GetInputInfo(InputHandle, Flag)

This function is used to retrieve the data upon receiving WM_INPUT messages. By passing the lParam of the WM_INPUT (0xFF00)
messages, it can retrieve all the members of the RAWINPUT structure, except the raw data coming from HID devices (use
AHKHID_GetInputData for that). To retrieve a member, simply specify the flag corresponding to the member you want, and call
the function. A list of all the flags can be found at the top of this script (the constants starting with II_). Returns -1
on error (with error message in ErrorLevel).

See Example 2 for an example on how to use it to retrieve each member of the structure.
See Example 3 for an example on how to interpret members which represent flags.

Tip: You have to use Critical in your message function or you might get invalid handle errors.
Tip: You can check the value of wParam to know if the application was in the foreground upon reception (see RIM_INPUT).
_____________________________________________
AHKHID_GetInputData(InputHandle, ByRef uData)

This function is used to retrieve the data sent by HID devices of type RIM_TYPEHID (ie. neither keyboard nor mouse) upon
receiving WM_INPUT messages. CAUTION: it does not check if the device is indeed of type HID. It is up to you to do so (you
can use GetInputInfo for that). Specify the lParam of the WM_INPUT (0xFF00) message and the function will put in uData the
raw data received from the device. It will then return the size (number of bytes) of uData. Returns -1 on error (with error
message in ErrorLevel).

See Example 2 for an example on how to use it (although you need an HID device of type RIM_TYPEHID to test it).

*/

AHKHID_Included := True
AHKHID_SetConstants:
;______________________________________
;Flags you can use in AHKHID_GetDevInfo
;http://msdn.microsoft.com/en-us/library/ms645581
DI_DEVTYPE                  := 4    ;Type of the device. See RIM_ constants.

DI_MSE_ID                   := 8    ;ID for the mouse device.
DI_MSE_NUMBEROFBUTTONS      := 12   ;Number of buttons for the mouse.
DI_MSE_SAMPLERATE           := 16   ;Number of data points per second. This information may not be applicable for every
                                    ;mouse device.
DI_MSE_HASHORIZONTALWHEEL   := 20   ;Vista and later only: TRUE if the mouse has a wheel for horizontal scrolling;
                                    ;otherwise, FALSE.

DI_KBD_TYPE                 := 8    ;Type of the keyboard. 
DI_KBD_SUBTYPE              := 12   ;Subtype of the keyboard. 
DI_KBD_KEYBOARDMODE         := 16   ;Scan code mode. 
DI_KBD_NUMBEROFFUNCTIONKEYS := 20   ;Number of function keys on the keyboard.
DI_KBD_NUMBEROFINDICATORS   := 24   ;Number of LED indicators on the keyboard.
DI_KBD_NUMBEROFKEYSTOTAL    := 28   ;Total number of keys on the keyboard. 

DI_HID_VENDORID             := 8    ;Vendor ID for the HID.
DI_HID_PRODUCTID            := 12   ;Product ID for the HID. 
DI_HID_VERSIONNUMBER        := 16   ;Version number for the HID. 
DI_HID_USAGEPAGE            := 20 | 0x0100  ;Top-level collection Usage Page for the device.
DI_HID_USAGE                := 22 | 0x0100  ;Top-level collection Usage for the device.
;________________________________________
;Flags you can use in AHKHID_GetInputInfo
;http://msdn.microsoft.com/en-us/library/ms645562
II_DEVTYPE          := 0    ;Type of the device generating the raw input data. See RIM_ constants.
II_DEVHANDLE        := 8    ;Handle to the device generating the raw input data.

II_MSE_FLAGS        := (08+A_PtrSize*2) | 0x0100  ;Mouse state. This member can be any reasonable combination of the
                                    ;following values -> see MOUSE constants.
II_MSE_BUTTONFLAGS  := (12+A_PtrSize*2) | 0x0100  ;Transition state of the mouse buttons. This member can be one or more of
                                    ;the following values -> see RI_MOUSE constants.
II_MSE_BUTTONDATA   := (14+A_PtrSize*2) | 0x1100  ;If usButtonFlags is RI_MOUSE_WHEEL, this member is a signed value that
                                    ;specifies the wheel delta.
II_MSE_RAWBUTTONS   := (16+A_PtrSize*2)           ;Raw state of the mouse buttons. 
II_MSE_LASTX        := (20+A_PtrSize*2) | 0x1000  ;Motion in the X direction. This is signed relative motion or absolute
                                    ;motion, depending on the value of usFlags.
II_MSE_LASTY        := (24+A_PtrSize*2) | 0x1000  ;Motion in the Y direction. This is signed relative motion or absolute
                                    ;motion, depending on the value of usFlags. 
II_MSE_EXTRAINFO    := (28+A_PtrSize*2)           ;Device-specific additional information for the event. 

II_KBD_MAKECODE     := (08+A_PtrSize*2) | 0x0100  ;Scan code from the key depression. The scan code for keyboard overrun is
                                    ;KEYBOARD_OVERRUN_MAKE_CODE.
II_KBD_FLAGS        := (10+A_PtrSize*2) | 0x0100  ;Flags for scan code information. It can be one or more of the following
                                    ;values -> see RI_KEY constants.
II_KBD_VKEY         := (14+A_PtrSize*2) | 0x0100  ;Microsoft Windows message compatible virtual-key code.
II_KBD_MSG          := (16+A_PtrSize*2)           ;Corresponding window message, for example WM_KEYDOWN, WM_SYSKEYDOWN, and
                                                  ;so forth.
II_KBD_EXTRAINFO    := (20+A_PtrSize*2)           ;Device-specific additional information for the event.

II_HID_SIZE         := (08+A_PtrSize*2)           ;Size, in bytes, of each HID input in bRawData.
II_HID_COUNT        := (12+A_PtrSize*2)           ;Number of HID inputs in bRawData.

;DO NOT USE WITH AHKHID_GetInputInfo. Use AHKHID_GetInputData instead to retrieve the raw data.
II_HID_DATA         := (16+A_PtrSize*2)           ;Raw input data as an array of bytes.
;__________________________________________________________________________________
;Device type values returned by AHKHID_GetDevType as well as DI_DEVTYPE and II_DEVTYPE
;http://msdn.microsoft.com/en-us/library/ms645568
RIM_TYPEMOUSE       := 0    ;The device is a mouse.
RIM_TYPEKEYBOARD    := 1    ;The device is a keyboard.
RIM_TYPEHID         := 2    ;The device is an Human Interface Device (HID) that is not a keyboard and not a mouse.
;_______________________________________________________________________________________________
;Different flags for RAWINPUTDEVICE structure (to be used with AHKHID_AddRegister and AHKHID_Register)
;http://msdn.microsoft.com/en-us/library/ms645565
RIDEV_REMOVE        := 0x00000001   ;If set, this removes the top level collection from the inclusion list. This tells the
                                    ;operating system to stop reading from a device which matches the top level collection.
RIDEV_EXCLUDE       := 0x00000010   ;If set, this specifies the top level collections to exclude when reading a complete
                                    ;usage page. This flag only affects a TLC whose usage page is already specified with
                                    ;RIDEV_PAGEONLY.
RIDEV_PAGEONLY      := 0x00000020   ;If set, this specifies all devices whose top level collection is from the specified
                                    ;usUsagePage. Note that usUsage must be zero. To exclude a particular top level
                                    ;collection, use RIDEV_EXCLUDE.
RIDEV_NOLEGACY      := 0x00000030   ;If set, this prevents any devices specified by usUsagePage or usUsage from generating
                                    ;legacy messages. This is only for the mouse and keyboard. See Remarks.
RIDEV_INPUTSINK     := 0x00000100   ;If set, this enables the caller to receive the input even when the caller is not in
                                    ;the foreground. Note that hwndTarget must be specified.
RIDEV_CAPTUREMOUSE  := 0x00000200   ;If set, the mouse button click does not activate the other window.
RIDEV_NOHOTKEYS     := 0x00000200   ;If set, the application-defined keyboard device hotkeys are not handled. However, the
                                    ;system hotkeys; for example, ALT+TAB and CTRL+ALT+DEL, are still handled. By default,
                                    ;all keyboard hotkeys are handled. RIDEV_NOHOTKEYS can be specified even if
                                    ;RIDEV_NOLEGACY is not specified and hwndTarget is NULL.
RIDEV_APPKEYS       := 0x00000400   ;Microsoft Windows XP Service Pack 1 (SP1): If set, the application command keys are
                                    ;handled. RIDEV_APPKEYS can be specified only if RIDEV_NOLEGACY is specified for a
                                    ;keyboard device.
RIDEV_EXINPUTSINK   := 0x00001000   ;Vista and later only: If set, this enables the caller to receive input in the
                                    ;background only if the foreground application does not process it. In other words, if
                                    ;the foreground application is not registered for raw input, then the background
                                    ;application that is registered will receive the input.
RIDEV_DEVNOTIFY     := 0x00002000   ;Vista and later only: If set, this enables the caller to receive WM_INPUT_DEVICE_CHANGE
                                    ;notifications for device arrival and device removal.
;__________________________________________________
;Different values of wParam in the WM_INPUT message
;http://msdn.microsoft.com/en-us/library/ms645590
RIM_INPUT       := 0    ;Input occurred while the application was in the foreground. The application must call
                        ;DefWindowProc so the system can perform cleanup.
RIM_INPUTSINK   := 1    ;Input occurred while the application was not in the foreground. The application must call
                        ;DefWindowProc so the system can perform the cleanup.
;__________________________________
;Flags for GetRawInputData API call
;http://msdn.microsoft.com/en-us/library/ms645596
RID_INPUT    := 0x10000003    ;Get the raw data from the RAWINPUT structure.
RID_HEADER   := 0x10000005    ;Get the header information from the RAWINPUT structure.
;_____________________________________
;Flags for RAWMOUSE (part of RAWINPUT)
;http://msdn.microsoft.com/en-us/library/ms645578

;Flags for the II_MSE_FLAGS member
MOUSE_MOVE_RELATIVE         := 0     ;Mouse movement data is relative to the last mouse position.
MOUSE_MOVE_ABSOLUTE         := 1     ;Mouse movement data is based on absolute position.
MOUSE_VIRTUAL_DESKTOP       := 0x02  ;Mouse coordinates are mapped to the virtual desktop (for a multiple monitor system)
MOUSE_ATTRIBUTES_CHANGED    := 0x04  ;Mouse attributes changed; application needs to query the mouse attributes.

;Flags for the II_MSE_BUTTONFLAGS member
RI_MOUSE_LEFT_BUTTON_DOWN   := 0x0001   ;Self-explanatory
RI_MOUSE_LEFT_BUTTON_UP     := 0x0002   ;Self-explanatory
RI_MOUSE_RIGHT_BUTTON_DOWN  := 0x0004   ;Self-explanatory
RI_MOUSE_RIGHT_BUTTON_UP    := 0x0008   ;Self-explanatory
RI_MOUSE_MIDDLE_BUTTON_DOWN := 0x0010   ;Self-explanatory
RI_MOUSE_MIDDLE_BUTTON_UP   := 0x0020   ;Self-explanatory
RI_MOUSE_BUTTON_4_DOWN      := 0x0040   ;XBUTTON1 changed to down.
RI_MOUSE_BUTTON_4_UP        := 0x0080   ;XBUTTON1 changed to up.
RI_MOUSE_BUTTON_5_DOWN      := 0x0100   ;XBUTTON2 changed to down.
RI_MOUSE_BUTTON_5_UP        := 0x0200   ;XBUTTON2 changed to up.
RI_MOUSE_WHEEL              := 0x0400   ;Raw input comes from a mouse wheel. The wheel delta is stored in usButtonData.
;____________________________________________
;Flags for the RAWKEYBOARD (part of RAWINPUT)
;http://msdn.microsoft.com/en-us/library/ms645575

;Flag for the II_KBD_MAKECODE member in the event of a keyboard overrun
KEYBOARD_OVERRUN_MAKE_CODE  := 0xFF

;Flags for the II_KBD_FLAGS member
RI_KEY_MAKE             := 0
RI_KEY_BREAK            := 1
RI_KEY_E0               := 2
RI_KEY_E1               := 4
RI_KEY_TERMSRV_SET_LED  := 8
RI_KEY_TERMSRV_SHADOW   := 0x10
;____________________________________
;AHKHID FUNCTIONS

If Not AHKHID_Included
    Return

AHKHID_UseConstants() {
    Global ;To make the constants global
    AHKHID_Included := False
    Gosub, AHKHID_SetConstants
}

AHKHID_Initialize(bRefresh = False) {
    Static uHIDList, bInitialized := False
    
    If bInitialized And Not bRefresh
        Return &uHIDList
    
    ;Get the device count
    r := DllCall("GetRawInputDeviceList", "Ptr", 0, "UInt*", iCount, "UInt", A_PtrSize * 2)
    
    ;Check for error
    If (r = -1) Or ErrorLevel {
        ErrorLevel = GetRawInputDeviceList call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return -1
    }
    
    ;Prep var
    VarSetCapacity(uHIDList, iCount * (A_PtrSize * 2))
    r := DllCall("GetRawInputDeviceList", "Ptr", &uHIDList, "UInt*", iCount, "UInt", A_PtrSize * 2)
    If (r = -1) Or ErrorLevel {
        ErrorLevel = GetRawInputDeviceList call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return -1
    }
    
    bInitialized := True
    Return &uHIDList
}

AHKHID_GetDevCount() {
    
    ;Get the device count
    r := DllCall("GetRawInputDeviceList", "Ptr", 0, "UInt*", iCount, "UInt", A_PtrSize * 2)
    
    ;Check for error
    If (r = -1) Or ErrorLevel {
        ErrorLevel = GetRawInputDeviceList call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return -1
    } Else Return iCount
}

AHKHID_GetDevHandle(i) {
    Return NumGet(AHKHID_Initialize(), (i - 1) * (A_PtrSize * 2))
}

AHKHID_GetDevIndex(Handle) {
    Loop % AHKHID_GetDevCount()
        If (NumGet(AHKHID_Initialize(), (A_Index - 1) * (A_PtrSize * 2)) = Handle)
            Return A_Index
    Return 0
}

AHKHID_GetDevType(i, IsHandle = False) {
    Return Not IsHandle ? NumGet(AHKHID_Initialize(), ((i - 1) * (A_PtrSize * 2)) + A_PtrSize, "UInt")
    : NumGet(AHKHID_Initialize(), ((AHKHID_GetDevIndex(i) - 1) * (A_PtrSize * 2)) + A_PtrSize, "UInt")
}

AHKHID_GetDevName(i, IsHandle = False) {
    
    ;Get index if i is handle
    h := IsHandle ? i : AHKHID_GetDevHandle(i)
    
    ;Get device name length.                                RIDI_DEVICENAME
    r := DllCall("GetRawInputDeviceInfo", "Ptr", h, "UInt", 0x20000007, "Ptr", 0, "UInt*", iLength)
    If (r = -1) Or ErrorLevel {
        ErrorLevel = GetRawInputDeviceInfo call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return ""
    }
	
    ;Get device name.
    VarSetCapacity(s, (iLength + 1) * 2)                         ;RIDI_DEVICENAME
    r := DllCall("GetRawInputDeviceInfo", "Ptr", h, "UInt", 0x20000007, "Str", s, "UInt*", iLength)
    If (r = -1) Or ErrorLevel {
        ErrorLevel = GetRawInputDeviceInfo call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return ""
    }
    
    Return s
}

AHKHID_GetDevInfo(i, Flag, IsHandle = False) {
    Static uInfo, iLastHandle := 0
    
    ;Get index if i is handle
    h := IsHandle ? i : AHKHID_GetDevHandle(i)
    
    ;Check if the handle changed
    If (h = iLastHandle) ;It's the same device. No need to call again
        Return NumGet(uInfo, Flag, AHKHID_NumIsShort(Flag) ? "UShort" : "UInt")
    Else {
        
        ;Get device info buffer size.                           RIDI_DEVICEINFO
        r := DllCall("GetRawInputDeviceInfo", "Ptr", h, "UInt", 0x2000000b, "Ptr", 0, "UInt*", iLength)
        If (r = -1) Or ErrorLevel {
            ErrorLevel = GetRawInputDeviceInfo call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            Return -1
        }
        
        ;Get device info
        VarSetCapacity(uInfo, iLength)
        NumPut(iLength, uInfo, 0, "UInt") ;Put length in struct RIDI_DEVICEINFO
        r := DllCall("GetRawInputDeviceInfo", "Ptr", h, "UInt", 0x2000000b, "Ptr", &uInfo, "UInt*", iLength)
        If (r = -1) Or ErrorLevel {
            ErrorLevel = GetRawInputDeviceInfo call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            Return -1
        }
        
        ;Successful. Keep handle.
        iLastHandle := h
        
        ;Retrieve data
        Return NumGet(uInfo, Flag, AHKHID_NumIsShort(Flag) ? "UShort" : "UInt")
    }
    
    Return 0
}

AHKHID_AddRegister(UsagePage = False, Usage = False, Handle = False, Flags = 0) {
    Static uDev, iIndex := 0, iCount := 0
    
    ;Check if we just want the address
    If Not (UsagePage Or Usage Or Handle Or Flags)
        Return &uDev
    ;Check if we just want the count
    Else If (UsagePage = "Count")
        Return iCount
    ;Check if we're dimensioning the struct
    Else If UsagePage And Not (Usage Or Handle Or Flags) {
        iCount := UsagePage
        iIndex := 0
        VarSetCapacity(uDev, iCount * (8 + A_PtrSize))
        Return &uDev
    }
    
    ;Check if there's space before adding data to struct
    If (iIndex = iCount)
        Return -1    ;Full capacity
    
    ;Check if hwnd needs to be null. RIDEV_REMOVE, RIDEV_EXCLUDE
    Handle := ((Flags & 0x00000001) Or (Flags & 0x00000010)) ? 0 : Handle

    ;Put in struct
    NumPut(UsagePage, uDev, (iIndex * (8 + A_PtrSize)) + 0, "UShort")
    NumPut(Usage,     uDev, (iIndex * (8 + A_PtrSize)) + 2, "UShort")
    NumPut(Flags,     uDev, (iIndex * (8 + A_PtrSize)) + 4, "UInt")
    NumPut(Handle,    uDev, (iIndex * (8 + A_PtrSize)) + 8, "Ptr")
    
    ;Move to next slot
    iIndex += 1
    
    Return &uDev
}

AHKHID_Register(UsagePage = False, Usage = False, Handle = False, Flags = 0) {
    
    ;Check if we're using the AddRegister array or only a single struct
    If Not (UsagePage Or Usage Or Handle Or Flags) {
        
        ;Call
        r := DllCall("RegisterRawInputDevices", "Ptr", AHKHID_AddRegister(), "UInt", AHKHID_AddRegister("Count"), "UInt", 8 + A_PtrSize)
        
        ;Check for error
        If Not r {
            ErrorLevel = RegisterRawInputDevices call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            Return -1
        }
        
    ;Build struct and call
    } Else {
        
        ;Prep var
        VarSetCapacity(uDev, (8 + A_PtrSize), 0)
        
        ;Check if hwnd needs to be null. RIDEV_REMOVE, RIDEV_EXCLUDE
        Handle := ((Flags & 0x00000001) Or (Flags & 0x00000010)) ? 0 : Handle
        
        NumPut(UsagePage, uDev, 0, "UShort")
        NumPut(Usage,     uDev, 2, "UShort")
        NumPut(Flags,     uDev, 4, "UInt")
        NumPut(Handle,    uDev, 8, "Ptr")
        
        ;Call
        r := DllCall("RegisterRawInputDevices", "Ptr", &uDev, "UInt", 1, "UInt", 8 + A_PtrSize)
        
        ;Check for error
        If Not r Or ErrorLevel {
            ErrorLevel = RegisterRawInputDevices call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            Return -1
        }
    }
    
    Return 0
}

AHKHID_GetRegisteredDevs(ByRef uDev) {
    
    ;Get length
    VarSetCapacity(iCount, 4, 0)
    r := DllCall("GetRegisteredRawInputDevices", "Ptr", 0, "UInt*", iCount, "UInt", 8 + A_PtrSize)
    If ErrorLevel {
        ErrorLevel = GetRegisteredRawInputDevices call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return -1
    }
    
    If (iCount > 0) {
        
        ;Prep var
        VarSetCapacity(uDev, iCount * (8 + A_PtrSize))
        
        ;Call
        r := DllCall("GetRegisteredRawInputDevices", "Ptr", &uDev, "UInt*", iCount, "UInt", 8 + A_PtrSize)
        If (r = -1) Or ErrorLevel {
            ErrorLevel = GetRegisteredRawInputDevices call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            Return -1
        }
    }
    
    Return iCount
}

AHKHID_GetInputInfo(InputHandle, Flag) {
    Static uRawInput, iLastHandle := 0
    
    ;Check if it's the same handle
    If (InputHandle = iLastHandle) ;We can retrieve the data without having to call again
        Return NumGet(uRawInput, Flag, AHKHID_NumIsShort(Flag) ? (AHKHID_NumIsSigned(Flag) ? "Short" : "UShort") : (AHKHID_NumIsSigned(Flag) ? "Int" : (Flag = 8 ? "Ptr" : "UInt")))
    Else {    ;We need to get a fresh copy
        
        ;Get raw data size                                           RID_INPUT
        r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + A_PtrSize * 2)
        If (r = -1) Or ErrorLevel {
            ErrorLevel = GetRawInputData call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            Return -1
        }
        
        ;Prep var
        VarSetCapacity(uRawInput, iSize)
        
        ;Get raw data                                                RID_INPUT
        r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", iSize, "UInt", 8 + A_PtrSize * 2)
        If (r = -1) Or ErrorLevel {
            ErrorLevel = GetRawInputData call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            Return -1
        } Else If (r <> iSize) {
            ErrorLevel = GetRawInputData did not return the correct size.`nSize returned: %r%`nSize allocated: %iSize%
            Return -1
        }
        
        ;Keep handle reference of current uRawInput
        iLastHandle := InputHandle
        
        ;Retrieve data
        Return NumGet(uRawInput, Flag, AHKHID_NumIsShort(Flag) ? (AHKHID_NumIsSigned(Flag) ? "Short" : "UShort") : (AHKHID_NumIsSigned(Flag) ? "Int" : (Flag = 8 ? "Ptr" : "UInt")))
    }
    
    Return 0
}

AHKHID_GetInputData(InputHandle, ByRef uData) {
    
    ;Get raw data size                                           RID_INPUT
    r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + A_PtrSize * 2)
    If (r = -1) Or ErrorLevel {
        ErrorLevel = GetRawInputData call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return -1
    }
    
    ;Prep var
    VarSetCapacity(uRawInput, iSize)
    
    ;Get raw data                                                RID_INPUT
    r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", iSize, "UInt", 8 + A_PtrSize * 2)
    If (r = -1) Or ErrorLevel {
        ErrorLevel = GetRawInputData call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return -1
    } Else If (r <> iSize) {
        ErrorLevel = GetRawInputData did not return the correct size.`nSize returned: %r%`nSize allocated: %iSize%
        Return -1
    }
    
    ;Get the size of each HID input and the number of them
    iSize   := NumGet(uRawInput, 8 + A_PtrSize * 2 + 0, "UInt") ;ID_HID_SIZE
    iCount  := NumGet(uRawInput, 8 + A_PtrSize * 2 + 4, "UInt") ;ID_HID_COUNT
    
    ;Allocate memory
    VarSetCapacity(uData, iSize * iCount)
    
    ;Copy bytes
    DllCall("RtlMoveMemory", UInt, &uData, UInt, &uRawInput + 8 + A_PtrSize * 2 + 8, UInt, iSize * iCount)
    
    Return (iSize * iCount)
}

;Internal use only
AHKHID_NumIsShort(ByRef Flag) {
    If (Flag & 0x0100) {
        Flag ^= 0x0100    ;Remove it
        Return True
    } Return False
}

;Internal use only
AHKHID_NumIsSigned(ByRef Flag) {
    If (Flag & 0x1000) {
        Flag ^= 0x1000    ;Remove it
        Return True
    } Return False
}

CoolHID.ahk - Something that Cloaker may have put together? Not sure:

Code: Select all

global HIDP_STATUS_SUCCESS := 0x110000

class HID {
	static STATUS_SUCCESS := 0x110000

	class HID_CAPS {
		__New(pData) {
            this.Usage := NumGet(pData + 0, 0, "Short")
            this.UsagePage := NumGet(pData + 0, 2, "Short")
            this.InputReportByteLength := NumGet(pData + 0, 4, "UShort")
            this.OutputReportByteLength := NumGet(pData + 0, 6, "UShort")
            this.FeatureReportByteLength := NumGet(pData + 0, 8, "UShort")
            this.Reserved := NumGet(pData + 0, 10, "UShort")
            this.NumberLinkCollectionNodes := NumGet(pData + 0, 44, "UShort")
            this.NumberInputButtonCaps := NumGet(pData + 0, 46, "UShort")
            this.NumberInputValueCaps := NumGet(pData + 0, 48, "UShort")
            this.NumberInputDataIndices := NumGet(pData + 0, 50, "UShort")
            this.NumberOutputButtonCaps := NumGet(pData + 0, 52, "UShort")
            this.NumberOutputValueCaps := NumGet(pData + 0, 54, "UShort")
            this.NumberOutputDataIndices := NumGet(pData + 0, 56, "UShort")
            this.NumberFeatureButtonCaps := NumGet(pData + 0, 58, "UShort")
            this.NumberFeatureValueCaps := NumGet(pData + 0, 60, "UShort")
            this.NumberFeatureDataIndices := NumGet(pData + 0, 62, "UShort")
		}
	}

	class HID_BUTTON_CAPS {
		__New(pData) {
			this.UsagePage := NumGet(pData + 0, 0, "Short")
			this.ReportID := NumGet(pData + 0, 2, "UChar")
			this.IsAlias := NumGet(pData + 0, 3, "Char")
			this.BitField := NumGet(pData + 0, 4, "UShort")
			this.LinkCollection := NumGet(pData + 0, 6, "UShort")
			this.LinkUsage := NumGet(pData + 0, 8, "Short")
			this.LinkUsagePage := NumGet(pData + 0, 10, "Short")
			this.IsRange := NumGet(pData + 0, 12, "Char")
			this.IsStringRange := NumGet(pData + 0, 13, "Char")
			this.IsDesignatorRange := NumGet(pData + 0, 14, "Char")
			this.IsAbsolute := NumGet(pData + 0, 15, "Char")
			this.ReportCount := NumGet(pData + 0, 16, "UShort")
			; 18: USHORT Reserved 
			; 20: ULONG Reserved[9] 36
			; 56


			if (this.IsRange) {
				Range := pData + 56

				this.UsageMin := NumGet(Range + 0, 0, "Short")
				this.UsageMax := NumGet(Range + 0, 2, "Short")
				this.StringMin := NumGet(Range + 0, 4, "UShort")
				this.StringMax := NumGet(Range + 0, 6, "UShort")
				this.DesignatorMin := NumGet(Range + 0, 8, "UShort")
				this.DesignatorMax := NumGet(Range + 0, 10, "UShort")
				this.DataIndexMin := NumGet(Range + 0, 12, "UShort")
				this.DataIndexMax := NumGet(Range + 0, 14, "UShort")
			}
			else {
				NotRange := pData + 56

				this.Usage := NumGet(NotRange + 0, 0, "Short")
				this.Reserved1 := NumGet(NotRange + 0, 2, "Short")
				this.StringIndex := NumGet(NotRange + 0, 4, "UShort")
				this.Reserved2 := NumGet(NotRange + 0, 6, "UShort")
				this.DesignatorIndex := NumGet(NotRange + 0, 8, "UShort")
				this.Reserved3 := NumGet(NotRange + 0, 10, "UShort")
				this.DataIndex := NumGet(NotRange + 0, 12, "UShort")
				this.Reserved4 := NumGet(NotRange + 0, 14, "UShort")
			}
		}
	}

	class HID_VALUE_CAPS {
		__New(pData) {
			this.UsagePage := NumGet(pData + 0, 0, "Short")
			this.ReportID := NumGet(pData + 0, 2, "UChar")
			this.IsAlias := NumGet(pData + 0, 3, "Char")
			this.BitField := NumGet(pData + 0, 4, "UShort")
			this.LinkCollection := NumGet(pData + 0, 6, "UShort")
			this.LinkUsage := NumGet(pData + 0, 8, "Short")
			this.LinkUsagePage := NumGet(pData + 0, 10, "Short")
			this.IsRange := NumGet(pData + 0, 12, "Char")
			this.IsStringRange := NumGet(pData + 0, 13, "Char")
			this.IsDesignatorRange := NumGet(pData + 0, 14, "Char")
			this.IsAbsolute := NumGet(pData + 0, 15, "Char")
			this.HasNull := NumGet(pData + 0, 16, "Char")
			this.Reserved := NumGet(pData + 0, 17, "UChar")
			this.BitSize := NumGet(pData + 0, 18, "UShort")
			this.ReportCount := NumGet(pData + 0, 20, "UShort")
			this.Reserved2 := NumGet(pData + 0, 22, "UShort")
			this.UnitsExp := NumGet(pData + 0, 32, "UInt")
			this.Units := NumGet(pData + 0, 36, "UInt")
			this.LogicalMin := NumGet(pData + 0, 40, "Int")
			this.LogicalMax := NumGet(pData + 0, 44, "Int")
			this.PhysicalMin := NumGet(pData + 0, 48, "Int")
			this.PhysicalMax := NumGet(pData + 0, 52, "Int")

			if (this.IsRange) {
				Range := pData + 56

				this.UsageMin := NumGet(Range + 0, 0, "Short")
				this.UsageMax := NumGet(Range + 0, 2, "Short")
				this.StringMin := NumGet(Range + 0, 4, "UShort")
				this.StringMax := NumGet(Range + 0, 6, "UShort")
				this.DesignatorMin := NumGet(Range + 0, 8, "UShort")
				this.DesignatorMax := NumGet(Range + 0, 10, "UShort")
				this.DataIndexMin := NumGet(Range + 0, 12, "UShort")
				this.DataIndexMax := NumGet(Range + 0, 14, "UShort")
			}
			else {
				NotRange := pData + 56

				this.Usage := NumGet(NotRange + 0, 0, "Short")
				this.Reserved1 := NumGet(NotRange + 0, 2, "Short")
				this.StringIndex := NumGet(NotRange + 0, 4, "UShort")
				this.Reserved2 := NumGet(NotRange + 0, 6, "UShort")
				this.DesignatorIndex := NumGet(NotRange + 0, 8, "UShort")
				this.Reserved3 := NumGet(NotRange + 0, 10, "UShort")
				this.DataIndex := NumGet(NotRange + 0, 12, "UShort")
				this.Reserved4 := NumGet(NotRange + 0, 14, "UShort")
			}
		}

	}

	GetButtonChannelCapabilities(Channel) {
		static HidP_Input := 0
		static HidP_Output := 1

		NumberButtonCaps := this.Capabilities["Number" Channel "ButtonCaps"]

		if (NumberButtonCaps != 0) {
			VarSetCapacity(ButtonCaps, NumberButtonCaps * 72, 0)

			ChannelNumber := Channel = "Input" ? 0 : 1

			r := DllCall("Hid.dll\HidP_GetButtonCaps"
				, "UInt", ChannelNumber
				, "Ptr", &ButtonCaps
				, "UInt*", NumberButtonCaps
				, "Ptr", this.pPreparseData
				, "UInt")
			If (r != HID.STATUS_SUCCESS) Or ErrorLevel {
				MsgBox, HidP_GetButtonCaps call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
			}
		}

		this.ButtonCapabilities[Channel] := []

		loop, % NumberButtonCaps {
			Offset := (A_Index - 1) * 72

			this.ButtonCapabilities[Channel].Push(new HID.HID_BUTTON_CAPS(&ButtonCaps + Offset))
		}
	}

	GetButtonCapabilities() {
		this.ButtonCapabilities := {}

		this.GetButtonChannelCapabilities("Input")
		this.GetButtonChannelCapabilities("Output")
	}

	GetValueChannelCapabilities(Channel) {
		static HidP_Input := 0
		static HidP_Output := 1

		NumberValueCaps := this.Capabilities["Number" Channel "ValueCaps"]

		if (NumberValueCaps != 0) {
			VarSetCapacity(ValueCaps, NumberValueCaps * 72, 0)

			ChannelNumber := Channel = "Input" ? 0 : 1
			ChannelNumber := Channel = "Feature" ? 2 : ChannelNumber

			r := DllCall("Hid.dll\HidP_GetValueCaps"
				, "UInt", ChannelNumber
				, "Ptr", &ValueCaps
				, "UInt*", NumberValueCaps
				, "Ptr", this.pPreparseData
				, "UInt")
			If (r != HID.STATUS_SUCCESS) Or ErrorLevel {
				MsgBox, HidP_GetValueCaps call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
			}
		}

		this.ValueCapabilities[Channel] := []

		loop, % NumberValueCaps {
			Offset := (A_Index - 1) * 72

			this.ValueCapabilities[Channel].Push(new HID.HID_VALUE_CAPS(&ValueCaps + Offset))
		}
	}

	GetValueCapabilities() {
		this.ValueCapabilities := {}

		this.GetValueChannelCapabilities("Input")
		this.GetValueChannelCapabilities("Output")
		this.GetValueChannelCapabilities("Feature")
	}

	__New(Handle) {
		this.Handle := Handle

		static RIDI_PREPARSEDDATA := 0x20000005

		r := DllCall("GetRawInputDeviceInfo", "Ptr", Handle, "UInt", RIDI_PREPARSEDDATA, "Ptr", 0, "UInt*", BufferSize)
		If (r = -1) Or ErrorLevel {
			MsgBox, GetRawInputDeviceInfo call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
		}

		this.SetCapacity("PreparseData", BufferSize)
		this.pPreparseData := this.GetAddress("PreparseData")

		r := DllCall("GetRawInputDeviceInfo", "Ptr", Handle, "UInt", RIDI_PREPARSEDDATA, "Ptr", this.pPreparseData, "UInt*", BufferSize)
		If (r = -1) Or ErrorLevel {
			MsgBox, GetRawInputDeviceInfo call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
		}

		VarSetCapacity(Capabilities, 64, 0)

		r := DllCall("Hid.dll\HidP_GetCaps"
			, "Ptr", this.pPreparseData
			, "Ptr", &Capabilities
			, "UInt")
		If (r != HID.STATUS_SUCCESS) Or ErrorLevel {
			MsgBox, HidP_GetCaps call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
		}

		this.Capabilities := new HID.HID_CAPS(&Capabilities)
		this.GetButtonCapabilities()
		this.GetValueCapabilities()
	}

	MaxUsagesForReportAndPage(ReportChannel, UsagePage) {
		r := DllCall("Hid.dll\HidP_MaxUsageListLength"
			, "Ptr", ReportChannel
			, "UInt", UsagePage
			, "Ptr", this.pPreparseData
			, "UInt")

		if (r = 0 || ErrorLevel) {
			MsgBox, HidP_MaxUsageListLength call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
		}

		return r
	}

	AddReport(pReport, ReportSize) {
		static HidP_Input := 0

		Result := {}
		Buttons := Result.Buttons := {}
		Values := Result.Values := {}

		for k, Button in this.ButtonCapabilities.Input {
			if (Buttons.HasKey(Button.UsagePage)) {
				continue
			}

			Buttons[Button.UsagePage] := []

			UsageCount := this.MaxUsagesForReportAndPage(HidP_Input, Button.UsagePage)

			VarSetCapacity(Usages, UsageCount * 4, 0)
			r := DllCall("Hid.dll\HidP_GetUsages"
				, "Ptr", HidP_Input
				, "UInt", Button.UsagePage
				, "UInt", Button.LinkCollection
				, "Ptr", &Usages
				, "UInt*", UsageCount
				, "Ptr", this.pPreparseData
				, "Ptr", pReport
				, "UInt", ReportSize
				, "UInt")
			If (r != HID.STATUS_SUCCESS) Or ErrorLevel {
				MsgBox, HidP_GetUsages call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
			}

			loop, % UsageCount {
				Usage := NumGet(&Usages + 0, (A_Index - 1) * 2, "UShort")

				Buttons[Button.UsagePage].Push(Usage)
			}
		}
		
		for k, Value in this.ValueCapabilities.Input {
			if (!IsObject(Values[Value.UsagePage, Value.Usage])) {
				Values[Value.UsagePage, Value.Usage] := []
			}

			if (Value.ReportCount > 1) {
				RawValueSize := Ceil((Value.ReportCount * Value.BitSize) / 8)

				VarSetCapacity(RawValue, RawValueSize, 0)

				r := DllCall("Hid.dll\HidP_GetUsageValueArray"
					, "Ptr", HidP_Input
					, "UInt", Value.UsagePage
					, "UInt", Value.LinkCollection
					, "UInt", Value.Usage
					, "UInt", &RawValue
					, "UInt", RawValueSize
					, "Ptr", this.pPreparseData
					, "Ptr", pReport
					, "UInt", ReportSize
					, "UInt")
				
				If (r != HID.STATUS_SUCCESS) Or ErrorLevel {
					MsgBox, HidP_GetUsageValue call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
				}

				Values[Value.UsagePage, Value.Usage].Push(Bin2Hex(&RawValue, RawValuesSize))
			}
			else {			
				r := DllCall("Hid.dll\HidP_GetUsageValue"
					, "Ptr", HidP_Input
					, "UInt", Value.UsagePage
					, "UInt", Value.LinkCollection
					, "UInt", Value.Usage
					, "UInt*", UsageValue
					, "Ptr", this.pPreparseData
					, "Ptr", pReport
					, "UInt", ReportSize
					, "UInt")
				
				If (r != HID.STATUS_SUCCESS) Or ErrorLevel {
					MsgBox, HidP_GetUsageValue call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
				}

				Values[Value.UsagePage, Value.Usage].Push(UsageValue)
			}
		}

		return Result
	}

	GetUsagePageName(UsagePage) {
		static Names := ["Generic Desktop", "Simulation Controls"
		               , "VR Controls", "Sport Controls"
		               , "Game Controls", "Generic Device Controls"
					   , "Keyboard/Keypad", "LED"
					   , "Button", "Ordinal"
					   , "Telephony Device", "Consumer"
					   , "Digitizers"]

		if (UsagePage = 0 || UsagePage > Names.Count()) {
			return "Undefined/unimplemented usage page (" UsagePage ")"
		}
		
		return Names[UsagePage]
	}

	GetUsageName(UsagePage, Usage) {
		static USAGE_PAGE_BUTTON := 0x9
		static USAGE_PAGE_ORDINAL := 0xA

		if (UsagePage = USAGE_PAGE_BUTTON) {
			return "Button " Usage
		}
		else if (UsagePage = USAGE_PAGE_ORDINAL) {
			return "Instance 1"
		}

		Result := HID.UsagePages[UsagePage][Usage]

		if (Result) {
			return Result
		}

		return "Undefined/unimplemented usage (" UsagePage ":" Usage ")"
	}

	LoadUsageTable(FileName) {
		Text := FileOpen(FileName, "r").Read()
		
		UsagePages := {}

		for k, Page in StrSplit(Text, "|") {
			Parts := StrSplit(Page, ":")

			PageNumber := Parts[1]
			PageNumber += 0

			Usages := StrReplace(Parts[2], "`r`n", "")

			UsagePages[PageNumber] := []

			for k, Usage in StrSplit(Usages, ",") {
				if (k = 1) {
					continue ; Skip first (null) one to translate from 0 index
				}

				UsagePages[PageNumber].Push(StrReplace(Trim(Usage), """", ""))
			}
		}

		return UsagePages
	}

	static UsagePages := HID.LoadUsageTable(A_ScriptDir "/HIDUsagePageTable.txt")
}

HIDUsagePageTable.txt - Used by CoolHID.ahk for the pen's actions (see 0x0D):

Code: Select all

0x01:
    0, "Pointer", "Mouse", 0, "Joystick", "Game Pad", "Keyboard", "Keypad",
    "Multi-axis Controller", "Tablet PC System Controls", "Water Cooling Device", "Computer Chassis Device", "Wireless Radio Controls", "Portable Device Control", 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "X", "Y", "Z", "Rx", "Ry", "Rz", "Slider", "Dial",
    "Wheel", "Hat switch", "Counted Buffer", "Byte Count", "Motion Wakeup", "Start", "Select", 0,
    "Vx", "Vy", "Vz", "Vbrx", "Vbry", "Vbrz", "Vno", "Feature Notification",
    "Resolution Multiplier", 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "System Control", "System Power Down", "System Sleep", "System Wake Up", "System Context Menu", "System Main Menu", "System App Menu", "System Menu Help",
    "System Menu Exit", "System Menu Select", "System Menu Right", "System Menu Left", "System Menu Up", "System Menu Down", "System Cold Restart", "System Warm Restart",
    "D-pad Up", "D-pad Down", "D-pad Right", "D-pad Left", 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "System Dock", "System Undock", "System Setup", "System Break", "System Debugger Break", "Application Break", "Application Debugger Break", "System Speaker Mute",
    "System Hibernate", 0, 0, 0, 0, 0, 0, 0,
    "System Display Invert", "System Display Internal", "System Display External", "System Display Both", "System Display Dual", "System Display Toggle Int/Ext", "System Display Swap Primary/Secondary", "System Display LCD Autoscale",
    0, 0, 0, 0, 0, 0, 0, 0,
    "Sensor Zone", "RPM", "Coolant Level", "Coolant Critical Level", "Coolant Pump", "Chassis Enclosure", "Wireless Radio Button", "Wireless Radio LED",
    "Wireless Radio Slider Switch", "System Display Rotation Lock Button", "System Display Rotation Lock Slider Switch", "Control Enable", 0, 0, 0, 0
|0x02:
    0, "Flight Simulation Device", "Automobile Simulation Device", "Tank Simulation Device", "Spaceship Simulation Device", "Submarine Simulation Device", "Sailing Simulation Device", "Motorcycle Simulation Device",
    "Sports Simulation Device", "Airplane Simulation Device", "Helicopter Simulation Device", "Magic Carpet Simulation Device", "Bicycle Simulation Device", 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Flight Control Stick", "Flight Stick", "Cyclic Control", "Cyclic Trim", "Flight Yoke", "Track Control", 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Aileron", "Aileron Trim", "Anti-Torque Control", "Autopilot Enable", "Chaff Release", "Collective Control", "Dive Brake", "Electronic Countermeasures",
    "Elevator", "Elevator Trim", "Rudder", "Throttle", "Flight Communications", "Flare Release", "Landing Gear", "Toe Brake",
    "Trigger", "Weapons Arm", "Weapons Select", "Wing Flaps", "Accelerator", "Brake", "Clutch", "Shifter",
    "Steering", "Turret Direction", "Barrel Elevation", "Dive Plane", "Ballast", "Bicycle Crank", "Handle Bars", "Front Brake",
    "Rear Brake", 0, 0, 0, 0, 0, 0, 0
|0x03:
    0, "Belt", "Body Suit", "Flexor", "Glove", "Head Tracker", "Head Mounted Display", "Hand Tracker",
    "Oculometer", "Vest", "Animatronic Device", 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Stereo Enable", "Display Enable", 0, 0, 0, 0, 0, 0
|0x04:
    0, "Baseball Bat", "Golf Club", "Rowing Machine", "Treadmill", 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Oar", "Slope", "Rate", "Stick Speed", "Stick Face Angle", "Stick Heel/Toe", "Stick Follow Through", "Stick Tempo",
    "Stick Type", "Stick Height", 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Putter", "1 Iron", "2 Iron", "3 Iron", "4 Iron", "5 Iron", "6 Iron", "7 Iron",
    "8 Iron", "9 Iron", "10 Iron", "11 Iron", "Sand Wedge", "Loft Wedge", "Power Wedge", "1 Wood",
    "3 Wood", "5 Wood", "7 Wood", "9 Wood", 0, 0, 0, 0
|0x05:
    0, "3D Game Controller", "Pinball Device", "Gun Device", 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Point of View", "Turn Right/Left", "Pitch Forward/Backward", "Roll Right/Left", "Move Right/Left", "Move Forward/Backward", "Move Up/Down", "Lean Right/Left",
    "Lean Forward/Backward", "Height of POV", "Flipper", "Secondary Flipper", "Bump", "New Game", "Shoot Ball", "Player",
    "Gun Bolt", "Gun Clip", "Gun Selector", "Gun Single Shot", "Gun Burst", "Gun Automatic", "Gun Safety", "Gamepad Fire/Jump",
    "Gamepad Trigger", 0, 0, 0, 0, 0, 0, 0
|0x06:
    0, "Background Controls", 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Battery Strength", "Wireless Channel", "Wireless ID", "Discover Wireless Control", "Security Code Character Entered", "Security Code Character Erased", "Security Code Cleared", "Sequence ID",
    "Sequence ID Reset", "RF Signal Strength", 0, 0, 0, 0, 0, 0
|0x07:
    0, "Keyboard ErrorRollOver", "Keyboard POSTFail", "Keyboard ErrorUndefined", "Keyboard a and A", "Keyboard b and B", "Keyboard c and C", "Keyboard d and D",
    "Keyboard e and E", "Keyboard f and F", "Keyboard g and G", "Keyboard h and H", "Keyboard i and I", "Keyboard j and J", "Keyboard k and K", "Keyboard l and L",
    "Keyboard m and M", "Keyboard n and N", "Keyboard o and O", "Keyboard p and P", "Keyboard q and Q", "Keyboard r and R", "Keyboard s and S", "Keyboard t and T",
    "Keyboard u and U", "Keyboard v and V", "Keyboard w and W", "Keyboard x and X", "Keyboard y and Y", "Keyboard z and Z", "Keyboard 1 and !", "Keyboard 2 and @",
    "Keyboard 3 and #", "Keyboard 4 and $", "Keyboard 5 and %", "Keyboard 6 and ^", "Keyboard 7 and &", "Keyboard 8 and *", "Keyboard 9 and (", "Keyboard 0 and )",
    "Keyboard Return (ENTER)", "Keyboard ESCAPE", "Keyboard DELETE (Backspace)", "Keyboard Tab", "Keyboard Spacebar", "Keyboard - and _", "Keyboard = and +", "Keyboard [ and {",
    "Keyboard ] and }", "Keyboard \\ and |", "Keyboard Non-US # and ~", "Keyboard ; and :", "Keyboard ‘ and “", "Keyboard Grave Accent and Tilde", "Keyboard , and <", "Keyboard . and >",
    "Keyboard / and ?", "Keyboard Caps Lock", "Keyboard F1", "Keyboard F2", "Keyboard F3", "Keyboard F4", "Keyboard F5", "Keyboard F6",
    "Keyboard F7", "Keyboard F8", "Keyboard F9", "Keyboard F10", "Keyboard F11", "Keyboard F12", "Keyboard PrintScreen", "Keyboard Scroll Lock",
    "Keyboard Pause", "Keyboard Insert", "Keyboard Home", "Keyboard PageUp", "Keyboard Delete Forward", "Keyboard End", "Keyboard PageDown", "Keyboard RightArrow",
    "Keyboard LeftArrow", "Keyboard DownArrow", "Keyboard UpArrow", "Keypad Num Lock and Clear", "Keypad /", "Keypad *", "Keypad -", "Keypad +",
    "Keypad ENTER", "Keypad 1 and End", "Keypad 2 and Down Arrow", "Keypad 3 and PageDn", "Keypad 4 and Left Arrow", "Keypad 5", "Keypad 6 and Right Arrow", "Keypad 7 and Home",
    "Keypad 8 and Up Arrow", "Keypad 9 and PageUp", "Keypad 0 and Insert", "Keypad . and Delete", "Keyboard Non-US \\ and |", "Keyboard Application", "Keyboard Power", "Keypad =",
    "Keyboard F13", "Keyboard F14", "Keyboard F15", "Keyboard F16", "Keyboard F17", "Keyboard F18", "Keyboard F19", "Keyboard F20",
    "Keyboard F21", "Keyboard F22", "Keyboard F23", "Keyboard F24", "Keyboard Execute", "Keyboard Help", "Keyboard Menu", "Keyboard Select",
    "Keyboard Stop", "Keyboard Again", "Keyboard Undo", "Keyboard Cut", "Keyboard Copy", "Keyboard Paste", "Keyboard Find", "Keyboard Mute",
    "Keyboard Volume Up", "Keyboard Volume Down", "Keyboard Locking Caps Lock", "Keyboard Locking Num Lock", "Keyboard Locking Scroll Lock", "Keypad Comma", "Keypad Equal Sign", "Keyboard International1",
    "Keyboard International2", "Keyboard International3", "Keyboard International4", "Keyboard International5", "Keyboard International6", "Keyboard International7", "Keyboard International8", "Keyboard International9",
    "Keyboard LANG1", "Keyboard LANG2", "Keyboard LANG3", "Keyboard LANG4", "Keyboard LANG5", "Keyboard LANG6", "Keyboard LANG7", "Keyboard LANG8",
    "Keyboard LANG9", "Keyboard Alternate Erase", "Keyboard SysReq/Attention", "Keyboard Cancel", "Keyboard Clear", "Keyboard Prior", "Keyboard Return", "Keyboard Separator",
    "Keyboard Out", "Keyboard Oper", "Keyboard Clear/Again", "Keyboard CrSel/Props", "Keyboard ExSel", 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Keypad 00", "Keypad 000", "Thousands Separator", "Decimal Separator", "Currency Unit", "Currency Sub-unit", "Keypad (", "Keypad )",
    "Keypad {", "Keypad }", "Keypad Tab", "Keypad Backspace", "Keypad A", "Keypad B", "Keypad C", "Keypad D",
    "Keypad E", "Keypad F", "Keypad XOR", "Keypad ^", "Keypad %", "Keypad <", "Keypad >", "Keypad &",
    "Keypad &&", "Keypad |", "Keypad ||", "Keypad :", "Keypad #", "Keypad Space", "Keypad @", "Keypad !",
    "Keypad Memory Store", "Keypad Memory Recall", "Keypad Memory Clear", "Keypad Memory Add", "Keypad Memory Subtract", "Keypad Memory Multiply", "Keypad Memory Divide", "Keypad +/-",
    "Keypad Clear", "Keypad Clear Entry", "Keypad Binary", "Keypad Octal", "Keypad Decimal", "Keypad Hexadecimal", 0, 0,
    "Keyboard LeftControl", "Keyboard LeftShift", "Keyboard LeftAlt", "Keyboard Left GUI", "Keyboard RightControl", "Keyboard RightShift", "Keyboard RightAlt", "Keyboard Right GUI",
|0x08:
    0, "Num Lock", "Caps Lock", "Scroll Lock", "Compose", "Kana", "Power", "Shift",
    "Do Not Disturb", "Mute", "Tone Enable", "High Cut Filter", "Low Cut Filter", "Equalizer Enable", "Sound Field On", "Surround On",
    "Repeat", "Stereo", "Sampling Rate Detect", "Spinning", "CAV", "CLV", "Recording Format Detect", "Off-Hook",
    "Ring", "Message Waiting", "Data Mode", "Battery Operation", "Battery OK", "Battery Low", "Speaker", "Head Set",
    "Hold", "Microphone", "Coverage", "Night Mode", "Send Calls", "Call Pickup", "Conference", "Stand-by",
    "Camera On", "Camera Off", "On-Line", "Off-Line", "Busy", "Ready", "Paper-Out", "Paper-Jam",
    "Remote", "Forward", "Reverse", "Stop", "Rewind", "Fast Forward", "Play", "Pause",
    "Record", "Error", "Usage Selected Indicator", "Usage In Use Indicator", "Usage Multi Mode Indicator", "Indicator On", "Indicator Flash", "Indicator Slow Blink",
    "Indicator Fast Blink", "Indicator Off", "Flash On Time", "Slow Blink On Time", "Slow Blink Off Time", "Fast Blink On Time", "Fast Blink Off Time", "Usage Indicator Color",
    "Indicator Red", "Indicator Green", "Indicator Amber", "Generic Indicator", "System Suspend", "External Power Connected", "Indicator Blue", "Indicator Orange",
    "Good Status", "Warning Status", "RGB LED", "Red LED Channel", "Greed LED Channel", "Blue LED Channel", "LED Intensity", 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Player Indicator", "Player 1", "Player 2", "Player 3", "Player 4",  "Player 5",  "Player 6",  "Player 7",
    "Player 8", 0, 0, 0, 0, 0, 0, 0
|0x0b:
    0, "Phone", "Answering Machine", "Message Controls", "Handset", "Headset", "Telephony Key Pad", "Programmable Button",
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Hook Switch", "Flash", "Feature", "Hold", "Redial", "Transfer", "Drop", "Park",
    "Forward Calls", "Alternate Function", "Line", "Speaker Phone", "Conference", "Ring Enable", "Ring Select", "Phone Mute",
    "Caller ID", "Send", 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Speed Dial", "Store Number", "Recall Number", "Phone Directory", 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Voice Mail", "Screen Calls", "Do Not Disturb", "Message", "Answer On/Off", 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Inside Dial Tone", "Outside Dial Tone", "Inside Ring Tone", "Outside Ring Tone", "Priority Ring Tone", "Inside Ringback", "Priority Ringback", "Line Busy Tone",
    "Reorder Tone", "Call Waiting Tone", "Confirmation Tone", "Confirmation Tone", "Tones Off", "Outside Ringback", "Ringer", 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Phone Key 0", "Phone Key 1", "Phone Key 2", "Phone Key 3", "Phone Key 4", "Phone Key 5", "Phone Key 6", "Phone Key 7",
    "Phone Key 8", "Phone Key 9", "Phone Key Star", "Phone Key Pound", "Phone Key A", "Phone Key B", "Phone Key C", "Phone Key D",
    "Phone Call History Key", "Phone Caller ID Key", "Phone Settings Key", 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Host Control", "Host Available", "Host Call Active", "Activate Handset Audio", "Ring Type", "Re-dialable Phone Number", 0, 0,
    "Stop Ring Tone", "PSTN Ring Tone", "Host Ring Tone", "Alert Sound Error", "Alert Sound Confirm", "Alert Sound Notification", "Silent Ring", 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Email Message Waiting", "Voicemail Message Waiting", "Host Hold", 0, 0, 0, 0, 0,
    "Incoming Call History Count", "Outgoing Call History Count", "Incoming Call History", "Outgoing Call History", "Phone Locale", 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Phone Time Second", "Phone Time Minute", "Phone Time Hour", "Phone Date Day", "Phone Date Month", "Phone Date Year", "Handset Nickname", "Address Book ID",
    "Call Duration", "Dual Mode Phone", 0, 0, 0, 0, 0, 0
|0x0C:
    0, "Consumer Control", "Numeric Key Pad", "Programmable Buttons", "Microphone", "Headphone", "Graphic Equalizer", 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "+10", "+100", "AM/PM", 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Power", "Reset", "Sleep", "Sleep After", "Sleep Mode", "Illumination", "Function Buttons", 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Menu", "Menu Pick", "Menu Up", "Menu Down", "Menu Left", "Menu Right", "Menu Escape", "Menu Value Increase",
    "Menu Value Decrease", 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Data On Screen", "Closed Caption", "Closed Caption Select", "VCR/TV", "Broadcast Mode", "Snapshot", "Still", "Picture-in-Picture Toggle",
    "Picture-in-Picture Swap", "Red Menu Button", "Green Menu Button", "Blue Menu Button", "Yellow Menu Button", "Aspect", "3D Mode Select", "Display Brightness Increment",
    "Display Brightness Decrement", "Display Brightness", "Display Backlight Toggle", "Display Set Brightness to Minimum", "Display Set Brightness to Maximum", "Display Set Auto Brightness", 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, "Assign Selection", "Mode Step", "Recall Last", "Enter Channel", "Order Movie", "Channel", 0,
    "Media Select Computer", "Media Select TV", "Media Select WWW", "Media Select DVD", "Media Select Telephone", "Media Select Program Guide", "Media Select Video Phone", "Media Select Games",
    "Media Select Messages", "Media Select CD", "Media Select VCR", "Media Select Tuner", "Quit", "Help", "Media Select Tape", "Media Select Cable",
    "Media Select Satellite", "Media Select Security", "Media Select Home", "Media Select Call", "Channel Increment", "Channel Decrement", "Media Select SAP", 0,
    "VCR Plus", "Once", "Daily", "Weekly", "Monthly", 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Play", "Pause", "Record", "Fast Forward", "Rewind", "Scan Next Track", "Scan Previous Track", "Stop",
    "Eject", "Random Play", "Select Disc", "Enter Disc", "Repeat", "Tracking", "Track Normal", "Slow Tracking",
    "Frame Forward", "Frame Back", "Mark", "Clear Mark", "Repeat From Mark", "Return To Mark", "Search Mark Forward", "Search Mark Backwards",
    "Counter Reset", "Show Counter", "Tracking Increment", "Tracking Decrement", "Stop/Eject", "Play/Pause", "Play/Skip", "Voice Command",
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Volume", "Balance", "Mute", "Bass", "Treble", "Bass Boost", "Surround Mode", "Loudness",
    "MPX", "Volume Increment", "Volume Decrement", 0, 0, 0, 0, 0,
    "Speed Select", "Playback Speed", "Standard Play", "Long Play", "Extended Play", "Slow", 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Fan Enable", "Fan Speed", "Light Enable", "Light Illumination Level", "Climate Control Enable", "Room Temperature", "Security Enable", "Fire Alarm",
    "Police Alarm", "Proximity", "Motion", "Duress Alarm", "Holdup Alarm", "Medical Alarm", 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Balance Right", "Balance Left", "Bass Increment", "Bass Decrement", "Treble Increment", "Treble Decrement", 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Speaker System", "Channel Left", "Channel Right", "Channel Center", "Channel Front", "Channel Center Front", "Channel Side", "Channel Surround",
    "Channel Low Frequency Enhancement", "Channel Top", "Channel Unknown", 0, 0, 0, 0, 0,
    "Sub-channel", "Sub-channel Increment", "Sub-channel Decrement", "Alternate Audio Increment", "Alternate Audio Decrement", 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Application Launch Buttons", "AL Launch Button Configuration Tool", "AL Programmable Button Configuration", "AL Consumer Control Configuration", "AL Word Processor", "AL Text Editor", "AL Spreadsheet", "AL Graphics Editor",
    "AL Presentation App", "AL Database App", "AL Email Reader", "AL Newsreader", "AL Voicemail", "AL Contacts/Address Book", "AL Calendar/Schedule", "AL Task/Project Manager",
    "AL Log/Journal/Timecard", "AL Checkbook/Finance", "AL Calculator", "AL A/V Capture/Playback", "AL Local Machine Browser", "AL LAN/WAN Browser", "AL Internet Browser", "AL Remote Networking/ISP Connect",
    "AL Network Conference", "AL Network Chat", "AL Telephony/Dialer", "AL Logon", "AL Logoff", "AL Logon/Logoff", "AL Terminal Lock/Screensaver", "AL Control Panel",
    "AL Command Line Processor/Run", "AL Process/Task Manager", "AL Select Task/Application", "AL Next Task/Application", "AL Previous Task/Application", "AL Preemptive Halt Task/Application", "AL Integrated Help Center", "AL Documents",
    "AL Thesaurus", "AL Dictionary", "AL Desktop", "AL Spell Check", "AL Grammar Check", "AL Wireless Status", "AL Keyboard Layout", "AL Virus Protection",
    "AL Encryption", "AL Screen Saver", "AL Alarms", "AL Clock", "AL File Browser", "AL Power Status", "AL Image Browser", "AL Audio Browser",
    "AL Movie Browser", "AL Digital Rights Manager", "AL Digital Wallet", 0, "AL Instant Messaging", "AL OEM Features/Tips/Tutorial Browser", "AL OEM Help", "AL Online Community",
    "AL Entertainment Content Browser", "AL Online Shopping Browser", "AL SmartCard Information/Help", "AL Market Monitor/Finance Browser", "AL Customized Corporate News Browser", "AL Online Activity Browser", "AL Research/Search Browser", "AL Audio Player",
    "AL Message Status", "AL Contact Sync", 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Generic GUI Application Controls", "AC New", "AC Open", "AC Close", "AC Exit", "AC Maximize", "AC Minimize", "AC Save",
    "AC Print", "AC Properties", "AC Undo", "AC Copy", "AC Cut", "AC Paste", "AC Select All", "AC Find",
    "AC Find and Replace", "AC Search", "AC Go To", "AC Home", "AC Back", "AC Forward", "AC Stop", "AC Refresh",
    "AC Previous Link", "AC Next Link", "AC Bookmarks", "AC History", "AC Subscriptions", "AC Zoom In", "AC Zoom Out", "AC Zoom",
    "AC Full Screen View", "AC Normal View", "AC View Toggle", "AC Scroll Up", "AC Scroll Down", "AC Scroll", "AC Pan Left", "AC Pan Right",
    "AC Pan", "AC New Window", "AC Tile Horizontally", "AC Tile Vertically", "AC Format", "AC Edit", "AC Bold", "AC Italics",
    "AC Underline", "AC Strikethrough", "AC Subscript", "AC Superscript", "AC All Caps", "AC Rotate", "AC Resize", "AC Flip horizontal",
    "AC Flip Vertical", "AC Mirror Horizontal", "AC Mirror Vertical", "AC Font Select", "AC Font Color", "AC Font Size", "AC Justify Left", "AC Justify Center H",
    "AC Justify Right", "AC Justify Block H", "AC Justify Top", "AC Justify Center V", "AC Justify Bottom", "AC Justify Block V", "AC Indent Decrease", "AC Indent Increase",
    "AC Numbered List", "AC Restart Numbering", "AC Bulleted List", "AC Promote", "AC Demote", "AC Yes", "AC No", "AC Cancel",
    "AC Catalog", "AC Buy/Checkout", "AC Add to Cart", "AC Expand", "AC Expand All", "AC Collapse", "AC Collapse All", "AC Print Preview",
    "AC Paste Special", "AC Insert Mode", "AC Delete", "AC Lock", "AC Unlock", "AC Protect", "AC Unprotect", "AC Attach Comment",
    "AC Delete Comment", "AC View Comment", "AC Select Word", "AC Select Sentence", "AC Select Paragraph", "AC Select Column", "AC Select Row", "AC Select Table",
    "AC Select Object", "AC Redo/Repeat", "AC Sort", "AC Sort Ascending", "AC Sort Descending", "AC Filter", "AC Set Clock", "AC View Clock",
    "AC Select Time Zone", "AC Edit Time Zones", "AC Set Alarm", "AC Clear Alarm", "AC Snooze Alarm", "AC Reset Alarm", "AC Synchronize", "AC Send/Receive",
    "AC Send To", "AC Reply", "AC Reply All", "AC Forward Msg", "AC Send", "AC Attach File", "AC Upload", "AC Download (Save Target As)",
    "AC Set Borders", "AC Insert Row", "AC Insert Column", "AC Insert File", "AC Insert Picture", "AC Insert Object", "AC Insert Symbol", "AC Save and Close",
    "AC Rename", "AC Merge", "AC Split", "AC Distribute Horizontally", "AC Distribute Vertically", 0, 0, 0,
    "AC Soft Key Left", "AC Soft Key Right", 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, "AC Next Keyboard Layout Select", 0, 0,
    "AC Idle Keep Alive", 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Extended Keyboard Attributes Collection", "Keyboard Form Factor", "Keyboard Key Type", "Keyboard Physical Layout", "Vendor-Specific Keyboard Physical Layout", "Keyboard IETF Language Tag Index", "Implemented Keyboard Input Assist Controls", "Keyboard Input Assist Previous",
    "Keyboard Input Assist Next", "Keyboard Input Assist Previous Group", "Keyboard Input Assist Next Group", "Keyboard Input Assist Accept", "Keyboard Input Assist Cancel", 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Contact Edited", "Contact Added", "Contact Record Active", "Contact Index", "Contact Nickname", "Contact First Name", "Contact Last Name", "Contact Full Name",
    "Contact Phone Number Personal", "Contact Phone Number Business", "Contact Phone Number Mobile", "Contact Phone Number Pager", "Contact Phone Number Fax", "Contact Phone Number Other", "Contact Email Personal", "Contact Email Business",
    "Contact Email Other", "Contact Email Main", "Contact Speed Dial Number", "Contact Status Flag", "Contact Misc.", 0, 0, 0
|0x0D:
    0, "Digitizer", "Pen", "Light Pen", "Touch Screen", "Touch Pad", "White Board", "Coordinate Measuring",
    "3D Digitizer", "Stereo Plotter", "Articulated Arm", "Armature", "Multiple Point Digitizer", "Free Space Wand", "Device configuration", "Capacitive Heat Map Digitizer",
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Stylus", "Puck", "Finger", "Device settings", "Character Gesture, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    "Tip Pressure", "Barrel Pressure", "In Range", "Touch", "Untouch", "Tap", "Quality", "Data Valid",
    "Transducer Index", "Tablet Function Keys", "Program Change Keys", "Battery Strength", "Invert", "X Tilt", "Y Tilt", "Azimuth",
    "Altitude", "Twist", "Tip Switch", "Secondary Tip Switch", "Barrel Switch", "Eraser", "Tablet Pick", "Touch Valid",
    "Width", "Height", 0, 0, 0, 0, 0, 0,
    0, "Contact identifier", "Device mode", "Device identifier", "Contact count", "Contact count maximum", "Scan Time", "Surface Switch",
    "Button Switch", "Pad Type", "Secondary Barrel Switch", "Transducer Serial Number", "Preferred Color", 0, 0, 0

I don't know much besides the fact that they were able to get some data back from Main.ahk, although I don't know how useful it was or how close the issue was to being solved, but I thought it couldn't hurt to take a shot and post it here to see if anyone else has some input to provide. :)

Return to “Ask for Help (v1)”