Author Topic: GenericHid HiDP_GetValueCaps is not working propertly on GenericHid 5.0 on C#  (Read 13482 times)

MacDie_88

  • Member
  • ***
  • Posts: 7
I'm trying to use HiDP_GetValueCaps in order to find the Link Collection of an specific report by entering Usage and Usage page. The Way that I'm using the library on HidDeclartions.cs is the following:
Code: [Select]

[ DllImport( "hid.dll", SetLastError=true ) ]       
internal static extern Int32 HidP_GetValueCaps(Int32 ReportType, ref HidP_Value_Caps ValueCaps, ref Int32 ValueCapsLength, IntPtr PreparsedData);


I call this library on an the internal class GetDeviceCapabilities locate on Hid.cs

Code: [Select]

internal HIDP_CAPS GetDeviceCapabilities( SafeFileHandle hidHandle, ref HidP_ValueCaps[] FeatureCaps )
        {             
            IntPtr preparsedData = new System.IntPtr();
            Int32 result = 0;
            Boolean success = false; 

try
{
//  ***
//  API function: HidD_GetPreparsedData

//  Purpose: retrieves a pointer to a buffer containing information about the device's capabilities.
//  HidP_GetCaps and other API functions require a pointer to the buffer.

//  Requires:
//  A handle returned by CreateFile.
//  A pointer to a buffer.

//  Returns:
//  True on success, False on failure.
//  ***

success = HidD_GetPreparsedData(hidHandle, ref preparsedData);

//  ***
//  API function: HidP_GetCaps

//  Purpose: find out a device's capabilities.
//  For standard devices such as joysticks, you can find out the specific
//  capabilities of the device.
//  For a custom device where the software knows what the device is capable of,
//  this call may be unneeded.

//  Accepts:
//  A pointer returned by HidD_GetPreparsedData
//  A pointer to a HIDP_CAPS structure.

//  Returns: True on success, False on failure.
//  ***

result = HidP_GetCaps(preparsedData, ref Capabilities);

//  ***
//  API function: HidP_GetValueCaps

//  Purpose: retrieves a buffer containing an array of HidP_ValueCaps structures.
//  Each structure defines the capabilities of one value.
//  This application doesn't use this data.

//  Accepts:
//  A report type enumerator from hidpi.h,
//  A pointer to a buffer for the returned array,
//  The NumberInputValueCaps member of the device's HidP_Caps structure,
//  A pointer to the PreparsedData structure returned by HidD_GetPreparsedData.

//  Returns: True on success, False on failure.
//  ***                   

Int32 vcSize = Capabilities.NumberFeatureValueCaps;
Byte[] valueCaps = new Byte[vcSize];

result = HidP_GetValueCaps(HidP_Feature, ref FeatureCaps[0], ref vcSize, preparsedData);                   
                 

// (To use this data, copy the ValueCaps byte array into an array of structures.)                   

}
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, ex);
throw;
}
finally
{
//  ***
//  API function: HidD_FreePreparsedData
                   
//  Purpose: frees the buffer reserved by HidD_GetPreparsedData.
                   
//  Accepts: A pointer to the PreparsedData structure returned by HidD_GetPreparsedData.
                   
//  Returns: True on success, False on failure.
//  ***

if (preparsedData != IntPtr.Zero)
{
success = HidD_FreePreparsedData(preparsedData);
}
}



On FrmMain.cs I create and array of structures and I call GetDeviceCapabilities to learn the capabilities of the device :
Code: [Select]

Hid.HidP_Value_Caps[] FeatureCaps = new Hid.HidP_Value_Caps[255];

MyHid.Capabilities=MyHid.GetDeviceCapabilities( hidHanlde, ref FeatureCaps);



I'm just getting the right data just on the first position of the array of structures, after that all the data does not make any sense or is not on the correct field.
Please can anyone see what is wrong with my code, or there is another way to use HiDP_GetValueCaps to get all the value capabilities of the device??

