Author Topic: HidP_GetUsages in VB  (Read 24244 times)

Pointy

  • Member
  • ***
  • Posts: 11
HidP_GetUsages in VB
« on: February 11, 2014, 07:01:17 am »
Hi,

I am trying to read a USB Joystick via rawInput in VB.Net.

Using this code as a guide.

My code is nearly there but I don't get the correct data, when I call HidP_GetUsages. Comparing the c++ code linked and mine side by side in debug mode the returned data matches up until the above call.

This is how I am declaring HidP_GetUsages...

Code: [Select]
<DllImport("hid.dll")>
Shared Function HidP_GetUsages(ByVal ReportType As HIDP_REPORT_TYPE, ByVal UsagePage As Short, _
ByVal LinkCollection As UShort, ByVal UsageList As IntPtr, ByRef UsageLength As UInt32, _
ByVal PreparsedData As IntPtr, ByRef Report As Byte, ByVal ReportLength As UInt32) As Integer
End Function

and this is how I am defining the structure...

Code: [Select]
    <StructLayout(LayoutKind.Sequential)> _
    Public Structure HIDP_BUTTON_CAPS
        Public UsagePage As Short
        Public ReportID As Byte
        Public IsAlias As Byte
        Public BitField As UShort
        Public LinkCollection As UShort
        Public LinkUsage As Short
        Public LinkUsagePage As Short
        Public IsRange As Byte
        Public IsStringRange As Byte
        Public IsDesignatorRange As Byte
        Public IsAbsolute As Byte
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=10)>
        Public Reserved() As UInt32
        Public RangeNotRange As HIDP_BUTTON_CAPS_2
    End Structure
    <StructLayout(LayoutKind.Explicit)> _
    Public Structure HIDP_BUTTON_CAPS_2
        <FieldOffset(0)> Public Range As HIDP_BUTTON_CAPS_RANGE
        <FieldOffset(0)> Public NotRange As HIDP_BUTTON_CAPS_NOTRANGE
    End Structure
    Public Structure HIDP_BUTTON_CAPS_RANGE
        Public UsageMin As Short
        Public UsageMax As Short
        Public StringMin As UShort
        Public StringMax As UShort
        Public DesignatorMin As UShort
        Public DesignatorMax As UShort
        Public DataIndexMin As UShort
        Public DataIndexMax As UShort
    End Structure
    Public Structure HIDP_BUTTON_CAPS_NOTRANGE
        Public Usage As Short
        Public Reserved1 As Short
        Public StringIndex As UShort
        Public Reserved2 As UShort
        Public DesignatorIndex As UShort
        Public Reserved3 As UShort
        Public DataIndex As UShort
        Public Reserved4 As UShort
    End Structure

It's driving me mad! Please tell me what I am doing wrong!

Regards,

Les

PS. I can paste the whole code if it helps.



Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: HidP_GetUsages in VB
« Reply #1 on: February 11, 2014, 09:23:46 am »
Where is it going wrong?

Pointy

  • Member
  • ***
  • Posts: 11
Re: HidP_GetUsages in VB
« Reply #2 on: February 11, 2014, 12:12:38 pm »
Where is it going wrong?

The HidP_GetUsages isn't returning what it should.

I have a joystick that has 128 buttons , my test firmware just sets each button on and then off one at a time. Using the c++ code and my VB directx code it loops through all 128 buttons no problem.

With my rawInput code it goes up to 120, and the last few button seem to get a random value.

As a test I dumped both the c++ pPreparsedData to file along with my VB version and both files are identical. The  HidP_GetCaps returned data  is identical to the c++ code. The HidP_GetButtonCaps returned data is the same in both programs other than the 10 reserved ULONGs (not sure if that is significant though).

HidP_GetUsages is supposed to return the buttons which are on and match the UsagePage, but it not working in my VB app.

As another test in my joystick firmware I have only button 128 on, in my app I am getting random results with the last few buttons, as if I am missing a byte somewhere, and it is just showing random memory for that last byte.

I just can't figure out where I have gone wrong.

Regards,

Les


Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: HidP_GetUsages in VB
« Reply #3 on: February 11, 2014, 06:08:48 pm »
The returned UsageList buffer has incorrect data in it?

Is the data in the buffer correct except for the "last few" buttons?

How do you declare the buffer and assign the UsageList IntPtr to it?

Pointy

  • Member
  • ***
  • Posts: 11
Re: HidP_GetUsages in VB
« Reply #4 on: February 12, 2014, 03:21:13 am »
The returned UsageList buffer has incorrect data in it?

Is the data in the buffer correct except for the "last few" buttons?

