我花了好几个小时仍然无法理解为什么在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
}
答案 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)));
}