Weird values from WM_INPUT, casting mistake?

2019-07-26 07:08发布

问题:

About

I am trying to get the raw mouse input of the system into a C# application. I am using WM_INPUT (MSDN link) to get access to the data. To do so I am using the user32.dll. This (stackoverflow link) helped me alot with writing the API Wrapper.

Current approach

The following snippet shows the WndProc method. rimTypeMouseCount already shows that the windows messages seem to get fetched correctly. When I turn the polling rate of my mouse up or down I can see this also by checking the count - Lower polling rate means a lower count, higher polling rate means a higher count while logging for equal time intervals. That's good so far.

WndProc callback method:

protected override void WndProc(ref Message m)
{
    if (m.Msg == 0x00ff) {
        uint dwSize = 40;

        byte[] raw = new byte[40];
        IntPtr rawPtr = Marshal.AllocHGlobal(raw.Length);

        APIWrapper.GetRawInputData((IntPtr)m.LParam, 0x10000003, rawPtr, ref dwSize, Marshal.SizeOf(new APIWrapper.RAWINPUTHEADER()));

        Marshal.Copy(rawPtr, raw, 0, 40);

        GCHandle handle = GCHandle.Alloc(raw, GCHandleType.Pinned);
        APIWrapper.RAWINPUT rawData = (APIWrapper.RAWINPUT)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(APIWrapper.RAWINPUT));
        handle.Free();

        if (rawData.header.dwType == 0)
        {
            rimTypeMouseCount++;
            logMouseX = logMouseX + rawData.mouse.lLastX.ToString() + Environment.NewLine;
            logMouseY = logMouseY + rawData.mouse.lLastY.ToString() + Environment.NewLine;
        }
    }

    base.WndProc(ref m);
}

RAWINPUT struct:

[StructLayout(LayoutKind.Explicit)]
    internal struct RAWINPUT
    {
        [FieldOffset(0)]
        public RAWINPUTHEADER header;

        [FieldOffset(16 + 8)]
        public RAWMOUSE mouse;

        [FieldOffset(16 + 8)]
        public RAWKEYBOARD keyboard;

        [FieldOffset(16 + 8)]
        public RAWHID hid;
    }

Problem

But there is a problem with logMouseX and logMouseY. Those to strings simply append the mouse deltas of the x and y axis with every call. Unfortunately logMouseX only logs the values 0 or 65535 while logMouseY logs 0 only. I also tried to log messages like left mouse button up/down and so on. Nothing seems to work actually. For me it seems a little that the numbers might be random or something like that.

Question

This is what I (think I) do:

  1. APIWrapper.GetRawInputData(...) needs a pointer to a byte array. So I simply allocated space in unmanaged memory with marshalling and got a pointer as a result.
  2. I am calling the function that fills the array the pointer is pointing at.
  3. I am taking the pointer that points to the filled array and copy its content into an actual byte array.
  4. To access the fields of the struct RAWINPUT I need to cast the byte array to a RAWINPUT struct.
  5. I do this with the three lines that use a GCHandle. I actually found that snippet somewhere but never worked with that class before.

Might it be that I am messing up something there? Maybe something like a Big-Endian and Little-Endian mix up?

Edits

  • I found out that the values I get when using rawData.mouse.lLastX & rawData.mouse.lLastY behave like this:

    • X is zero when moving the mouse downwards and 65535 when moving upwards
    • Y is zero always no matter if I move the mouse left or right
  • I found out that using rawDataL.hid.pbRawData includes more information about the movement. There I get values in dependency of how fast I am moving the mouse. Unfortunately there is no X or Y component so this only works for one axis (downwards/upwards).

  • I found a simpler way of casts. Unfortunately I still get the stranges values as described above. It looks like this now:

Casting part of WndProc:

uint dwSize = 40;

byte[] raw = new byte[40];
GCHandle handle = GCHandle.Alloc(raw, GCHandleType.Pinned);

APIWrapper.GetRawInputData((IntPtr)m.LParam, 0x10000003, handle.AddrOfPinnedObject(), ref dwSize, Marshal.SizeOf(new APIWrapper.RAWINPUTHEADER()));

APIWrapper.RAWINPUT rawData = (APIWrapper.RAWINPUT)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(APIWrapper.RAWINPUT));

if (rawData.header.dwType == 0)
{
    // accessing data
}

handle.Free();

回答1:

I posted my question here (MSDN link) and I was told that the offsets of the RAWMOUSE struct are not correct.

So I simply logged all bytes of the raw data array and finally could see all the data I need and therefore the correct offsets. I guess the offsets differ when using 32 bit or 64 bit machines. Here are the relevant code changes:

WndProc changes:

RawMouseData rawData = (RawMouseData) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(RawMouseData));

RawMouseData struct:

[StructLayout(LayoutKind.Explicit)]
internal struct RawMouseData
{
    [FieldOffset(20)]
    public ButtonFlags buttonFlags;
    [FieldOffset(22)]
    public ushort buttonData;
    [FieldOffset(28)]
    public int lastX;
    [FieldOffset(32)]
    public int lastY;
}

This is how the data log of the raw byte array looks like for each mouse movement direction:

Downwards:      Upwards:
Byte 29: 0      Byte 29: 0 
Byte 30: 0      Byte 30: 0 
Byte 31: 0      Byte 31: 0 
Byte 32: 0      Byte 32: 0 
Byte 33: 1      Byte 33: 255 
Byte 34: 0      Byte 34: 255 
Byte 35: 0      Byte 35: 255 
Byte 36: 0      Byte 36: 255

Left:           Right:
Byte 29: 255    Byte 29: 1 
Byte 30: 255    Byte 30: 0 
Byte 31: 255    Byte 31: 0 
Byte 32: 255    Byte 32: 0 
Byte 33: 0      Byte 33: 0 
Byte 34: 0      Byte 34: 0 
Byte 35: 0      Byte 35: 0 
Byte 36: 0      Byte 36: 0 

The faster I move the mouse the bigger (when moving downwards or right) or smaller (when moving upwards or left) the values get.