The UsageList buffer should return a list of buttons that are on. So with button 128 on, HidP_GetUsages should return a usageLength of 1, and the buffer should contain an array of 128 uShorts. The First item in that array should be 128 (the button that is pressed) and the rest should be 0.

I just ran my program 6 times and got the following results (obviously the USB device is sending the same data all the time for testing)...

usageLength: 1, pUsageBuffer: 126 followed by all 0
usageLength: 1, pUsageBuffer: 128 followed by all 0
usageLength: 2, pUsageBuffer: 125,126 followed by all 0
usageLength: 2, pUsageBuffer: 127,128 followed by all 0
usageLength: 0, pUsageBuffer: all 0
usageLength: 1, pUsageBuffer: 127 followed by all 0

I can't figure out why it appears to work up to button 120.

How do you declare the buffer and assign the UsageList IntPtr to it?

Here's pretty much the whole Sub (pRawInput is a RAWINPUT structure passed to the sub)...

Code: [Select]
       ' get the preparseddata buffer size
       Dim pcbSize As System.UInt32 = Convert.ToUInt32(0)
       GetRawInputDeviceInfo(pRawInput.header.hDevice, RIDI_PREPARSEDDATA, IntPtr.Zero, pcbSize)
       ' get the actual preparseddata
       pPreparsedData = Marshal.AllocHGlobal(Convert.ToInt32(pcbSize))
       GetRawInputDeviceInfo(pRawInput.header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, pcbSize)
       Dim bData(CInt(pcbSize)) As Byte
       Marshal.Copy(pPreparsedData, bData, 0, CInt(pcbSize))
       ' get hid capabilities
       Dim Capabilities As New HIDP_CAPS
       HidP_GetCaps(pPreparsedData, Capabilities)
       ' create buffer for button data
       pButtonBuffer = Marshal.AllocHGlobal(Convert.ToInt32(Marshal.SizeOf(GetType(HIDP_BUTTON_CAPS)) * Capabilities.NumberInputButtonCaps))
       ' get the number of button capabilities
       Dim capsLength As Integer = Capabilities.NumberInputButtonCaps
       ' get button capabilities into buffer
       HidP_GetButtonCaps(HIDP_REPORT_TYPE.HidP_Input, pButtonBuffer, capsLength, pPreparsedData)
       ' marshal buffer to HIDP_BUTTON_CAPS structure
       Dim pButtonCaps As HIDP_BUTTON_CAPS = CType(Marshal.PtrToStructure(pButtonBuffer, GetType(HIDP_BUTTON_CAPS)), HIDP_BUTTON_CAPS)
       ' calc number of buutons from usagemax & usagemin
       Dim g_NumberOfButtons As Integer = pButtonCaps.RangeNotRange.Range.UsageMax - pButtonCaps.RangeNotRange.Range.UsageMin + 1
       ' set class variable buttoncount
       ButtonCount = g_NumberOfButtons
       ' create buffer for return button usage count
       Dim usageLength As UInt32 = CUInt(g_NumberOfButtons)
       ' create buffer for button values
       pUsageBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(UInt16)) * 128)
       ' get button values into buffer
       If HidP_GetUsages(HIDP_REPORT_TYPE.HidP_Input, pButtonCaps.UsagePage, 0, pUsageBuffer, usageLength, pPreparsedData, pRawInput.hid.bRawData, pRawInput.hid.dwSizHid) <> HIDP_STATUS_SUCCESS Then
            Throw New ApplicationException("An error occurred while retrieving the list of button usages!")
       End If
       ' copy button usage back to array
       Dim button(127) As Short
       Marshal.Copy(pUsageBuffer, button, 0, 128)

I really appreciate you taking the time to look at this, it is driving me insane.

Regards,

Les

Pointy

  • Member
  • ***
  • Posts: 11
Re: HidP_GetUsages in VB
« Reply #5 on: February 12, 2014, 03:55:35 am »
Just as a follow up I just did another test...

This time in the USB firmware I switch the 1st 64 buttons on and the last 64 button off, and then wait 100ms and then do the opposite so that the first 64 buttons are off and the last 64 are on.

The first 120 buttons always show correctly and the last 8 will be random, but the last 8 don't flash and change, they stay static. They are just different each time I start the program.

Looking at the info passed in and out of HidP_GetUsages I think we can rule out the following...

ReportType: I can't see this being the problem and here is the declaration...

Code: [Select]
Public Enum HIDP_REPORT_TYPE
    HidP_Input
    HidP_Output
    HidP_Feature
End Enum

