[Win] Completed raw input IJoystickDriver2

For improved safety, we are now using managed memory buffers instead of
stack allocations and pointers.
This commit is contained in:
Stefanos A 2014-01-18 16:00:27 +01:00 committed by thefiddler
parent f6b382c929
commit cec48ab20a
7 changed files with 339 additions and 146 deletions

View file

@ -1500,16 +1500,6 @@ namespace OpenTK.Platform.Windows
/// <para>If Data is not large enough for the data, the function returns -1. If Data is NULL, the function returns a value of zero. In both of these cases, Size is set to the minimum size required for the Data buffer.</para>
/// <para>Call GetLastError to identify any other errors.</para>
/// </returns>
[CLSCompliant(false)]
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern UINT GetRawInputDeviceInfo(
HANDLE Device,
[MarshalAs(UnmanagedType.U4)] RawInputDeviceInfoEnum Command,
[In, Out] LPVOID Data,
[In, Out] ref UINT Size
);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern INT GetRawInputDeviceInfo(
@ -1519,6 +1509,16 @@ namespace OpenTK.Platform.Windows
[In, Out] ref INT Size
);
[CLSCompliant(false)]
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern INT GetRawInputDeviceInfo(
HANDLE Device,
[MarshalAs(UnmanagedType.U4)] RawInputDeviceInfoEnum Command,
[In, Out] byte[] Data,
[In, Out] ref INT Size
);
/// <summary>
/// Gets information about the raw input device.
/// </summary>
@ -2791,7 +2791,7 @@ namespace OpenTK.Platform.Windows
/// <summary>
/// Size, in bytes, of the RawInputDeviceInfo structure.
/// </summary>
DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
internal DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
/// <summary>
/// Type of raw input data.
/// </summary>

View file

@ -43,38 +43,54 @@ namespace OpenTK.Platform.Windows
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetButtonCaps")]
public static extern HidProtocolStatus GetButtonCaps(HidProtocolReportType hidProtocolReportType,
IntPtr button_caps, ref ushort p, IntPtr preparsed_data);
IntPtr button_caps, ref ushort p, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetButtonCaps")]
public static extern HidProtocolStatus GetButtonCaps(HidProtocolReportType hidProtocolReportType,
HidProtocolButtonCaps[] button_caps, ref ushort p, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetCaps")]
public static extern HidProtocolStatus GetCaps(IntPtr preparsed_data, ref HidProtocolCaps capabilities);
public static extern HidProtocolStatus GetCaps(byte[] preparsed_data, ref HidProtocolCaps capabilities);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetData")]
public static extern HidProtocolStatus GetData(HidProtocolReportType type,
IntPtr data, ref int data_length,
IntPtr preparsed_data, IntPtr report, int report_length);
byte[] preparsed_data, IntPtr report, int report_length);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetData")]
public static extern HidProtocolStatus GetData(HidProtocolReportType type,
HidProtocolData[] data, ref int data_length,
byte[] preparsed_data, IntPtr report, int report_length);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetScaledUsageValue")]
static public extern HidProtocolStatus GetScaledUsageValue(HidProtocolReportType type,
HIDPage usage_page, short link_collection, short usage, ref int usage_value,
IntPtr preparsed_data, IntPtr report, int report_length);
byte[] preparsed_data, IntPtr report, int report_length);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetUsageValue")]
public static extern HidProtocolStatus GetUsageValue(HidProtocolReportType type,
HIDPage usage_page, short link_collection, short usage, ref uint usage_value,
IntPtr preparsed_data, IntPtr report, int report_length);
byte[] preparsed_data, IntPtr report, int report_length);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetValueCaps")]
public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type, IntPtr caps,
ref ushort caps_length, IntPtr preparsed_data);
public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type,
IntPtr caps, ref ushort caps_length, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetValueCaps")]
public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type,
HidProtocolValueCaps[] caps, ref ushort caps_length, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_MaxDataListLength")]
public static extern int MaxDataListLength(HidProtocolReportType type, IntPtr preparsed_data);
public static extern int MaxDataListLength(HidProtocolReportType type, byte[] preparsed_data);
}
enum HidProtocolReportType : ushort

View file