Thanks!!!

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
« Last Edit: July 13, 2014, 09:58:28 pm by Jan Axelson »

MacDie_88

  • Member
  • ***
  • Posts: 7
I have seen the previous post but even if I declared the structure with the offsets it does not give me the correct data on the correct data fields. I do not know if I am using HidP_GetValueCaps wrongly because it seems that every method I have tried is giving me the same results. I even have tried to follow a sample code developed by you on VB6 where it uses HidP_GetValueCaps to get compare every feature item on the HID so the the program can match the UsageID and UsagePage entered by the user with their corresponding ReportID and LinkCollection. Is there another way to do this? or can you point me to the right direction of how to use HidP_GetValueCaps ?

Thank you so much

ksrsrinivasan

  • Member
  • ***
  • Posts: 5
Hello,

I believe your issue is with you array initialization. You initialize it to 255 elements without checking to see if there are actually 255 valuecaps. The result is you get junk on the array elements beyond the actual valuecaps count. Assuming that you got most of what you want, below is the structure definitions and imports to use.
Code: [Select]
        public const int HIDP_STATUS_SUCCESS = (0x0 << 28) | (0x11 << 16) | 0;

        public enum HIDP_REPORT_TYPE
        {
            HidP_Input = 0,
            HidP_Output = 1,
            HidP_Feature = 2,
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct HidCaps
        {
            public UInt16 Usage;
            public UInt16 UsagePage;
            public UInt16 InputReportByteLength;
            public UInt16 OutputReportByteLength;
            public UInt16 FeatureReportByteLength;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
            public UInt16[] Reserved;
            public UInt16 NumberLinkCollectionNodes;
            public UInt16 NumberInputButtonCaps;
            public UInt16 NumberInputValueCaps;
            public UInt16 NumberInputDataIndices;
            public UInt16 NumberOutputButtonCaps;
            public UInt16 NumberOutputValueCaps;
            public UInt16 NumberOutputDataIndices;
            public UInt16 NumberFeatureButtonCaps;
            public UInt16 NumberFeatureValueCaps;
            public UInt16 NumberFeatureDataIndices;
        }

        [StructLayout(LayoutKind.Sequential, Pack=4)]
        public struct HidP_Range
        {
            public UInt16 UsageMin;
            public UInt16 UsageMax;
            public UInt16 StringMin;
            public UInt16 StringMax;
            public UInt16 DesignatorMin;
            public UInt16 DesignatorMax;
            public UInt16 DataIndexMin;
            public UInt16 DataIndexMax;
        }

        [StructLayout(LayoutKind.Sequential, Pack=4)]
        public struct HidP_NotRange
        {
            public UInt16 Usage;
            public UInt16 Reserved1;
            public UInt16 StringIndex;
            public UInt16 Reserved2;
            public UInt16 DesignatorIndex;
            public UInt16 Reserved3;
            public UInt16 DataIndex;
            public UInt16 Reserved4;
        }

        [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Ansi, Pack=4)]
        public struct HidP_Button_Caps
        {
            [FieldOffset(0)]
            public UInt16 UsagePage;
            [FieldOffset(2)]
            public byte ReportID;
            [FieldOffset(3), MarshalAs(UnmanagedType.U1)]
            public bool IsAlias;
            [FieldOffset(4)]
            public UInt16 BitField;
            [FieldOffset(6)]
            public UInt16 LinkCollection;
            [FieldOffset(8)]
            public UInt16 LinkUsage;
            [FieldOffset(10)]
            public UInt16 LinkUsagePage;
            [FieldOffset(12), MarshalAs(UnmanagedType.U1)]
            public bool IsRange;
            [FieldOffset(13), MarshalAs(UnmanagedType.U1)]
            public bool IsStringRange;
            [FieldOffset(14), MarshalAs(UnmanagedType.U1)]
            public bool IsDesignatorRange;
            [FieldOffset(15), MarshalAs(UnmanagedType.U1)]
            public bool IsAbsolute;
            [FieldOffset(16), MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public int[] Reserved;
            // The Structs in the Union
            [FieldOffset(56)]
            public HidP_Range Range;
            [FieldOffset(56)]
            public HidP_NotRange NotRange;
        }

        [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Ansi, Pack=4)]
        public struct HidP_Value_Caps
        {
            [FieldOffset(0)]
            public UInt16 UsagePage;
            [FieldOffset(2)]
            public byte ReportID;
            [MarshalAs(UnmanagedType.I1)]
            [FieldOffset(3)]
            public bool IsAlias;
            [FieldOffset(4)]
            public UInt16 BitField;
            [FieldOffset(6)]
            public UInt16 LinkCollection;
            [FieldOffset(8)]
            public UInt16 LinkUsage;
            [FieldOffset(10)]
            public UInt16 LinkUsagePage;
            [MarshalAs(UnmanagedType.I1)]
            [FieldOffset(12)]
            public bool IsRange;
            [MarshalAs(UnmanagedType.I1)]
            [FieldOffset(13)]
            public bool IsStringRange;
            [MarshalAs(UnmanagedType.I1)]
            [FieldOffset(14)]
            public bool IsDesignatorRange;
            [MarshalAs(UnmanagedType.I1)]
            [FieldOffset(15)]
            public bool IsAbsolute;
            [MarshalAs(UnmanagedType.I1)]
            [FieldOffset(16)]
            public bool HasNull;
            [FieldOffset(17)]
            public System.Char Reserved;
            [FieldOffset(18)]
            public UInt16 BitSize;
            [FieldOffset(20)]
            public UInt16 ReportCount;
            [FieldOffset(22)]
            public UInt16 Reserved2a;
            [FieldOffset(24)]
            public UInt16 Reserved2b;
            [FieldOffset(26)]
            public UInt16 Reserved2c;
            [FieldOffset(28)]
            public UInt16 Reserved2d;
            [FieldOffset(30)]
            public UInt16 Reserved2e;
            [FieldOffset(32)]
            public UInt64 UnitsExp;
            [FieldOffset(36)]
            public UInt64 Units;
            [FieldOffset(40)]
            public Int64 LogicalMin;
            [FieldOffset(44)]
            public Int64 LogicalMax;
            [FieldOffset(48)]
            public Int64 PhysicalMin;
            [FieldOffset(52)]
            public Int64 PhysicalMax;
            // The Structs in the Union
            [FieldOffset(56)]
            public HidP_Range Range;
            [FieldOffset(56)]
            public HidP_NotRange NotRange;
        }

        [DllImport("hid.dll", SetLastError = true)]
        public static extern bool HidD_GetPreparsedData(SafeFileHandle hFile, out IntPtr lpData);

        [DllImport("hid.dll", SetLastError = true)]
        public static extern int HidP_GetCaps(IntPtr lpData, out HidCaps oCaps);

        [DllImport("hid.dll", SetLastError = true)]
        public static extern bool HidD_FreePreparsedData(ref IntPtr pData);

        [DllImport("hid.dll", SetLastError = true)]
        public static extern int HidP_GetButtonCaps(HIDP_REPORT_TYPE reportType, [In, Out] HidP_Button_Caps[] buttonCaps, ref UInt16 buttonCapsLength, IntPtr preparsedData);

        [DllImport("hid.dll", SetLastError = true)]
        public static extern int HidP_GetValueCaps(HIDP_REPORT_TYPE reportType, [In, Out] HidP_Value_Caps[] valueCaps, ref UInt16 valueCapsLength, IntPtr preparsedData);



