Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RAWINPUT RAWHID Structure in C# Byte Array

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#

like image 981
Ian.V Avatar asked Oct 23 '25 00:10

Ian.V


1 Answers

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.

like image 199
Ian.V Avatar answered Oct 25 '25 15:10

Ian.V