@ -45,7 +45,6 @@ namespace OpenTK.Platform.Windows
// WinFactory constructor has finished running. The reason is
// that they call WinFactory methods internally.
WinRawInput rawinput_driver; // For keyboard and mouse input
WinCombinedJoystick joystick_driver; // For joystick input
internal static IntPtr OpenGLHandle { get; private set; }
const string OpenGLName = "OPENGL32.DLL";
@ -132,7 +131,7 @@ namespace OpenTK.Platform.Windows
public override IJoystickDriver2 CreateJoystickDriver()
{
return CombinedJoystickDriver;
return RawInputDriver.JoystickDriver;
}
#endregion
@ -154,22 +153,6 @@ namespace OpenTK.Platform.Windows
}
}
WinCombinedJoystick CombinedJoystickDriver
{
get
{
lock (SyncRoot)
{
if (joystick_driver == null)
{
joystick_driver = new WinCombinedJoystick(
new XInputJoystick(), new WinMMJoystick());
}
return joystick_driver;
}
}
}
#endregion
#region IDisposable Members
@ -181,7 +164,6 @@ namespace OpenTK.Platform.Windows
if (manual)
{
rawinput_driver.Dispose();
joystick_driver.Dispose();
}
base.Dispose(manual);

View file

@ -88,36 +88,41 @@ namespace OpenTK.Platform.Windows
// Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID.
protected unsafe override IntPtr WindowProcedure(
IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
try
{
switch (message)
{
case WindowMessage.INPUT:
int size = 0;
#if false
// Get the size of the input buffer
Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
IntPtr.Zero, ref size, API.RawInputHeaderSize);
void* data_ptr = stackalloc byte[size];
RawInput* data = (RawInput*)data_ptr;
#endif
RawInput data;
// Read the actual raw input structure
if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
(IntPtr)data_ptr, ref size, API.RawInputHeaderSize))
out data, ref size, API.RawInputHeaderSize))
{
switch (data->Header.Type)
switch (data.Header.Type)
{
case RawInputDeviceType.KEYBOARD:
if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(ref *data))
if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(ref data))
return IntPtr.Zero;
break;
case RawInputDeviceType.MOUSE:
if (((WinRawMouse)MouseDriver).ProcessMouseEvent(ref *data))
if (((WinRawMouse)MouseDriver).ProcessMouseEvent(ref data))
return IntPtr.Zero;
break;
case RawInputDeviceType.HID:
if (((WinRawJoystick)JoystickDriver).ProcessEvent(data))
if (((WinRawJoystick)JoystickDriver).ProcessEvent(ref data))
return IntPtr.Zero;
break;
}
@ -132,6 +137,12 @@ namespace OpenTK.Platform.Windows
}
return base.WindowProcedure(handle, message, wParam, lParam);
}
catch (Exception e)
{
Debug.Print("[WinRawInput] Caught unhandled exception {0}", e);
return IntPtr.Zero;
}
}
#endregion

View file

