I am trying to get raw input data from a Surface touch screen, using RAWINPUT API.
Getting the devices list works good. But when I want to register the touch screen device with RegisterRawInputDevices, I have issues : the function returns false with 87 (ERROR_INVALID_PARAMETER) in GetLastError... I have tried different things that I read online but none work.
I am using Interop on C# for very simple desktop console application, developping with Visual Studio 2013 on Windows 8.1. The target is a Surface Pro 3.
The code doing the work with the API is hereunder... (sorry for the ugly editing).
I will be so thanksful for your help :)
Pierre
CLASS for the API wrapping in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace ConsoleApplication3
{
class APIWrapper
{
#region Properties
private static int _vendorID=-1;
public static int VendorID
{
get { return _vendorID; }
}
private static int _productID=-1;
public static int ProductID
{
get { return _productID; }
}
#endregion
#region API structs
internal struct RAWINPUTDEVICELIST_ELMT
{
public IntPtr hDevice;
public uint dwType;
}
public enum RawInputDeviceType : uint
{
RIM_TYPEMOUSE = 0,
RIM_TYPEKEYBOARD = 1,
RIM_TYPEHID = 2,
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUTDEVICE
{
public ushort usUsagePage;
public ushort usUsage;
public int dwFlags;
public IntPtr hwndTarget;
}
public enum RawInputDeviceInfoType : uint
{
RIDI_DEVICENAME = 0x20000007,
RIDI_DEVICEINFO = 0x2000000b,
RIDI_PREPARSEDDATA = 0x20000005,
}
public const ushort RIDEV_INPUTSINK =0x00000100;
public const ushort RIDEV_PAGEONLY = 0x00000020;
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_HID
{
public int dwVendorId;
public int dwProductId;
public int dwVersionNumber;
public ushort usUsagePage;
public ushort usUsage;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_MOUSE
{
public int dwId;
public int dwNumberOfButtons;
public int dwSampleRate;
public bool fHasHorizontalWheel;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_KEYBOARD
{
public int dwType;
public int dwSubType;
public int dwKeyboardMode;
public int dwNumberOfFunctionKeys;
public int dwNumberOfIndicators;
public int dwNumberOfKeysTotal;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RID_DEVICE_INFO
{
[FieldOffset(0)]
public uint cbSize;
[FieldOffset(4)]
public RawInputDeviceType dwType;
[FieldOffset(8)]
public RID_DEVICE_INFO_MOUSE mouse;
[FieldOffset(8)]
public RID_DEVICE_INFO_KEYBOARD keyboard;
[FieldOffset(8)]
public RID_DEVICE_INFO_HID hid;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUTHEADER
{
public RawInputDeviceType dwType;
public int dwSize;
public IntPtr hDevice;
public uint wParam;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RAWMOUSE
{
[FieldOffset(0)]
public ushort usFlags;
[FieldOffset(2)]
public uint ulButtons;
[FieldOffset(4)]
public ushort usButtonFlags;
[FieldOffset(2)]
public ushort usButtonData;
[FieldOffset(6)]
public uint ulRawButtons;
[FieldOffset(10)]
public int lLastX;
[FieldOffset(14)]
public int lLastY;
[FieldOffset(18)]
public uint ulExtraInformation;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWKEYBOARD
{
public ushort MakeCode;
public ushort Flags;
public ushort Reserved;
public ushort VKey;
public uint Message;
public uint ExtraInformation;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWHID
{
public int dwSizeHid;
public int dwCount;
//use of a pointer here for struct reason
public IntPtr pbRawData;
}
[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;
}
#endregion
#region API functions
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetRawInputDeviceList([In, Out] RAWINPUTDEVICELIST_ELMT[] InputdeviceList, [In, Out] ref uint puiNumDevices, [In] uint cbSize);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetRawInputDeviceInfo([In] IntPtr hDevice, [In] RawInputDeviceInfoType uiCommand, [In, Out] IntPtr pData, [In, Out] ref uint pcbSize);
[DllImport("user32.dll", SetLastError = true)]
static extern bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevices, uint uiNumDevices, uint cbSize);
[DllImport("user32.dll")]
static extern uint GetRegisteredRawInputDevices([In, Out] RAWINPUTDEVICE[] InputdeviceList, [In, Out] ref uint puiNumDevices, [In] uint cbSize);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetRawInputBuffer([In,Out] RAWINPUT[] pData, [In, Out] ref uint pcbSize, [In] uint cbSizeHeader);
#endregion
#region Methods for devices
public static bool GetRawInputDeviceList()
{
bool res = false;
RAWINPUTDEVICELIST_ELMT[] pRawInputDeviceList = null;
uint puiNumDevices = 0;
uint returnCode = GetRawInputDeviceList(pRawInputDeviceList, ref puiNumDevices, (uint)Marshal.SizeOf(new RAWINPUTDEVICELIST_ELMT()));
res = (0xFFFFFFFF != returnCode);
if (res)
{
//alloc array
pRawInputDeviceList = new RAWINPUTDEVICELIST_ELMT[puiNumDevices];
//get devices
returnCode = GetRawInputDeviceList(pRawInputDeviceList, ref puiNumDevices, (uint)Marshal.SizeOf((typeof(RAWINPUTDEVICELIST_ELMT))));
res = (0xFFFFFFFF != returnCode);
if (res)
{
//look for the touchscreen.
bool foundTouchScreen = false;
foreach (RAWINPUTDEVICELIST_ELMT rawInputDevice in pRawInputDeviceList)
{
//IntPtr pData = IntPtr.Zero;
//uint strsize = 0;
//IntPtr deviceHandle = rawInputDevice.hDevice;
//returnCode = GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoType.RIDI_DEVICENAME, pData, ref strsize);
//pData = Marshal.AllocHGlobal((int)strsize);
//returnCode = GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoType.RIDI_DEVICENAME, pData, ref strsize);
////Console.WriteLine("Result = " + returnCode + " ErrorCode = " + Marshal.GetLastWin32Error());
//string name = Marshal.PtrToStringAnsi(pData);
//Console.WriteLine("Name = " + name);
uint structsize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
RID_DEVICE_INFO di = new RID_DEVICE_INFO();
di.cbSize = structsize;
IntPtr pData = Marshal.AllocHGlobal((int)structsize);
returnCode = GetRawInputDeviceInfo(rawInputDevice.hDevice, RawInputDeviceInfoType.RIDI_DEVICEINFO, pData, ref structsize);
if (0xFFFFFFF != returnCode && 0 != returnCode)
{
di = (RID_DEVICE_INFO)Marshal.PtrToStructure(pData, typeof(RID_DEVICE_INFO));
//Console.WriteLine("di.dwType = " + Enum.GetName(typeof(RawInputDeviceType), di.dwType));
switch (di.dwType)
{
case RawInputDeviceType.RIM_TYPEHID:
/* Console.WriteLine("di.hid.dwVendorId = " + di.hid.dwVendorId);
Console.WriteLine("di.hid.dwProductId = " + di.hid.dwProductId);
Console.WriteLine("di.hid.dwVersionNumber = " + di.hid.dwVersionNumber);
Console.WriteLine("di.hid.usUsagePage = " + di.hid.usUsagePage);
Console.WriteLine("di.hid.usUsage = " + di.hid.usUsage);*/
if (0x0D == di.hid.usUsagePage && 0x04 == di.hid.usUsage)
{
_vendorID = di.hid.dwVendorId;
_productID = di.hid.dwProductId;
foundTouchScreen = true;
}
break;
case RawInputDeviceType.RIM_TYPEKEYBOARD:
case RawInputDeviceType.RIM_TYPEMOUSE:
default:
break;
}
if (foundTouchScreen)
{
RAWINPUTDEVICE[] rawInputDevicesToMonitor = new RAWINPUTDEVICE[1];
RAWINPUTDEVICE device = new RAWINPUTDEVICE();
device.dwFlags =RIDEV_INPUTSINK;
device.hwndTarget = Process.GetCurrentProcess().MainWindowHandle;
device.usUsage = di.hid.usUsage;
device.usUsagePage = di.hid.usUsagePage;
rawInputDevicesToMonitor[0] = device;
if (!RegisterRawInputDevices(rawInputDevicesToMonitor, (uint)1, (uint)Marshal.SizeOf(new RAWINPUTDEVICE())))
{
Console.WriteLine("Registration of device --> NOK (error: " + Marshal.GetLastWin32Error()+")");
RAWINPUTDEVICE [] pRegisteredRawInputDeviceList = null;
uint puiNumRegDevices = 0;
returnCode = GetRegisteredRawInputDevices(pRegisteredRawInputDeviceList, ref puiNumRegDevices, (uint)Marshal.SizeOf(new RAWINPUTDEVICE()));
res = (0xFFFFFFFF != returnCode);
if (res)
{
//alloc array
pRegisteredRawInputDeviceList = new RAWINPUTDEVICE[puiNumRegDevices];
//get devices
returnCode = GetRegisteredRawInputDevices(pRegisteredRawInputDeviceList, ref puiNumRegDevices, (uint)Marshal.SizeOf((typeof(RAWINPUTDEVICE))));
Console.WriteLine("Registered devices nb : " + returnCode);
}
}
break;
}
}
}
}
/*Need to set RIDEV_INPUTSINK in the dwFlags member and set hwndTarget to be able to make use of this function otherwise
* GetRawInputBuffer will ALWAYS say 0 when you try to find out what size you need the buffer to be.
*/
}
/*else
int error = Marshal.GetLastWin32Error();*/
return res;
}
#endregion
}
}
MAIN CLASS:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
//look for the touch screen device
if (!APIWrapper.GetRawInputDeviceList())
{
Console.WriteLine("Error occured when getting RawInputDevice List.");
}
if (APIWrapper.VendorID == -1 && APIWrapper.ProductID == -1)
{
Console.WriteLine("No Touchscreen device has been found.");
}
else
{
Console.WriteLine("Touchscreen found : VendorID=" + APIWrapper.VendorID + " ProductID=" + APIWrapper.ProductID);
//wait for rawinputdata
Console.WriteLine("Waiting data loop : started.");
uint dataNb = APIWrapper.ReadHIDData();
switch (dataNb)
{
case 0xFFFFFFFF:
Console.WriteLine("An error occured.");
break;
case 0:
Console.WriteLine("No data received.");
break;
default:
Console.WriteLine(dataNb + " rawInputData received");
break;
}
Console.WriteLine("Waiting data loop : ended.");
}
Console.ReadLine();
}
}
}