With these above definitions and assuming that you have a file handle opened using the createfile method, here is how to get the value caps.

Code: [Select]
IntPtr preparedData;
HidCaps hidCaps = null;
HidP_Button_Caps[] outputButtonCaps = null;
HidP_Value_Caps[] outputValueCaps = null;
HidP_Button_Caps[] inputButtonCaps = null;
HidP_Value_Caps[] inputValueCaps = null;
HidP_Button_Caps[] featureButtonCaps = null;
HidP_Value_Caps[] numberFeatureValueCaps = null;

try
{
if (HidD_GetPreparsedData(safeHandle, out preparedData))
{
if (HidP_GetCaps(preparedData, out hidCaps) != HIDP_STATUS_SUCCESS)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}

var numberOutputButtonCaps = hidCaps.NumberOutputButtonCaps;
if (numberOutputButtonCaps != 0)
{
outputButtonCaps = new HidP_Button_Caps[numberOutputButtonCaps];
if (HidP_GetButtonCaps(HIDP_REPORT_TYPE.HidP_Output, outputButtonCaps, ref numberOutputButtonCaps, preparedData) != HIDP_STATUS_SUCCESS)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

var numberOutputValueCaps = hidCaps.NumberOutputValueCaps;
if (numberOutputValueCaps != 0)
{
outputValueCaps = new HidP_Value_Caps[numberOutputValueCaps];
if (HidP_GetValueCaps(HIDP_REPORT_TYPE.HidP_Output, outputValueCaps, ref numberOutputValueCaps, preparedData) != HIDP_STATUS_SUCCESS)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

var numberInputButtonCaps = hidCaps.NumberInputButtonCaps;
if (numberInputButtonCaps != 0)
{
inputButtonCaps = new HidP_Button_Caps[numberInputButtonCaps];
if (HidP_GetButtonCaps(HIDP_REPORT_TYPE.HidP_Input, inputButtonCaps, ref numberInputButtonCaps, preparedData) != HIDP_STATUS_SUCCESS)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

var numberInputValueCaps = hidCaps.NumberInputValueCaps;
if (numberInputValueCaps != 0)
{
inputValueCaps = new HidP_Value_Caps[numberInputValueCaps];
if (HidP_GetValueCaps(HIDP_REPORT_TYPE.HidP_Input, inputValueCaps, ref numberInputValueCaps, preparedData) != HIDP_STATUS_SUCCESS)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

var numberFeatureButtonCaps = hidCaps.NumberFeatureButtonCaps;
if (numberFeatureButtonCaps != 0)
{
featureButtonCaps = new HidP_Button_Caps[numberFeatureButtonCaps];
if (HidP_GetButtonCaps(HIDP_REPORT_TYPE.HidP_Feature, featureButtonCaps, ref numberFeatureButtonCaps, preparedData) != HIDP_STATUS_SUCCESS)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

var numberFeatureValueCaps = hidCaps.NumberFeatureValueCaps;
if (numberFeatureValueCaps != 0)
{
featureValueCaps = new HidP_Value_Caps[numberFeatureValueCaps];
if (HidP_GetValueCaps(HIDP_REPORT_TYPE.HidP_Feature, featureValueCaps, ref numberFeatureValueCaps, preparedData) != HIDP_STATUS_SUCCESS)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
catch (Win32Exception)
{
throw;
}
finally
{
if(preparedData != IntPtr.Zero)
HidD_FreePreparsedData(ref preparedData)
}


MacDie_88

  • Member
  • ***
  • Posts: 7
Ksrsrinivansan thank you so much for your fast response and help. I used the structure definitions and imports as you told me. What I need to use in my project is featureValueCaps. I'm still getting the right data on the first array but after that everything seems wrong. So on the array of structure featureValueCaps
  • all the data inside seems correct and on featureValueCaps [1],featureValueCaps [2], etc the data is wrong. My code looks like this:
Code: [Select]

internal HIDP_CAPS GetDeviceCapabilities( SafeFileHandle hidHandle )
        {             
            IntPtr preparsedData = new System.IntPtr();
            Int32 result = 0;
            Boolean success = false;
            HidP_Button_Caps[] featureButtonCaps = null;
            HidP_Value_Caps[] featureValueCaps = null;

try
{
success = HidD_GetPreparsedData(hidHandle, ref preparsedData);
                result = HidP_GetCaps(preparsedData, ref Capabilities);
if ((result != 0))
{

                    var numberFeatureButtonCaps = Capabilities.NumberFeatureButtonCaps;
if (numberFeatureButtonCaps !=0)                   
                    {

                        featureButtonCaps = new HidP_Button_Caps[numberFeatureButtonCaps];
                        if (HidP_GetButtonCaps(HidP_Feature, featureButtonCaps, ref numberFeatureButtonCaps, preparsedData) != HIDP_STATUS_SUCCESS)
                        {
                            throw new Win32Exception(Marshal.GetLastWin32Error());                       
                        }                       
                    }

                    var numberFeatureValueCaps = Capabilities.NumberFeatureValueCaps;                   
                    if (numberFeatureValueCaps != 0)
                    {

                        featureValueCaps = new HidP_Value_Caps[numberFeatureValueCaps];
                        if (HidP_GetValueCaps(HidP_Feature, featureValueCaps, ref numberFeatureValueCaps, preparsedData) != HIDP_STATUS_SUCCESS)
                        {

                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }
                    }                                   

}
}
catch (Exception ex)
{
DisplayException(MODULE_NAME, ex);
throw;
}
finally
{

if (preparsedData != IntPtr.Zero)
{
success = HidD_FreePreparsedData(preparsedData);
}
}
           
            return Capabilities;             
        }


Using visual studio I insert a breakpoint so I can see the data inside featureValueCaps[0] (WHICH SEEMS RIGHT), then I tried to see data inside featureValueCaps[1] which is wrong. I have attached two pictures showing the data that I am getting.
WRONG DATA

RIGHT DATA



Thank you so much for your help.

ksrsrinivasan

  • Member
  • ***
  • Posts: 5
MacDie_88,

Looking at the HasNull definition on HIDP_VALUE_CAPS structure on MSDN (http://msdn.microsoft.com/en-us/library/windows/hardware/ff539832%28v=vs.85%29.aspx),

Quote
HasNull
Specifies, if TRUE, that the usage supports a NULL value, which indicates that the data is not valid and should be ignored. Otherwise, if HasNull is FALSE, the usage does not have a NULL value.

It looks like you should be ignoring the second value cap, since it has its HasNull set to true and is really a null structure.

MacDie_88

  • Member
  • ***
  • Posts: 7
ksrsrinivasan,

For the rest of Value Caps (which are 69)  even if they have a False on the HasNull field the data does not make any sense either, the only data that makes sense is the first value cap. Am I using HidP_GetValueCaps in the right way? Because what I have read on MSDN (http://msdn.microsoft.com/en-us/library/windows/hardware/ff539754(v=vs.85).aspx) is telling me that it returns a value capability array.
Quote
The HidP_GetValueCaps routine returns a value capability array that describes all the HID control values in a top-level collection for a specified type of HID report.

Do I need to populate the array by calling another function? or HidP_GetValueCaps already returns the array of structures with all the data?

MacDie_88

  • Member
  • ***
  • Posts: 7
ksrsrinivasan,

I got it to work thank you very much I was just missing the struck HipP_NotRange and HipP_Range.  I just have one more question, how can I use public UInt16 Usage that is in the public struct HipP_NotRange so I can see the Usage ID for each value cap?

Thank you so much I was having trouble with this for almost a week, I really appreciate it.

ksrsrinivasan

  • Member
  • ***
  • Posts: 5
Hello MacDie_88,

Glad you got it working. The structures, data types and offsets are really important. The UInt16 Usage on the HidP_NotRange is the actual Usage ID. In your case, you'll be access it using
Code: [Select]
featureValueCaps[0].NotRange.Usage