@ -76,6 +76,12 @@ namespace OpenTK.Platform.Windows
State.SetButton(button, value);
}
public void SetConnected(bool value)
{
Capabilities.SetIsConnected(value);
State.SetIsConnected(value);
}
public JoystickCapabilities GetCapabilities()
{
return Capabilities;
@ -134,6 +140,11 @@ namespace OpenTK.Platform.Windows
readonly Dictionary<int, IntPtr> IndexToDevice =
new Dictionary<int, IntPtr>();
byte[] PreparsedData = new byte[1024];
HidProtocolValueCaps[] AxisCaps = new HidProtocolValueCaps[4];
HidProtocolButtonCaps[] ButtonCaps = new HidProtocolButtonCaps[4];
HidProtocolData[] DataBuffer = new HidProtocolData[16];
public WinRawJoystick(IntPtr window)
{
Debug.WriteLine("Using WinRawJoystick.");
@ -148,6 +159,17 @@ namespace OpenTK.Platform.Windows
new RawInputDevice(HIDUsageGD.Joystick, RawInputDeviceFlags.INPUTSINK, window),
new RawInputDevice(HIDUsageGD.GamePad, RawInputDeviceFlags.INPUTSINK, window),
};
if (!Functions.RegisterRawInputDevices(DeviceTypes, DeviceTypes.Length, API.RawInputDeviceSize))
{
Debug.Print("[Warning] Raw input registration failed with error: {0}.",
Marshal.GetLastWin32Error());
}
else
{
Debug.Print("[WinRawJoystick] Registered for raw input");
}
RefreshDevices();
Debug.Unindent();
@ -157,20 +179,34 @@ namespace OpenTK.Platform.Windows
public void RefreshDevices()
{
if (!Functions.RegisterRawInputDevices(DeviceTypes, DeviceTypes.Length, API.RawInputDeviceSize))
// Mark all devices as disconnected. We will check which of those
// are connected later on.
for (int i = 0; i < IndexToDevice.Count; i++)
{
Debug.Print("[Warning] Raw input registration failed with error: {0}.",
Marshal.GetLastWin32Error());
}
else
{
Debug.Print("Registered for raw joystick input");
}
Devices[IndexToDevice[i]].SetConnected(false);
}
public unsafe bool ProcessEvent(RawInput* rin)
foreach (RawInputDeviceList dev in WinRawInput.GetDeviceList())
{
IntPtr handle = rin->Header.Device;
if (dev.Type != RawInputDeviceType.HID)
continue;
IntPtr handle = dev.Device;
Guid guid = GetDeviceGuid(handle);
JoystickCapabilities caps = GetDeviceCaps(handle);
if (!Devices.ContainsKey(handle))
Devices.Add(handle, new Device(handle, guid, caps));
Device stick = Devices[handle];
stick.SetConnected(true);
}
}
public unsafe bool ProcessEvent(ref RawInput rin)
{
IntPtr handle = rin.Header.Device;
Device stick = GetDevice(handle);
if (stick == null)
{
@ -178,49 +214,20 @@ namespace OpenTK.Platform.Windows
return false;
}
// Query the size of the _HIDP_PREPARSED_DATA structure for this event.
int preparsed_size = 0;
Functions.GetRawInputDeviceInfo(rin->Header.Device, RawInputDeviceInfoEnum.PREPARSEDDATA,
IntPtr.Zero, ref preparsed_size);
if (preparsed_size == 0)
return false;
// Allocate space for _HIDP_PREPARSED_DATA.
// This is an untyped blob of data.
void* preparsed_data_ptr = stackalloc byte[preparsed_size];
IntPtr preparsed_data = (IntPtr)preparsed_data_ptr;
if (Functions.GetRawInputDeviceInfo(rin->Header.Device, RawInputDeviceInfoEnum.PREPARSEDDATA,
preparsed_data, ref preparsed_size) < 0)
return false;
// Query joystick capabilities
HidProtocolCaps caps = new HidProtocolCaps();
if (HidProtocol.GetCaps(preparsed_data, ref caps) != HidProtocolStatus.Success)
return false;
// Axis capabilities
HidProtocolValueCaps* axes_caps = stackalloc HidProtocolValueCaps[caps.NumberInputValueCaps];
if (HidProtocol.GetValueCaps(HidProtocolReportType.Input,
(IntPtr)axes_caps, ref caps.NumberInputValueCaps, preparsed_data) !=
HidProtocolStatus.Success)
if (!GetPreparsedData(handle, ref PreparsedData))
{
return false;
}
// Button capabilities
HidProtocolButtonCaps* button_caps = stackalloc HidProtocolButtonCaps[caps.NumberInputButtonCaps];
if (HidProtocol.GetButtonCaps(HidProtocolReportType.Input,
(IntPtr)button_caps, ref caps.NumberInputButtonCaps, preparsed_data) !=
HidProtocolStatus.Success)
HidProtocolCaps caps;
if (!GetDeviceCaps(PreparsedData, out caps, ref AxisCaps, ref ButtonCaps))
{
Debug.Print("[WinRawJoystick] HidProtocol.GetButtonCaps() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
// Query current state
// Allocate enough storage to hold the data of the current report
int size = HidProtocol.MaxDataListLength(HidProtocolReportType.Input, preparsed_data);
int size = HidProtocol.MaxDataListLength(HidProtocolReportType.Input, PreparsedData);
if (size == 0)
{
Debug.Print("[WinRawJoystick] HidProtocol.MaxDataListLength() failed with {0}",
@ -229,37 +236,218 @@ namespace OpenTK.Platform.Windows
}
// Fill the data buffer
HidProtocolData* data = stackalloc HidProtocolData[size];
if (DataBuffer.Length < size)
{
Array.Resize(ref DataBuffer, size);
}
fixed (void* pdata = &rin.Data.HID.RawData)
{
if (HidProtocol.GetData(HidProtocolReportType.Input,
(IntPtr)data, ref size, preparsed_data,
new IntPtr(&rin->Data.HID.RawData), rin->Data.HID.Size) != HidProtocolStatus.Success)
DataBuffer, ref size, PreparsedData,
(IntPtr)pdata, rin.Data.HID.Size) != HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetData() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
}
UpdateAxes(stick, caps, axes_caps, data);
UpdateButtons(stick, caps, button_caps, data);
#if false
// Button state
//g_NumberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1;
#endif
UpdateAxes(stick, caps, AxisCaps, DataBuffer);
UpdateButtons(stick, caps, ButtonCaps, DataBuffer);
return true;
}
unsafe static void UpdateAxes(Device stick, HidProtocolCaps caps, HidProtocolValueCaps* axes_caps, HidProtocolData* data)
static bool GetPreparsedData(IntPtr handle, ref byte[] prepared_data)
{
// Query the size of the _HIDP_PREPARSED_DATA structure for this event.
int preparsed_size = 0;
Functions.GetRawInputDeviceInfo(handle, RawInputDeviceInfoEnum.PREPARSEDDATA,
IntPtr.Zero, ref preparsed_size);
if (preparsed_size == 0)
{
Debug.Print("[WinRawJoystick] Functions.GetRawInputDeviceInfo(PARSEDDATA) failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
// Allocate space for _HIDP_PREPARSED_DATA.
// This is an untyped blob of data.
if (prepared_data.Length < preparsed_size)
{
Array.Resize(ref prepared_data, preparsed_size);
}
if (Functions.GetRawInputDeviceInfo(handle, RawInputDeviceInfoEnum.PREPARSEDDATA,
prepared_data, ref preparsed_size) < 0)
{
Debug.Print("[WinRawJoystick] Functions.GetRawInputDeviceInfo(PARSEDDATA) failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
return true;
}
JoystickCapabilities GetDeviceCaps(IntPtr handle)
{
HidProtocolCaps caps;
if (GetPreparsedData(handle, ref PreparsedData) &&
GetDeviceCaps(PreparsedData, out caps, ref AxisCaps, ref ButtonCaps))
{
int axes = 0;
int dpads = 0;
int buttons = 0;
for (int i = 0; i < caps.NumberInputValueCaps; i++)
{
if (AxisCaps[i].IsRange)
continue; // Todo: range values not currently supported
switch (AxisCaps[i].UsagePage)
{
case HIDPage.GenericDesktop:
switch ((HIDUsageGD)AxisCaps[i].NotRange.Usage)
{
case HIDUsageGD.X:
case HIDUsageGD.Y:
case HIDUsageGD.Z:
case HIDUsageGD.Rx:
case HIDUsageGD.Ry:
case HIDUsageGD.Rz:
case HIDUsageGD.Slider:
case HIDUsageGD.Dial:
case HIDUsageGD.Wheel:
axes++;
break;
case HIDUsageGD.Hatswitch:
dpads++;
break;
}
break;
case HIDPage.Simulation:
switch ((HIDUsageSim)AxisCaps[i].NotRange.Usage)
{
case HIDUsageSim.Rudder:
case HIDUsageSim.Throttle:
axes++;
break;
}
break;
case HIDPage.Button:
buttons++;
break;
}
}
return new JoystickCapabilities(axes, buttons, true);
}
return new JoystickCapabilities();
}
static bool GetDeviceCaps(byte[] preparsed_data, out HidProtocolCaps caps,
ref HidProtocolValueCaps[] axes, ref HidProtocolButtonCaps[] buttons)
{
// Query joystick capabilities
caps = new HidProtocolCaps();
if (HidProtocol.GetCaps(preparsed_data, ref caps) != HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetCaps() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
// Make sure our caps arrays are big enough
if (axes.Length < caps.NumberInputValueCaps)
{
Array.Resize(ref axes, caps.NumberInputValueCaps);
}
if (buttons.Length < caps.NumberInputButtonCaps)
{
Array.Resize(ref buttons, caps.NumberInputButtonCaps);
}
// Axis capabilities
if (HidProtocol.GetValueCaps(HidProtocolReportType.Input,
axes, ref caps.NumberInputValueCaps, preparsed_data) !=
HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetValueCaps() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
// Button capabilities
if (HidProtocol.GetButtonCaps(HidProtocolReportType.Input,
buttons, ref caps.NumberInputButtonCaps, preparsed_data) !=
HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetButtonCaps() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
return true;
}
// Retrieves the GUID of a device, which is stored
// in the last part of the DEVICENAME string
Guid GetDeviceGuid(IntPtr handle)
{
Guid guid = new Guid();
unsafe
{
// Find out how much memory we need to allocate
// for the DEVICENAME string
int size = 0;
if (Functions.GetRawInputDeviceInfo(handle, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size) < 0 || size == 0)
{
Debug.Print("[WinRawJoystick] Functions.GetRawInputDeviceInfo(DEVICENAME) failed with error {0}",
Marshal.GetLastWin32Error());
return guid;
}
// Allocate memory and retrieve the DEVICENAME string
char* pname = stackalloc char[size + 1];
if (Functions.GetRawInputDeviceInfo(handle, RawInputDeviceInfoEnum.DEVICENAME, (IntPtr)pname, ref size) < 0)
{
Debug.Print("[WinRawJoystick] Functions.GetRawInputDeviceInfo(DEVICENAME) failed with error {0}",
Marshal.GetLastWin32Error());
return guid;
}
// Convert the buffer to a .Net string, and split it into parts
string name = new string(pname);
if (String.IsNullOrEmpty(name))
{
Debug.Print("[WinRawJoystick] Failed to construct device name");
return guid;
}
// The GUID is stored in the last part of the string
string[] parts = name.Split('#');
if (parts.Length >= 3)
{
guid = new Guid(parts[2]);
}
}
return guid;
}
static void UpdateAxes(Device stick, HidProtocolCaps caps, HidProtocolValueCaps[] axes, HidProtocolData[] data)
{
// Use the data indices in the axis and button caps arrays to
// access the data buffer we just filled.
for (int i = 0; i < caps.NumberInputValueCaps; i++)
{
HidProtocolValueCaps* axis = axes_caps + i;
if (!axis->IsRange)
if (!axes[i].IsRange)
{
int index = axis->NotRange.DataIndex;
int index = axes[i].NotRange.DataIndex;
if (index < 0 || index >= caps.NumberInputValueCaps)
{
// Should never happen
@ -267,20 +455,19 @@ namespace OpenTK.Platform.Windows
continue;
}
HidProtocolData* d = (data + index);
if (d->DataIndex != index)
if (data[i].DataIndex != index)
{
// Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
d->DataIndex, index);
data[i].DataIndex, index);
continue;
}
short value = (short)HidHelper.ScaleValue(d->RawValue,
axis->LogicalMin, axis->LogicalMax,
short value = (short)HidHelper.ScaleValue(data[i].RawValue,
axes[i].LogicalMin, axes[i].LogicalMax,
short.MinValue, short.MaxValue);
stick.SetAxis(axis->UsagePage, axis->NotRange.Usage, value);
stick.SetAxis(axes[i].UsagePage, axes[i].NotRange.Usage, value);
}
else
{
@ -289,14 +476,13 @@ namespace OpenTK.Platform.Windows
}
}
unsafe static void UpdateButtons(Device stick, HidProtocolCaps caps, HidProtocolButtonCaps* button_caps, HidProtocolData* data)
unsafe static void UpdateButtons(Device stick, HidProtocolCaps caps, HidProtocolButtonCaps[] buttons, HidProtocolData[] data)
{
for (int i = 0; i < caps.NumberInputButtonCaps; i++)
{
HidProtocolButtonCaps* button = button_caps + i;
if (!button->IsRange)
if (!buttons[i].IsRange)
{
int index = button->NotRange.DataIndex;
int index = buttons[i].NotRange.DataIndex;
if (index < 0 || index >= caps.NumberInputButtonCaps)
{
// Should never happen
@ -304,18 +490,16 @@ namespace OpenTK.Platform.Windows
continue;
}
HidProtocolData* d = (data + index);
if (d->DataIndex != index)
if (data[i].DataIndex != index)
{
// Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
d->DataIndex, index);
data[i].DataIndex, index);
continue;
}
bool value = d->On;
stick.SetButton(button->UsagePage, button->NotRange.Usage, value);
bool value = data[i].On;
stick.SetButton(buttons[i].UsagePage, buttons[i].NotRange.Usage, value);
}
else
{

View file

@ -231,7 +231,7 @@ namespace OpenTK.Platform.Windows
static string GetDeviceName(RawInputDeviceList dev)
{
uint size = 0;
int size = 0;
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size);
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size);

View file

@ -252,7 +252,7 @@ namespace OpenTK.Platform.Windows
static string GetDeviceName(RawInputDeviceList dev)
{
// get name size
uint size = 0;
int size = 0;
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
// get actual name