从C#调用HidP_GetButtonCaps例程时出错

时间:2015-01-23 15:51:05

标签: interop dllimport hid

我花了好几个小时仍然无法理解为什么在C#中使用Interop时调用HidP_GetButtonCaps例程(来自hid.dll)会失败。

我正在列出设备并试图获取ButtonCaps等等。但是当调用HidP_GetButtonCaps(或HidP_GetValueCaps)时,extern函数返回HIDP_STATUS_INVALID_REPORT_TYPE错误。我只是没有得到它...这个例程中的报告类型参数是一个枚举值,它怎么会失败???

这是一些代码提取。我不会放一切,因为它很长。当然,如果需要提供更多细节,我会添加它。

代码中有注释" //备注",这里是备注内容: - 备注1:hidDevice对象参数来自另一个方法。我知道" DevicePath"," preparsedData"的内容。和" ButtonCaps"在调试模式下检查事物时字段是否正确。 - 备注2:这是我遇到问题的地方。我已经为参数尝试了3种不同的方式:使用HIDP_REPORT_TYPE对象,直接使用HIDP_REPORT_TYPE.HidP_Input或" 0",它没有任何区别......

非常感谢你的帮助。

结构,常数,枚举......:

    public enum HIDP_REPORT_TYPE : ushort
    {
        HidP_Input,
        HidP_Output,
        HidP_Feature
    }

    public struct ButtonCapsRange
    {
        public ushort UsageMin;
        public ushort UsageMax;
        public ushort StringMin;
        public ushort StringMax;
        public ushort DesignatorMin;
        public ushort DesignatorMax;
        public ushort DataIndexMin;
        public ushort DataIndexMax;
    }

    public struct ButtonCapsNotRange
    {
        public ushort Usage;
        public ushort Reserved1;
        public ushort StringIndex;
        public ushort Reserved2;
        public ushort DesignatorIndex;
        public ushort Reserved3;
        public ushort DataIndex;
        public ushort Reserved4;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct HIDP_BUTTON_CAPS
    {
        [FieldOffset(0)]
        public ushort UsagePage;
        [FieldOffset(2)]
        public byte ReportID;
        [FieldOffset(3)]
        public bool IsAlias;
        [FieldOffset(4)]
        public ushort BitField;
        [FieldOffset(6)]
        public ushort LinkCollection;
        [FieldOffset(8)]
        public ushort LinkUsage;
        [FieldOffset(10)]
        public ushort LinkUsagePage;
        [FieldOffset(12)]
        public bool IsRange;
        [FieldOffset(13)]
        public bool IsStringRange;
        [FieldOffset(14)]
        public bool IsDesignatorRange;
        [FieldOffset(15)]
        public bool IsAbsolute;
        [FieldOffset(16)]
        public uint[] Reserved;
        [FieldOffset(16 + 10 * 4)]
        public ButtonCapsRange Range;
        [FieldOffset(16 + 10 * 4)]
        public ButtonCapsNotRange NotRange;
    }

    public struct HID_DEVICE
    {
        public String DevicePath;
        public IntPtr pHidDevice; // A file handle to the hid device.
        public bool OpenedForRead;
        public bool OpenedForWrite;
        public bool OpenedOverlapped;
        public bool OpenedExclusive;
        public IntPtr Ppd; // The opaque parser info describing this device
        public HIDP_CAPS Caps; // The Capabilities of this hid device.
        public HIDD_ATTRIBUTES Attributes;
        public byte[] pInputReportBuffer;
        public HID_DATA[] InputData; // array of hid data structures
        public ulong InputDataLength; // Num elements in this array.
        public HIDP_BUTTON_CAPS[] pInputButtonCaps;
        public HIDP_VALUE_CAPS[] pInputValueCaps;
        public byte[] pOutputReportBuffer;
        public HID_DATA[] pOutputData;
        public ulong OutputDataLength;
        public HIDP_BUTTON_CAPS[] pOutputButtonCaps;
        public HIDP_VALUE_CAPS[] pOutputValueCaps;
        public byte[] pFeatureReportBuffer;
        public HID_DATA[] pFeatureData;
        public ulong FeatureDataLength;
        public HIDP_BUTTON_CAPS[] pFeatureButtonCaps;
        public HIDP_VALUE_CAPS[] pFeatureValueCaps;
    }

外部方法:

    [DllImport("hid.dll", SetLastError = true)]
    [SecurityPermission(SecurityAction.Assert, Unrestricted = true)]
    static extern int HidP_GetButtonCaps([MarshalAs(UnmanagedType.U2)] HIDP_REPORT_TYPE ReportType, ref HIDP_BUTTON_CAPS[] ButtonCaps, ref ushort ButtonCapsLength, IntPtr PreparsedData);

调用方法:

    private static bool FillDeviceInfo(ref HID_DEVICE hidDevice)
    { 
        //REMARK 1

        ulong numValues;
        ushort numCaps;
        HIDP_BUTTON_CAPS[] buttonCaps;
        HIDP_VALUE_CAPS[] valueCaps;
        HID_DATA[] data;
        ulong i;
        ushort usage;
        uint dataIdx;

        hidDevice.pInputReportBuffer = new byte[hidDevice.Caps.InputReportByteLength];

        buttonCaps = new HIDP_BUTTON_CAPS[hidDevice.Caps.NumberInputButtonCaps];
        //for(int a=0;a<buttonCaps.Length ;a++)
        //    buttonCaps[a].Reserved = new uint[10];
        hidDevice.pInputButtonCaps = buttonCaps;

        valueCaps = new HIDP_VALUE_CAPS[hidDevice.Caps.NumberInputValueCaps];
        hidDevice.pInputValueCaps = valueCaps;

        numCaps = hidDevice.Caps.NumberInputButtonCaps;

        if (numCaps > 0)
        {
            //REMARK 2
            HIDP_REPORT_TYPE reportType = HIDP_REPORT_TYPE.HidP_Input;
            int val = (HidP_GetButtonCaps(reportType, ref buttonCaps, ref numCaps, hidDevice.Ppd));
            if (HIDP_STATUS_SUCCESS != val)
            {
                return false;
            }
        }
//other stuff and retur true at end
     }

2 个答案:

答案 0 :(得分:1)

确定发现了问题......经常发生愚蠢的错误。

我解决了&#34;无效报告类型&#34;的问题。删除&#34;:ushort&#34;在枚举定义中。我认为它是C中的2字节数据,但它是一个4字节的数据。

此外,在HidP_GetButtonCaps中使用托管内存中的表时出现问题。

我没有在HidP_GetButtonCaps中使用HIDP_BUTTON_CAPS数组(在托管内存中初始化),而是使用IntPtr引用非托管内存区域。

答案 1 :(得分:0)

我遇到了同样的异常。我认为在CLR中尝试通过联合将数据自动编组到结构时存在问题。也许。我写了自己的拆包方法,看来已经解决了这个问题。

public static HidpButtonCaps FromByteArray(byte[] bytes, int offset)
{
    var hbc = new HidpButtonCaps()
    {
        UsagePage = (ushort)(((ushort)bytes[offset+1] << 8) | (ushort)(bytes[offset])),
        ReportID = bytes[offset + 2] > 0 ? true : false,
        IsAlias = bytes[offset + 3] > 0 ? true : false,
        BitField = (ushort)(((ushort)bytes[offset + 5] << 8) | (ushort)(bytes[offset + 4])),
        LinkCollection = (ushort)(((ushort)bytes[offset + 7] << 8) | (ushort)(bytes[offset + 6])),
        LinkUsage = (ushort)(((ushort)bytes[offset + 9] << 8) | (ushort)(bytes[offset + 8])),
        IsRange = bytes[offset + 10] > 0 ? true : false,
        IsStringRange = bytes[offset + 11] > 0 ? true : false,
        IsDesignatorRange = bytes[offset + 12] > 0 ? true : false,
        IsAbsolute = bytes[offset + 13] > 0 ? true : false,
        // skip reserved
        Range = new HidpButtonCapsRange()
        {
            UsageMin = (ushort)(((ushort)bytes[offset + 57] << 8) | (ushort)(bytes[offset + 56])),
            UsageMax = (ushort)(((ushort)bytes[offset + 59] << 8) | (ushort)(bytes[offset + 58])),
            StringMin = (ushort)(((ushort)bytes[offset + 61] << 8) | (ushort)(bytes[offset + 61])),
            StringMax = (ushort)(((ushort)bytes[offset + 63] << 8) | (ushort)(bytes[offset + 62])),
            DesignatorMin = (ushort)(((ushort)bytes[offset + 65] << 8) | (ushort)(bytes[offset + 64])),
            DesignatorMax = (ushort)(((ushort)bytes[offset + 67] << 8) | (ushort)(bytes[offset + 66])),
            DataIndexMin = (ushort)(((ushort)bytes[offset + 69] << 8) | (ushort)(bytes[offset + 68])),
            DataIndexMax = (ushort)(((ushort)bytes[offset + 71] << 8) | (ushort)(bytes[offset + 70])),
        }
    };

    return hbc;
}

我这样使用

dwSize = hidpcaps.NumberInputButtonCaps;
var bytelength = Marshal.SizeOf(typeof(HidpButtonCaps)) * (int)dwSize;
HidpButtonCaps[] buttonCaps = new HidpButtonCaps[(int)dwSize];
var pbuttoncaps = Marshal.AllocHGlobal(bytelength);
hidpstatus = (HidpStatus)HidP_GetButtonCaps(HidpReportType.HidP_Input, pbuttoncaps, ref dwSize, pPreparsedData);
if (hidpstatus != HidpStatus.HIDP_STATUS_SUCCESS)
{
    throw new Win32Exception($"HidP_GetButtonCaps: {hidpstatus.ToString()}");
}

var buttonCapBytes = new byte[bytelength];
Marshal.Copy(pbuttoncaps, buttonCapBytes, 0, bytelength);
for (int i=0; i<(int)hidpcaps.NumberInputButtonCaps; i++)
{
    buttonCaps[i] = HidpButtonCaps.FromByteArray(buttonCapBytes, i * Marshal.SizeOf(typeof(HidpButtonCaps)));
}