I am trying to read data from HID devices using:
uint dwsize = 0;
//RAWINPUT input = new RAWINPUT();
uint result = GetRawInputData(lParam, RID_INPUT, IntPtr.Zero, ref dwsize, Marshal.SizeOf(typeof(RAWINPUTHEADER)));
//_rawBuffer = new RAWINPUT(new RAWINPUTHEADER(), new RAWDATA(new RAWKEYBOARD(), new RAWHID(0,0)));
result = GetRawInputData(lParam, RID_INPUT, out _rawBuffer, ref dwsize, Marshal.SizeOf(typeof(RAWINPUTHEADER)));
This code works, but there is one problem getting the byte array from the RAWHID structure, according to the MSDN documentations this is how it should look:
typedef struct tagRAWHID {
DWORD dwSizeHid;
DWORD dwCount;
BYTE bRawData[1];
} RAWHID, *PRAWHID, *LPRAWHID;
This is what I have:
[StructLayout(LayoutKind.Sequential)]
internal struct RAWHID
{
public uint dwSizHid;
public uint dwCount;
public byte bRawData;
}
I have tried to look everywhere but could not find a solution that worked. I also tried Getting a IntPtr instead of a byte array and marshal copying that using this code:
int length = (int)(_rawBuffer.data.hid.dwCount * _rawBuffer.data.hid.dwSizHid);
byte[] bytes = new byte[length];
GCHandle pinnedPacket = GCHandle.Alloc(_rawBuffer.data.hid.bRawData, GCHandleType.Pinned);
Marshal.Copy(_rawBuffer.data.hid.bRawData, bytes, 0, length);
pinnedPacket.Free();
The reason I pinned it is because I keep getting the
System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'
Error.
Question short: How do I make the RAWHID structure work with a byte array in C#
This is not an answer using only C#, but this is how I solved it. If you know how to do it in just C# or a different way feel free to answer!
I solved it by making my own C++ dll, I do not have a lot of experience with C++ so please comment improvements. But here is my C++:
#include "stdafx.h"
#include <iostream>
extern "C" {
#include "Winuser.h"
__declspec(dllexport)
BYTE*
__cdecl
GetRawInputDataHid(_In_ HRAWINPUT hRawInput, //Returns Byte array
_In_ UINT uiCommand,
_Inout_ PUINT pcbSize,
_In_ UINT cbSizeHeader) {
UINT dwSize;
RAWINPUT *raw;
LPBYTE lpb;
dwSize = (UINT)pcbSize;
lpb = new BYTE[dwSize];
GetRawInputData(hRawInput, uiCommand, lpb, pcbSize, cbSizeHeader);
raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEHID) { // Checks if device is HID
INT rawDataSize = raw->data.hid.dwCount*raw->data.hid.dwSizeHid;
BYTE * returnval = new BYTE[rawDataSize];
std::memcpy(returnval, raw->data.hid.bRawData, rawDataSize); // Copies memory
if (sizeof(returnval) <= 0) { return NULL; } // Return Null of byte array length is 0 or lower
delete lpb; // Deletes lpb
return returnval; // Returns Byte array
}
delete lpb; // Deletes lpb
return NULL; // Returns null if not HID
}
__declspec(dllexport)
INT
__cdecl
freeMem(BYTE* arrayPtr) { //Frees memory
if (arrayPtr != nullptr) {
delete[] arrayPtr;
}
return 0;
}
}
In my C# I added the following:
/* OWN DLL PATH FOR TESTING */
private const string DllFilePath = @"D:\VS 2017\repos\GetRawInputDataHid\Release\GetRawInputDataHid.dll";
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetRawInputDataHid(IntPtr hRawInput, uint uiCommand, ref uint pcbSize, int cbSizeHeader);
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);
And also the following if device is a HID device:
IntPtr bytesData = GetRawInputDataHid(lParam, RID_INPUT, ref dwsize, Marshal.SizeOf(typeof(RAWINPUTHEADER)));
int length = (int)(_rawBuffer.data.hid.dwCount * _rawBuffer.data.hid.dwSizHid);
byte[] bytes = new byte[length];
if (bytesData != IntPtr.Zero)
{
Marshal.Copy(bytesData, bytes, 0, length);
freeMem(bytesData);
} else
{
uint error = GetLastError();
Console.WriteLine(error);
}
Hope this may help some people out there, if you know how to do it in just C# or a different way feel free to answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With