UsagePage: This must be correct otherwise the first 120 buttons wouldn't work.
LinkCollection: This is not used.
UsageList: I am sure the buffer is correct ,otherwise again, why do the first 120 buttons work?
UsageLength: This is definitely being set to 128 before the call.
PreparsedData: I am sure the data is correct,  otherwise the capabilities would be wrong, amongst other things.

By the process of elimination I think we can rule out the the above.

This leaves us Report & ReportLength, which leads me to a part I don't quite understand...

The RAWHID structure from MSDN...
Quote
Members

dwSizeHid
Type: DWORD
The size, in bytes, of each HID input in bRawData.
dwCount
Type: DWORD
The number of HID inputs in bRawData.
bRawData
Type: BYTE[1]
The raw input data, as an array of bytes.
Remarks

Each WM_INPUT can indicate several inputs, but all of the inputs come from the same HID. The size of the bRawData array is dwSizeHid *   dwCount.

I don't get the purpose of bRawData , even in the c++ code the bRawData is always 1 byte. My dwSizeHid is 65 bytes, and dwCount is 1 so shouldn't bRawData be an array of 65 bytes?


Regards,

Les

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research

Pointy

  • Member
  • ***
  • Posts: 11
Re: HidP_GetUsages in VB
« Reply #7 on: February 12, 2014, 04:10:50 pm »
Some comments about bRawData here:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms645549%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/ms645596%28v=vs.85%29.aspx

I looked at the pages several times but never noticed the comments.

After using some of the code posted by Đonny, I was able to get it working, so many thanks Jan for pointing the comments out.

I was correct in that it was the bRawData that was causing the problem, I just needed the right structure declaration.

Now I just have one teensy problem, I can't figure out how to get multiple values of the same type using HidP_GetUsageValue. I have 4 hats switches but it always seems to return the data for the 1st. Same thing for my sliders.

Best I go do some more reading.

Regards,

Les

Pointy

  • Member
  • ***
  • Posts: 11
Re: HidP_GetUsages in VB
« Reply #8 on: February 12, 2014, 05:59:38 pm »
Best I go do some more reading.

Looks like I need to use HidP_GetUsageValueArray, but again it's not working for me.

This is turning out to be much harder than i thought, but at least I am slowly getting there.

Regards,

Les

Pointy

  • Member
  • ***
  • Posts: 11
Re: HidP_GetUsages in VB
« Reply #9 on: February 13, 2014, 03:06:16 am »
Ok it would seem that HidP_GetUsageValueArray is returning the HIDP_STATUS_NOT_VALUE_ARRAY error.

This is my USB descriptor...

Code: [Select]
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x04,                     // Usage (Joystick)
        0xA1, 0x01,                     // Collection (Application)
        0x15, 0x00,                     // Logical Minimum (0)
        0x25, 0x01,                     // Logical Maximum (1)
        0x75, 0x01,                     // Report Size (1)
        0x95, 0x80,                     // Report Count (128)
        0x05, 0x09,                     // Usage Page (Button)
        0x19, 0x01,                     // Usage Minimum (Button #1)
        0x29, 0x80,                     // Usage Maximum (Button #128)
        0x81, 0x02,                     // Input (variable,absolute)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x01,                     // Usage (Pointer)
        0xA1, 0x00,                     // Collection ()
0x15, 0x00,                     // Logical Minimum (0)
0x27, 0xFF, 0xFF, 0, 0,         // Logical Maximum (65535)
        0x75, 0x10,                     // Report Size (16)
        0x95, 23,                       // Report Count (23)
        0x09, 0x30,                     // Usage (X)
        0x09, 0x31,                     // Usage (Y)
        0x09, 0x32,                     // Usage (Z)
        0x09, 0x33,                     // Usage (Rx)
        0x09, 0x34,                     // Usage (Ry)
        0x09, 0x35,                     // Usage (Rz)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x81, 0x02,                     // Input (variable,absolute)
        0xC0,                           // End Collection
        0x15, 0x00,                     // Logical Minimum (0)
        0x25, 0x07,                     // Logical Maximum (7)
        0x35, 0x00,                     // Physical Minimum (0)
        0x46, 0x3B, 0x01,               // Physical Maximum (315)
        0x75, 0x04,                     // Report Size (4)
        0x95, 0x04,                     // Report Count (4)
        0x65, 0x14,                     // Unit (20)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x39,                     // Usage (Hat switch)
        0x09, 0x39,                     // Usage (Hat switch)
        0x09, 0x39,                     // Usage (Hat switch)
        0x09, 0x39,                     // Usage (Hat switch)                       
        0x81, 0x42,                     // Input (variable,absolute,null_state)
        0xC0                            // End Collection

