[PATCH v4 0/1] MR10301: hidclass.sys: Report correct device type for HID device objects.
HID child PDOs were created with DeviceType 0 and no characteristics, causing GetFileType() to return FILE_TYPE_DISK instead of FILE_TYPE_UNKNOWN. This broke .NET applications (e.g. ETS6) that use FileStream on HID device handles, as FileStream incorrectly treats FILE_TYPE_DISK handles as seekable files. Set DeviceType to FILE_DEVICE_UNKNOWN and Characteristics to FILE_AUTOGENERATED_DEVICE_NAME to match Windows behavior, and add an IRP_MJ_QUERY_VOLUME_INFORMATION handler to return these values. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59512 -- v4: hidclass.sys: Report correct device type for HID device objects. https://gitlab.winehq.org/wine/wine/-/merge_requests/10301
From: Robert Gerigk <Robert-Gerigk@online.de> HID child PDOs were created with DeviceType 0 and no characteristics, causing GetFileType() to return FILE_TYPE_DISK instead of FILE_TYPE_UNKNOWN. This broke .NET applications (e.g. ETS6) that use FileStream on HID device handles, as FileStream incorrectly treats FILE_TYPE_DISK handles as seekable files. Set DeviceType to FILE_DEVICE_UNKNOWN to match Windows behavior, and add an IRP_MJ_QUERY_VOLUME_INFORMATION handler to return the device type. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59512 --- dlls/hid/tests/device.c | 22 ++++++++++++++++++++++ dlls/hidclass.sys/pnp.c | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/dlls/hid/tests/device.c b/dlls/hid/tests/device.c index 8b5ff7a0fe7..61e9a2f38a3 100644 --- a/dlls/hid/tests/device.c +++ b/dlls/hid/tests/device.c @@ -24,6 +24,9 @@ #include "hidusage.h" #include "ddk/hidsdi.h" +#include "winternl.h" +#include "winioctl.h" + #include "wine/test.h" #define READ_MAX_TIME 5000 @@ -458,9 +461,28 @@ static void test_get_input_report(void) HeapFree(GetProcessHeap(), 0, report); } +static void test_device_file_type(HANDLE device) +{ + FILE_FS_DEVICE_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD file_type; + + file_type = GetFileType(device); + todo_wine + ok(file_type == FILE_TYPE_UNKNOWN, "Expected FILE_TYPE_UNKNOWN, got %lu\n", file_type); + + memset(&info, 0xcc, sizeof(info)); + memset(&io, 0xcc, sizeof(io)); + status = NtQueryVolumeInformationFile(device, &io, &info, sizeof(info), FileFsDeviceInformation); + ok(status == STATUS_SUCCESS, "NtQueryVolumeInformationFile failed: %lx\n", status); + ok(info.DeviceType == FILE_DEVICE_UNKNOWN, "Expected FILE_DEVICE_UNKNOWN (0x22), got %#lx\n", info.DeviceType); +} + START_TEST(device) { run_for_each_device(test_device_info); + run_for_each_device(test_device_file_type); test_read_device(); test_get_input_report(); } diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c index 4774ba2b9fc..84e4736de76 100644 --- a/dlls/hidclass.sys/pnp.c +++ b/dlls/hidclass.sys/pnp.c @@ -276,7 +276,8 @@ static NTSTATUS create_child_pdos( minidriver *minidriver, DEVICE_OBJECT *device fdo->base.hid.PhysicalDeviceObject ); RtlInitUnicodeString(&string, pdo_name); - if ((status = IoCreateDevice( device->DriverObject, sizeof(*pdo), &string, 0, 0, FALSE, &child_device ))) + if ((status = IoCreateDevice( device->DriverObject, sizeof(*pdo), &string, + FILE_DEVICE_UNKNOWN, 0, FALSE, &child_device ))) { ERR( "Failed to create child PDO, status %#lx.\n", status ); return status; @@ -666,6 +667,37 @@ static NTSTATUS WINAPI driver_close(DEVICE_OBJECT *device, IRP *irp) return pdo_close(device, irp); } +static NTSTATUS WINAPI driver_query_volume(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + NTSTATUS status; + + if (irpsp->Parameters.QueryVolume.FsInformationClass == FileFsDeviceInformation) + { + if (irpsp->Parameters.QueryVolume.Length < sizeof(FILE_FS_DEVICE_INFORMATION)) + { + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + FILE_FS_DEVICE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer; + info->DeviceType = device->DeviceType; + info->Characteristics = device->Characteristics; + irp->IoStatus.Information = sizeof(*info); + status = STATUS_SUCCESS; + } + } + else + { + FIXME("unhandled volume info class %x\n", irpsp->Parameters.QueryVolume.FsInformationClass); + status = STATUS_NOT_IMPLEMENTED; + } + + irp->IoStatus.Status = status; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return status; +} + static NTSTATUS WINAPI driver_ioctl(DEVICE_OBJECT *device, IRP *irp) { return pdo_ioctl(device, irp); @@ -715,6 +747,7 @@ NTSTATUS WINAPI HidRegisterMinidriver(HID_MINIDRIVER_REGISTRATION *registration) registration->DriverObject->MajorFunction[IRP_MJ_WRITE] = driver_write; registration->DriverObject->MajorFunction[IRP_MJ_CREATE] = driver_create; registration->DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver_close; + registration->DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = driver_query_volume; driver->PNPDispatch = registration->DriverObject->MajorFunction[IRP_MJ_PNP]; registration->DriverObject->MajorFunction[IRP_MJ_PNP] = driver_pnp; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10301
On Wed Mar 11 16:21:26 2026 +0000, Jan Robert Gerigk wrote:
changed this line in [version 4 of the diff](/wine/wine/-/merge_requests/10301/diffs?diff_id=250142&start_sha=decb44ef925a32efc52c62324f367abd0fc3d342#e556a32110a66405c3d4b3bb481961aafce44922_280_280) The test Jan added explicitly shows that FILE_AUTOGENERATED_DEVICE_NAME should be used. In that case we should instead get rid of the string.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10301#note_131862
On Wed Mar 11 16:45:13 2026 +0000, Elizabeth Figura wrote:
The test Jan added explicitly shows that FILE_AUTOGENERATED_DEVICE_NAME should be used. In that case we should instead get rid of the string. Yes, either one should work.
With FILE_AUTOGENERATED_DEVICE_NAME, the name becomes \Device\xxxx. When using the string, the name is \Device\HID#. I'm not sure if changing the name will cause any issues. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10301#note_132044
On Thu Mar 12 15:13:52 2026 +0000, Maotong Zhang wrote:
Yes, either one should work. With FILE_AUTOGENERATED_DEVICE_NAME, the name becomes \Device\xxxx. When using the string, the name is \Device\HID#. I'm not sure if changing the name will cause any issues. Any reason this was resolved by removing FILE_AUTOGENERATED_DEVICE_NAME and the matching test?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10301#note_132735
@rbernon I followed @xiaotong's suggestion to keep the string parameter and set Characteristics back to 0, since the device name is already provided explicitly via the String. This avoids changing the device name from \\Device\\HID# to \\Device\\xxxx wich could potentially affect other code that relies on the current naming scheme. I am really not sure in this point. If you prefer the other approach (FILE_AUTOGENERATED_DEVICE_NAME removing the string), I am happy to change it. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10301#note_132746
I don't really mind, assuming that part wasn't necessary to fix the bug, but if the name is autogenerated on Windows, it shouldn't break anything to do the same. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10301#note_132751
This merge request was approved by Rémi Bernon. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10301
On Thu Mar 19 11:26:38 2026 +0000, Rémi Bernon wrote:
I don't really mind, assuming that part wasn't necessary to fix the bug, but if the name is autogenerated on Windows, it shouldn't break anything to do the same. I checked again on Windows, HID PDO names are autogenerated (\\Device\\00000089, \\Device\\00000081, etc.), not string-based. So FILE_AUTOGENERATED_DEVICE_NAME is the correct approach.
I will update the patch to use FILE_AUTOGENERATED_DEVICE_NAME and remove the string parameter. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10301#note_132763
participants (5)
-
Elizabeth Figura (@zfigura) -
Jan Robert Gerigk (@RgSg86) -
Maotong Zhang (@xiaotong) -
Robert Gerigk -
Rémi Bernon