Surely if I call HidP_GetUsageValueArray with a UsagePage of 1 and a Usage of 0x39 it should return and array of 4?

I have also noticed that in both my program and the vc++ sample that ReportCount is always 1 for every HIDP_VALUE_CAPS returned by HidP_GetValueCaps.

After reading this page, I thought I would also have a look at doing it a slightly different way  by using HidP_GetData but HidP_MaxDataListLength always returns 0.

Am I missing something again?

Regards,

Les

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: HidP_GetUsages in VB
« Reply #10 on: February 13, 2014, 09:12:13 am »
Show your code that declares and calls HidP_GetUsageValueArray and/or HidP_GetData.

Pointy

  • Member
  • ***
  • Posts: 11
Re: HidP_GetUsages in VB
« Reply #11 on: February 14, 2014, 05:47:29 am »
I gave up with this after reading this, which said I should be using ReadFile anyway.

I just use some of the above code to get the details about the Hid joystick, such as number of buttons and axes and then use ReadFile to get the data from it. It's all working beautifully up to the point where I grab the report. I just need to convert that report into actual values.

Regards,

Les

Pointy

  • Member
  • ***
  • Posts: 11
Re: HidP_GetUsages in VB
« Reply #12 on: February 14, 2014, 07:33:08 am »
Show your code that declares and calls HidP_GetUsageValueArray and/or HidP_GetData.

Just out of interest I would like to know where I was going wrong.

Code: [Select]
   <DllImport("hid.dll")> _
    Shared Function HidP_GetData(ByVal ReportType As HIDP_REPORT_TYPE, ByRef DataList As IntPtr, ByRef DataLength As UInteger, ByRef PreparsedData As IntPtr, ByRef Report As Byte, ByVal ReportLength As UInteger) As Integer
    End Function
    <DllImport("hid.dll")> _
    Shared Function HidP_MaxDataListLength(ByVal ReportType As HIDP_REPORT_TYPE, ByRef PreparsedData As IntPtr) As Integer
    End Function

Code: [Select]
   Public Structure HIDP_DATA
        Public DataIndex As UShort
        Public Reserved As UShort
        Public Value As UnionValue
    End Structure

    <StructLayout(LayoutKind.Explicit)> _
    Public Structure UnionValue
        <FieldOffset(0)> Public RawValue As UInteger
        <FieldOffset(0)> Public State As Byte
    End Structure

    Public Enum HIDP_REPORT_TYPE
        HidP_Input
        HidP_Output
        HidP_Feature
    End Enum

Code: [Select]
       Dim pPreparsedData, pDataBuffer As IntPtr
        If Not HidD_GetPreparsedData(CurrentJoystick.Handle, pPreparsedData) Then
            Debug.Print("HidD_GetPreparsedData failed")
        End If
        Dim Capabilities As New HIDP_CAPS
        If Not HidP_GetCaps(pPreparsedData, Capabilities) Then
            Debug.Print("HidP_GetCaps failed")
            CloseHandle(CurrentJoystick.Handle)
        End If
        Dim count As Integer = HidP_MaxDataListLength(HIDP_REPORT_TYPE.HidP_Input, pPreparsedData)
        pDataBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(HIDP_DATA)) * count)
        If Not HidP_GetData(HIDP_REPORT_TYPE.HidP_Input, pDataBuffer, pPreparsedData, count, pRawInput.hid.bRawData(0), pRawInput.hid.dwSizeHid) Then
            Debug.Print("HidP_GetData failed")
        End If

HidP_MaxDataListLength returns 0 and HidP_GetData returns HIDP_STATUS_INVALID_PREPARSED_DATA

Surely if the PreparsedData was invalid then the HidP_GetCaps would fail?

Regards,

Les
« Last Edit: February 14, 2014, 07:38:46 am by Pointy »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: HidP_GetUsages in VB
« Reply #13 on: February 14, 2014, 09:32:28 am »
I gave up with this after reading this, which said I should be using ReadFile anyway.

That's just telling you to use ReadFile (or you can use FileStreams), which retrieves reports obtained using interrupt transfers, instead of HidD_GetInputReport, which requests an Input report using a control transfer.

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: HidP_GetUsages in VB
« Reply #14 on: February 14, 2014, 09:35:29 am »
Have you tried using breakpoints and examining what you are passing to the API calls and what they are returning? Sometimes that can offer a clue. For example, is the report data returned by ReadFile what you expect? If so, work forward from there to find a parameter that is suspicious.
« Last Edit: February 14, 2014, 09:38:00 am by Jan Axelson »