-- v2: ntoskrnl/tests: Add tests for IoReportTargetDeviceChange.
From: Vibhav Pant vibhavp@gmail.com
Instead of using the user-passed DEV_BROADCAST_* value to filter incoming notifications, simplify device_notification_details to precisely reflect filtering semantics, additionally making it a public definition in wine/dbt.h. --- dlls/sechost/service.c | 73 +++++++++++++++++++++++++++--------------- dlls/user32/input.c | 32 ++++++------------ include/Makefile.in | 1 + include/wine/dbt.h | 47 +++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 48 deletions(-) create mode 100644 include/wine/dbt.h
diff --git a/dlls/sechost/service.c b/dlls/sechost/service.c index 915b0b4ebe0..201b4947d4d 100644 --- a/dlls/sechost/service.c +++ b/dlls/sechost/service.c @@ -36,6 +36,8 @@ #include "svcctl.h" #include "plugplay.h"
+#include "wine/dbt.h" + WINE_DEFAULT_DEBUG_CHANNEL(service);
struct notify_data @@ -1973,17 +1975,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH StartServiceCtrlDispatcherW( const SERVICE_TABLE_E return service_run_main_thread(); }
-struct device_notification_details -{ - DWORD (CALLBACK *cb)(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header); - HANDLE handle; - union - { - DEV_BROADCAST_HDR header; - DEV_BROADCAST_DEVICEINTERFACE_W iface; - } filter; -}; - static HANDLE device_notify_thread; static struct list device_notify_list = LIST_INIT(device_notify_list);
@@ -1993,23 +1984,33 @@ struct device_notify_registration struct device_notification_details details; };
-static BOOL notification_filter_matches( DEV_BROADCAST_HDR *filter, DEV_BROADCAST_HDR *event ) +static BOOL notification_filter_matches( const struct device_notification_details *filter, const DEV_BROADCAST_HDR *event ) { - if (!filter->dbch_devicetype) return TRUE; - if (filter->dbch_devicetype != event->dbch_devicetype) return FALSE; + if (!filter->devicetype) return TRUE; + if (filter->devicetype != event->dbch_devicetype) return FALSE;
- if (filter->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + switch (filter->devicetype) { - DEV_BROADCAST_DEVICEINTERFACE_W *filter_iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)filter; - DEV_BROADCAST_DEVICEINTERFACE_W *event_iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)event; - if (filter_iface->dbcc_size == offsetof(DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_classguid)) return TRUE; - return IsEqualGUID( &filter_iface->dbcc_classguid, &event_iface->dbcc_classguid ); + case DBT_DEVTYP_DEVICEINTERFACE: + { + DEV_BROADCAST_DEVICEINTERFACE_W *event_iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)event; + if (filter->filter.deviceinterface.all_classes) return TRUE; + return IsEqualGUID( &filter->filter.deviceinterface.class, + &event_iface->dbcc_classguid ); + } + default: + FIXME( "Filter devicetype %lu not implemented \n", filter->devicetype ); }
- FIXME( "Filter dbch_devicetype %lu not implemented\n", filter->dbch_devicetype ); return TRUE; }
+struct device_notification_details_with_hdevnotify +{ + HDEVNOTIFY notify_handle; + struct device_notification_details details; +}; + static DWORD WINAPI device_notify_proc( void *arg ) { WCHAR endpoint[] = L"\pipe\wine_plugplay"; @@ -2017,7 +2018,7 @@ static DWORD WINAPI device_notify_proc( void *arg ) RPC_WSTR binding_str; DWORD err = ERROR_SUCCESS; struct device_notify_registration *registration; - struct device_notification_details *details_copy; + struct device_notification_details_with_hdevnotify *details_copy; unsigned int details_copy_nelems, details_copy_size; plugplay_rpc_handle handle = NULL; DWORD code = 0; @@ -2084,7 +2085,8 @@ static DWORD WINAPI device_notify_proc( void *arg ) EnterCriticalSection( &service_cs ); LIST_FOR_EACH_ENTRY(registration, &device_notify_list, struct device_notify_registration, entry) { - details_copy[i++] = registration->details; + details_copy[i].details = registration->details; + details_copy[i++].notify_handle = registration; details_copy_nelems++; if (i == details_copy_size) { @@ -2096,8 +2098,10 @@ static DWORD WINAPI device_notify_proc( void *arg )
for (i = 0; i < details_copy_nelems; i++) { - if (!notification_filter_matches( &details_copy[i].filter.header, (DEV_BROADCAST_HDR *)buf )) continue; - details_copy[i].cb( details_copy[i].handle, code, (DEV_BROADCAST_HDR *)buf ); + const struct device_notification_details *details = &details_copy[i].details; + + if (!notification_filter_matches( details, (DEV_BROADCAST_HDR *)buf )) continue; + details->cb( details->handle, code, (DEV_BROADCAST_HDR *)buf ); } MIDL_user_free(buf); } @@ -2116,6 +2120,24 @@ static DWORD WINAPI device_notify_proc( void *arg ) return 0; }
+static inline const char * +debugstr_device_notification_details( const struct device_notification_details *details ) +{ + if (!details) return "(null)"; + switch (details->devicetype) + { + case DBT_DEVTYP_DEVICEINTERFACE: + if (details->filter.deviceinterface.all_classes) + return wine_dbg_sprintf( "{%p %p DBT_DEVTYP_DEVICEINTERFACE {all_classes=1}}", + details->cb, details->handle ); + return wine_dbg_sprintf( "{%p %p DBT_DEVTYP_DEVICEINTERFACE {%s}}", details->cb, + details->handle, + debugstr_guid( &details->filter.deviceinterface.class ) ); + default: + return wine_dbg_sprintf( "{%p %p (unknown %#lx)}", details->cb, details->handle, details->devicetype); + } +} + /****************************************************************************** * I_ScRegisterDeviceNotification (sechost.@) */ @@ -2124,7 +2146,8 @@ HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification( struct device_notification_det { struct device_notify_registration *registration;
- TRACE("callback %p, handle %p, filter %p, flags %#lx\n", details->cb, details->handle, filter, flags); + TRACE( "details %s, filter %p, flags %#lx\n", debugstr_device_notification_details( details ), + filter, flags );
if (!(registration = malloc( sizeof(struct device_notify_registration) ))) { diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 108592f018f..f0d33cf7540 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -24,11 +24,15 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "ntstatus.h" +#define WIN32_NO_STATUS #include "user_private.h" #include "dbt.h" #include "wine/server.h" #include "wine/debug.h"
+#include "wine/dbt.h" + WINE_DEFAULT_DEBUG_CHANNEL(win); WINE_DECLARE_DEBUG_CHANNEL(keyboard);
@@ -530,10 +534,8 @@ static DWORD CALLBACK devnotify_window_callbackA(HANDLE handle, DWORD flags, DEV free( ifaceA ); return 0; } - default: FIXME( "unimplemented W to A mapping for %#lx\n", header->dbch_devicetype ); - /* fall through */ case DBT_DEVTYP_HANDLE: case DBT_DEVTYP_OEM: break; @@ -550,21 +552,6 @@ static DWORD CALLBACK devnotify_service_callback(HANDLE handle, DWORD flags, DEV return 0; }
-struct device_notification_details -{ - DWORD (CALLBACK *cb)(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header); - HANDLE handle; - union - { - DEV_BROADCAST_HDR header; - DEV_BROADCAST_DEVICEINTERFACE_W iface; - } filter; -}; - -extern HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification( struct device_notification_details *details, - void *filter, DWORD flags ); -extern BOOL WINAPI I_ScUnregisterDeviceNotification( HDEVNOTIFY handle ); - /*********************************************************************** * RegisterDeviceNotificationA (USER32.@) * @@ -580,7 +567,7 @@ HDEVNOTIFY WINAPI RegisterDeviceNotificationA( HANDLE handle, void *filter, DWOR */ HDEVNOTIFY WINAPI RegisterDeviceNotificationW( HANDLE handle, void *filter, DWORD flags ) { - struct device_notification_details details; + struct device_notification_details details = {0}; DEV_BROADCAST_HDR *header = filter;
TRACE("handle %p, filter %p, flags %#lx\n", handle, filter, flags); @@ -597,21 +584,20 @@ HDEVNOTIFY WINAPI RegisterDeviceNotificationW( HANDLE handle, void *filter, DWOR return NULL; }
- if (!header) details.filter.header.dbch_devicetype = 0; + if (!header) details.devicetype = 0; else if (header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { DEV_BROADCAST_DEVICEINTERFACE_W *iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)header; - details.filter.iface = *iface;
+ details.devicetype = DBT_DEVTYP_DEVICEINTERFACE; if (flags & DEVICE_NOTIFY_ALL_INTERFACE_CLASSES) - details.filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_classguid ); + details.filter.deviceinterface.all_classes = TRUE; else - details.filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name ); + details.filter.deviceinterface.class = iface->dbcc_classguid; } else if (header->dbch_devicetype == DBT_DEVTYP_HANDLE) { FIXME( "DBT_DEVTYP_HANDLE filter type not implemented\n" ); - details.filter.header.dbch_devicetype = 0; } else { diff --git a/include/Makefile.in b/include/Makefile.in index 000214484ed..cc35c12827c 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -901,6 +901,7 @@ SOURCES = \ wine/asm.h \ wine/atsvc.idl \ wine/condrv.h \ + wine/dbt.h \ wine/dcetypes.idl \ wine/debug.h \ wine/dplaysp.h \ diff --git a/include/wine/dbt.h b/include/wine/dbt.h new file mode 100644 index 00000000000..702fda17126 --- /dev/null +++ b/include/wine/dbt.h @@ -0,0 +1,47 @@ +/* + * Definitions for registering device notifications + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINE_DBT_H_ +#define __WINE_WINE_DBT_H_ + +#include <wine/debug.h> + +struct device_notification_details +{ + DWORD (CALLBACK *cb)(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header); + HANDLE handle; + DWORD devicetype; + union + { + struct + { + /* Used to implement DEVICE_NOTIFY_ALL_INTERFACE_CLASSES. If true, the class field below + * should be ignored. */ + BOOL all_classes; + GUID class; + } deviceinterface; + } filter; +}; + +extern HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification( struct device_notification_details *details, + void *filter, DWORD flags ); +extern BOOL WINAPI I_ScUnregisterDeviceNotification( HDEVNOTIFY handle ); + +#endif /* __WINE_WINE_DBT_H_ */
From: Vibhav Pant vibhavp@gmail.com
DBT_DEVTYP_HANDLE notifications are registered by providing the file path for the user-provided handle in device_notification_details. The device path is sent with dbch_reserved set to an offset in dbch_data pointing to the path for the device the notification originates from. --- dlls/sechost/service.c | 21 ++++++++++- dlls/user32/input.c | 75 ++++++++++++++++++++++++++++++++++++++-- include/wine/dbt.h | 8 +++++ programs/plugplay/main.c | 8 +++-- 4 files changed, 106 insertions(+), 6 deletions(-)
diff --git a/dlls/sechost/service.c b/dlls/sechost/service.c index 201b4947d4d..c5901d7e3e5 100644 --- a/dlls/sechost/service.c +++ b/dlls/sechost/service.c @@ -31,6 +31,7 @@
#include "wine/debug.h" #include "wine/exception.h" +#include "wine/heap.h" #include "wine/list.h"
#include "svcctl.h" @@ -1998,6 +1999,13 @@ static BOOL notification_filter_matches( const struct device_notification_detail return IsEqualGUID( &filter->filter.deviceinterface.class, &event_iface->dbcc_classguid ); } + case DBT_DEVTYP_HANDLE: + { + DEV_BROADCAST_HANDLE *event_handle = (DEV_BROADCAST_HANDLE *)event; + const WCHAR *device_path = (const WCHAR *)&event_handle->dbch_data[event_handle->dbch_reserved]; + + return !wcsicmp( device_path, filter->filter.device.name_info->Name.Buffer ); + } default: FIXME( "Filter devicetype %lu not implemented \n", filter->devicetype ); } @@ -2098,9 +2106,15 @@ static DWORD WINAPI device_notify_proc( void *arg )
for (i = 0; i < details_copy_nelems; i++) { - const struct device_notification_details *details = &details_copy[i].details; + struct device_notification_details *details = &details_copy[i].details;
if (!notification_filter_matches( details, (DEV_BROADCAST_HDR *)buf )) continue; + if (details->devicetype == DBT_DEVTYP_HANDLE) + { + DEV_BROADCAST_HANDLE *handle = (DEV_BROADCAST_HANDLE *)buf; + handle->dbch_handle = details->filter.device.device; + handle->dbch_hdevnotify = details_copy[i].notify_handle; + } details->cb( details->handle, code, (DEV_BROADCAST_HDR *)buf ); } MIDL_user_free(buf); @@ -2133,6 +2147,9 @@ debugstr_device_notification_details( const struct device_notification_details * return wine_dbg_sprintf( "{%p %p DBT_DEVTYP_DEVICEINTERFACE {%s}}", details->cb, details->handle, debugstr_guid( &details->filter.deviceinterface.class ) ); + case DBT_DEVTYP_HANDLE: + return wine_dbg_sprintf( "{%p %p DBT_DEVTYP_HANDLE {%p %p}}", details->cb, details->handle, + details->filter.device.name_info, details->filter.device.device ); default: return wine_dbg_sprintf( "{%p %p (unknown %#lx)}", details->cb, details->handle, details->devicetype); } @@ -2183,6 +2200,8 @@ BOOL WINAPI I_ScUnregisterDeviceNotification( HDEVNOTIFY handle ) EnterCriticalSection( &service_cs ); list_remove( ®istration->entry ); LeaveCriticalSection(&service_cs); + if (registration->details.devicetype == DBT_DEVTYP_HANDLE) + heap_free( registration->details.filter.device.name_info ); free( registration ); return TRUE; } diff --git a/dlls/user32/input.c b/dlls/user32/input.c index f0d33cf7540..3625219a079 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -30,6 +30,7 @@ #include "dbt.h" #include "wine/server.h" #include "wine/debug.h" +#include "wine/heap.h"
#include "wine/dbt.h"
@@ -534,11 +535,51 @@ static DWORD CALLBACK devnotify_window_callbackA(HANDLE handle, DWORD flags, DEV free( ifaceA ); return 0; } - default: - FIXME( "unimplemented W to A mapping for %#lx\n", header->dbch_devicetype ); + case DBT_DEVTYP_HANDLE: + { + const DEV_BROADCAST_HANDLE *handleW = (const DEV_BROADCAST_HANDLE *)header; + DEV_BROADCAST_HANDLE *handleA; + size_t lenA = 0; + size_t name_lenW = handleW->dbch_nameoffset == -1 + ? 0 + : wcslen( (WCHAR *)&handleW->dbch_data[handleW->dbch_nameoffset] ); + size_t data_size = handleW->dbch_nameoffset == -1 + ? handleW->dbch_reserved + : handleW->dbch_nameoffset + ( name_lenW * 3 + 1 ); + + handleA = malloc( offsetof( DEV_BROADCAST_HANDLE, dbch_data[data_size] )); + if (!handleA) + return 0; + + handleA->dbch_devicetype = DBT_DEVTYP_HANDLE; + if (handleW->dbch_nameoffset != -1) + { + lenA = WideCharToMultiByte( + CP_ACP, 0, (WCHAR *)&handleW->dbch_data[handleW->dbch_nameoffset], name_lenW + 1, + (CHAR *)&handleA->dbch_data[handleW->dbch_nameoffset], name_lenW * 3 + 1, NULL, + NULL ); + handleA->dbch_size = offsetof( DEV_BROADCAST_HANDLE, dbch_data[handleW->dbch_nameoffset + lenA + 1] ); + } + else + handleA->dbch_size = offsetof(DEV_BROADCAST_HANDLE, dbch_data[handleW->dbch_reserved]); + + handleA->dbch_reserved = 0; + handleA->dbch_handle = handleW->dbch_handle; + handleA->dbch_hdevnotify = handleW->dbch_hdevnotify; + handleA->dbch_eventguid = handleW->dbch_eventguid; + handleA->dbch_nameoffset = handleW->dbch_nameoffset; + memcpy( &handleA->dbch_data, &handleW->dbch_data, + handleW->dbch_nameoffset == -1 ? data_size : handleW->dbch_nameoffset ); + SendMessageTimeoutA( handle, WM_DEVICECHANGE, flags, (LPARAM)handleA, SMTO_ABORTIFHUNG, + 2000, NULL ); + free( handleA ); + return 0; + } case DBT_DEVTYP_OEM: break; + default: + FIXME( "unimplemented W to A mapping for %#lx\n", header->dbch_devicetype ); } }
@@ -597,7 +638,35 @@ HDEVNOTIFY WINAPI RegisterDeviceNotificationW( HANDLE handle, void *filter, DWOR } else if (header->dbch_devicetype == DBT_DEVTYP_HANDLE) { - FIXME( "DBT_DEVTYP_HANDLE filter type not implemented\n" ); + OBJECT_NAME_INFORMATION *name_info; + DEV_BROADCAST_HANDLE *handle = (DEV_BROADCAST_HANDLE *)header; + HANDLE device = handle->dbch_handle; + NTSTATUS status; + DWORD size; + + details.devicetype = DBT_DEVTYP_HANDLE; + status = NtQueryObject( device, ObjectNameInformation, NULL, 0, &size ); + if (status != STATUS_INFO_LENGTH_MISMATCH) + { + SetLastError( RtlNtStatusToDosError( status ) ); + return NULL; + } + name_info = heap_alloc( size ); + if (!name_info) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return NULL; + } + + status = NtQueryObject( device, ObjectNameInformation, name_info, size, 0 ); + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError( status )); + heap_free( name_info ); + return NULL; + } + details.filter.device.name_info = name_info; + details.filter.device.device = handle->dbch_handle; } else { diff --git a/include/wine/dbt.h b/include/wine/dbt.h index 702fda17126..b8a82a87b08 100644 --- a/include/wine/dbt.h +++ b/include/wine/dbt.h @@ -37,6 +37,14 @@ struct device_notification_details BOOL all_classes; GUID class; } deviceinterface; + struct + { + /* The path for the device file the notification is originating from. */ + OBJECT_NAME_INFORMATION *name_info; + /* HANDLE to the device the event originates from. Is passed to the callback as-is, even + * if the user has closed this handle. */ + HANDLE device; + } device; } filter; };
diff --git a/programs/plugplay/main.c b/programs/plugplay/main.c index 8426f2204a3..1242b01ba2a 100644 --- a/programs/plugplay/main.c +++ b/programs/plugplay/main.c @@ -141,9 +141,13 @@ void __cdecl plugplay_send_event( DWORD code, const BYTE *data, unsigned int siz { struct listener *listener; struct event *event; + const DEV_BROADCAST_HDR *header = (const DEV_BROADCAST_HDR *)data;
- BroadcastSystemMessageW( 0, NULL, WM_DEVICECHANGE, code, (LPARAM)data ); - BroadcastSystemMessageW( 0, NULL, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0 ); + if (header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + { + BroadcastSystemMessageW( 0, NULL, WM_DEVICECHANGE, code, (LPARAM)data ); + BroadcastSystemMessageW( 0, NULL, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0 ); + }
EnterCriticalSection( &plugplay_cs );
From: Vibhav Pant vibhavp@gmail.com
--- dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +- dlls/ntoskrnl.exe/pnp.c | 60 +++++++++++++++++++++++++++++ include/ddk/wdm.h | 10 +++++ 3 files changed, 71 insertions(+), 1 deletion(-)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index e9ad2aa7e74..690b4e166c3 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -465,7 +465,7 @@ @ stub IoReportHalResourceUsage @ stdcall IoReportResourceForDetection(ptr ptr long ptr ptr long ptr) @ stdcall IoReportResourceUsage(ptr ptr ptr long ptr ptr long long ptr) -@ stub IoReportTargetDeviceChange +@ stdcall IoReportTargetDeviceChange(ptr ptr) @ stub IoReportTargetDeviceChangeAsynchronous @ stub IoRequestDeviceEject @ stdcall IoReuseIrp(ptr long) diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index cbd5cf6ff2a..16837c9cd57 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -1001,6 +1001,66 @@ NTSTATUS WINAPI IoRegisterDeviceInterface(DEVICE_OBJECT *device, const GUID *cla return status; }
+/*********************************************************************** + * IoReportTargetDeviceChange (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI IoReportTargetDeviceChange( DEVICE_OBJECT *pdo, VOID *n ) +{ + OBJECT_NAME_INFORMATION *name_info; + DEV_BROADCAST_HANDLE *event_handle; + DWORD custom_buf_size, data_size; + TARGET_DEVICE_CUSTOM_NOTIFICATION *notification = n; + ULONG size; + NTSTATUS ret; + + TRACE( "(%p, %p)\n", pdo, n ); + + if (notification->Version != 1) + return STATUS_INVALID_PARAMETER; + + ret = ObQueryNameString( pdo, NULL, 0, &size ); + if (ret != STATUS_INFO_LENGTH_MISMATCH) + return ret; + + name_info = heap_alloc( size ); + if (!name_info) + return STATUS_NO_MEMORY; + + ret = ObQueryNameString( pdo, name_info, size, &size ); + if ( ret != STATUS_SUCCESS ) return ret; + + custom_buf_size = notification->Size - offsetof(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer) + 1; + data_size = custom_buf_size + name_info->Name.Length * sizeof( WCHAR ) + 1; + if (notification->NameBufferOffset != -1 && notification->CustomDataBuffer[custom_buf_size - 1] != '\0') + data_size++; + + event_handle = heap_alloc_zero( offsetof( DEV_BROADCAST_HANDLE, dbch_data[data_size] ) ); + if (!event_handle) + { + heap_free( name_info ); + return STATUS_NO_MEMORY; + } + event_handle->dbch_size = offsetof( DEV_BROADCAST_HANDLE, dbch_data[data_size] ); + event_handle->dbch_devicetype = DBT_DEVTYP_HANDLE; + event_handle->dbch_eventguid = notification->Event; + event_handle->dbch_nameoffset = notification->NameBufferOffset; + memcpy( event_handle->dbch_data, notification->CustomDataBuffer, custom_buf_size ); + if (notification->NameBufferOffset != -1 && notification->CustomDataBuffer[custom_buf_size - 1] != '\0') + notification->CustomDataBuffer[custom_buf_size++] = '\0'; + + memcpy( &event_handle->dbch_data[custom_buf_size], name_info->Name.Buffer, + name_info->Name.Length * sizeof( WCHAR ) + 1); + event_handle->dbch_reserved = custom_buf_size; + heap_free( name_info ); + + /* TODO: Note that this is not really synchronous. This will merely queue up an event in the + * plugplay service, which will be asynchronously broadcast to all listeners. */ + send_devicechange( DBT_CUSTOMEVENT, (BYTE *)event_handle, event_handle->dbch_size ); + heap_free( event_handle ); + + return STATUS_SUCCESS; +} + /*********************************************************************** * IoOpenDeviceRegistryKey (NTOSKRNL.EXE.@) */ diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index e0c21d0ccfd..23ac13018e7 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1613,6 +1613,15 @@ typedef enum _DIRECTORY_NOTIFY_INFORMATION_CLASS { DirectoryNotifyExtendedInformation } DIRECTORY_NOTIFY_INFORMATION_CLASS, *PDIRECTORY_NOTIFY_INFORMATION_CLASS;
+typedef struct _TARGET_DEVICE_CUSTOM_NOTIFICATION { + USHORT Version; + USHORT Size; + GUID Event; + FILE_OBJECT *FileObject; + LONG NameBufferOffset; + UCHAR CustomDataBuffer[1]; +} TARGET_DEVICE_CUSTOM_NOTIFICATION, *PTARGET_DEVICE_CUSTOM_NOTIFICATION; + typedef enum _WORK_QUEUE_TYPE { CriticalWorkQueue, DelayedWorkQueue, @@ -1781,6 +1790,7 @@ NTSTATUS WINAPI IoRegisterDeviceInterface(PDEVICE_OBJECT,const GUID*,PUNICODE_S void WINAPI IoReleaseCancelSpinLock(KIRQL); void WINAPI IoReleaseRemoveLockAndWaitEx(IO_REMOVE_LOCK*,void*,ULONG); void WINAPI IoReleaseRemoveLockEx(IO_REMOVE_LOCK*,void*,ULONG); +NTSTATUS WINAPI IoReportTargetDeviceChange(DEVICE_OBJECT *, VOID *); void WINAPI IoReuseIrp(IRP*,NTSTATUS); NTSTATUS WINAPI IoSetDeviceInterfaceState(UNICODE_STRING*,BOOLEAN); NTSTATUS WINAPI IoSetDevicePropertyData(DEVICE_OBJECT*,const DEVPROPKEY*,LCID,ULONG,DEVPROPTYPE,ULONG,void*);
From: Vibhav Pant vibhavp@gmail.com
--- dlls/ntoskrnl.exe/tests/driver.h | 38 ++++++++++++ dlls/ntoskrnl.exe/tests/driver_pnp.c | 43 ++++++++++++++ dlls/ntoskrnl.exe/tests/ntoskrnl.c | 86 ++++++++++++++++++++++++++-- 3 files changed, 162 insertions(+), 5 deletions(-)
diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver.h index 9c51af0a4c6..a3a3c622097 100644 --- a/dlls/ntoskrnl.exe/tests/driver.h +++ b/dlls/ntoskrnl.exe/tests/driver.h @@ -78,6 +78,44 @@ struct return_status_params
static const GUID control_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc0}};
+static const struct +{ + GUID eventguid; + BYTE *data; + SIZE_T data_size; + const WCHAR *str; + const CHAR *strA; +} custom_events[] = { + { + {0xdeadbeef, 0xdead, 0xbeef, { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0x01}}, + NULL, + 0, + NULL, + NULL, + }, + { + {0xdeadbeef, 0xdead, 0xbeef, { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0x02}}, + (BYTE *)(&custom_events[1].eventguid), + sizeof(GUID), + NULL, + NULL, + }, + { + {0xdeadbeef, 0xdead, 0xbeef, { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0x03}}, + (BYTE *)(&custom_events[2].eventguid), + sizeof(GUID), + L"Wine is not an emulator", + "Wine is not an emulator" + }, + { + {0xdeadbeef, 0xdead, 0xbeef, { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0x04}}, + NULL, + 0, + L"Wine is not an emulator", + "Wine is not an emulator" + } +}; + #define SERVER_LISTEN_PORT 9374 #define CLIENT_LISTEN_PORT 9375
diff --git a/dlls/ntoskrnl.exe/tests/driver_pnp.c b/dlls/ntoskrnl.exe/tests/driver_pnp.c index 70678b1a45c..b3e80c9f6b6 100644 --- a/dlls/ntoskrnl.exe/tests/driver_pnp.c +++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c @@ -686,6 +686,48 @@ static void test_device_properties( DEVICE_OBJECT *device ) return; }
+static void test_devicechange( DEVICE_OBJECT *obj ) +{ + INT i; + + for (i = 0; i < ARRAY_SIZE( custom_events ); i++) + { + TARGET_DEVICE_CUSTOM_NOTIFICATION *notif = NULL; + SIZE_T size = offsetof(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer) + 1; + NTSTATUS status; + + winetest_push_context( "custom_events %d", i + 1 ); + if (custom_events[i].data) + size += custom_events[i].data_size - 1; + + if (custom_events[i].str) + size += wcslen(custom_events[i].str) * sizeof(WCHAR) + (custom_events[i].data ? 1 : 0); + + notif = ExAllocatePool( PagedPool, size ); + ok( notif != NULL, "Failed to allocate memory for notification.\n" ); + notif->Version = 1; + notif->Size = size; + notif->Event = custom_events[i].eventguid; + notif->FileObject = NULL; + + if (custom_events[i].data) + memcpy(notif->CustomDataBuffer, custom_events[i].data, custom_events[i].data_size); + + if (custom_events[i].str) + { + notif->NameBufferOffset = custom_events[i].data_size; + wcscpy( (WCHAR *)¬if->CustomDataBuffer[notif->NameBufferOffset], custom_events[i].str ); + } + else + notif->NameBufferOffset = -1; + + status = IoReportTargetDeviceChange( obj, notif ); + ok( status == STATUS_SUCCESS, "IoReportTargetDeviceChange failed, status %#lx size %d,\n", status, notif->Size ); + ExFreePool( notif ); + winetest_pop_context(); + } +} + static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code) { switch (code) @@ -693,6 +735,7 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code) case IOCTL_WINETEST_BUS_MAIN: test_bus_query(); test_device_properties( bus_pdo ); + test_devicechange( bus_pdo ); return STATUS_SUCCESS;
case IOCTL_WINETEST_BUS_REGISTER_IFACE: diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 48deda7c455..9b6ac844994 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -1356,7 +1356,10 @@ static void add_file_to_catalog(HANDLE catalog, const WCHAR *file) static const GUID bus_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc1}}; static const GUID child_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc2}};
-static unsigned int got_bus_arrival, got_bus_removal, got_child_arrival, got_child_removal; +static unsigned int got_bus_arrival, got_bus_removal, got_child_arrival, got_child_removal, got_custom_event; + +static HDEVNOTIFY notify_handle_custom; +static HANDLE bus;
static LRESULT WINAPI device_notify_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) { @@ -1427,6 +1430,63 @@ static LRESULT WINAPI device_notify_proc(HWND window, UINT message, WPARAM wpara } break; } + + case DBT_CUSTOMEVENT: + { + const DEV_BROADCAST_HANDLE *handle = (const DEV_BROADCAST_HANDLE *)lparam; + INT i; + + if (handle->dbch_devicetype != DBT_DEVTYP_HANDLE) + break; + + if (winetest_debug > 1) trace("custom device event\n"); + + ok( handle->dbch_handle == bus, "Expected dbch_handle %p, got %p.\n", bus, handle->dbch_handle ); + + for (i = 0; i < ARRAY_SIZE( custom_events ); i++) + { + const GUID *guid = &custom_events[i].eventguid; + DWORD expect_size; + DWORD size = 0; + + if (!IsEqualGUID( &handle->dbch_eventguid, guid )) + continue; + + winetest_push_context( "custom_events %d", i + 1 ); + got_custom_event++; + + if (custom_events[i].data) size = custom_events[i].data_size; + if (custom_events[i].strA) size += strlen( custom_events[i].strA ); + + expect_size = offsetof( DEV_BROADCAST_HANDLE, dbch_data[size] ); + ok( handle->dbch_size >= expect_size, + "Unexpected dbch_size=%lu, expected atleast %lu.\n", handle->dbch_size, + expect_size ); + ok( handle->dbch_handle == bus, "Expected dbch_handle=%p, got %p.\n", bus, + handle->dbch_handle ); + ok( handle->dbch_hdevnotify == notify_handle_custom, + "Expected dbch_hdevnotify=%p, got %p.\n", notify_handle_custom, + handle->dbch_hdevnotify ); + + if (handle->dbch_size < expect_size) continue; + + if (custom_events[i].data) + ok( !memcmp( handle->dbch_data, custom_events[i].data, custom_events[i].data_size ), + "Unexpected dbch_data contents.\n"); + + if (custom_events[i].strA) + { + const CHAR *strA = (CHAR *)&handle->dbch_data[handle->dbch_nameoffset]; + + ok( handle->dbch_nameoffset != -1, "Expected dbch_nameoffset = %lu, got -1.\n", + handle->dbch_nameoffset ); + ok( !strcmp( strA, custom_events[i].strA ), "Expected %s, got %s\n", + debugstr_a( custom_events[i].strA ), debugstr_a( strA ) ); + } + winetest_pop_context(); + } + break; + } } return DefWindowProcA(window, message, wparam, lparam); } @@ -1456,11 +1516,16 @@ static void test_pnp_devices(void) SP_DEVICE_INTERFACE_DETAIL_DATA_A *iface_detail = (void *)buffer; SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; SP_DEVINFO_DATA device = {sizeof(device)}; - DEV_BROADCAST_DEVICEINTERFACE_A filter = + DEV_BROADCAST_DEVICEINTERFACE_A filter_iface = { - .dbcc_size = sizeof(filter), + .dbcc_size = sizeof(filter_iface), .dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE, }; + DEV_BROADCAST_HANDLE filter_handle = + { + .dbch_size = sizeof(filter_handle), + .dbch_devicetype = DBT_DEVTYP_HANDLE, + }; static const WNDCLASSA class = { .lpszClassName = "ntoskrnl_test_wc", @@ -1468,7 +1533,7 @@ static void test_pnp_devices(void) }; HDEVNOTIFY notify_handle; DWORD size, type, dword; - HANDLE bus, child, tmp; + HANDLE child, tmp; OBJECT_ATTRIBUTES attr; UNICODE_STRING string; OVERLAPPED ovl = {0}; @@ -1477,12 +1542,13 @@ static void test_pnp_devices(void) HWND window; BOOL ret; int id; + INT i;
ret = RegisterClassA(&class); ok(ret, "failed to register class\n"); window = CreateWindowA("ntoskrnl_test_wc", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); ok(!!window, "failed to create window\n"); - notify_handle = RegisterDeviceNotificationA(window, &filter, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); + notify_handle = RegisterDeviceNotificationA(window, &filter_iface, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); ok(!!notify_handle, "failed to register window, error %lu\n", GetLastError());
set = SetupDiGetClassDevsA(&control_class, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); @@ -1513,8 +1579,17 @@ static void test_pnp_devices(void) bus = CreateFileA(iface_detail->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, NULL); ok(bus != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError());
+ filter_handle.dbch_handle = bus; + notify_handle_custom = RegisterDeviceNotificationA(window, &filter_handle, 0); + ok(!!notify_handle_custom, "failed to register for device notifications, error %lu\n", GetLastError()); + ret = DeviceIoControl(bus, IOCTL_WINETEST_BUS_MAIN, NULL, 0, NULL, 0, &size, NULL); ok(ret, "got error %lu\n", GetLastError()); + for (i = 0; i < ARRAY_SIZE(custom_events); i++) + pump_messages(); + ok(got_custom_event == ARRAY_SIZE(custom_events), + "got %u custom event messages, expected %d\n", got_custom_event, + (int)ARRAY_SIZE(custom_events));
/* Test IoRegisterDeviceInterface() and IoSetDeviceInterfaceState(). */
@@ -1734,6 +1809,7 @@ static void test_pnp_devices(void) CloseHandle(bus);
UnregisterDeviceNotification(notify_handle); + UnregisterDeviceNotification(notify_handle_custom); DestroyWindow(window); UnregisterClassA("ntoskrnl_test_wc", GetModuleHandleA(NULL)); }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148170
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w7u_adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w7u_el (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w8adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w864 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w1064v1507 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w1064v1809 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w1064_tsign (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w10pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w10pro64_en_AE_u8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w11pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w7pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w864 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w1064v1507 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w1064v1809 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w1064_2qxl (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w1064_adm (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w1064_tsign (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w10pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w10pro64_ar (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w10pro64_ja (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w10pro64_zh_CN (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w11pro64_amd (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver' does not exist.
=== w7u_2qxl (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w7u_adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w7u_el (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w8adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w864 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w1064v1507 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w1064v1809 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w1064_tsign (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w10pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w10pro64_en_AE_u8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w11pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w7pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w864 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w1064v1507 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w1064v1809 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w1064_2qxl (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w1064_adm (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w1064_tsign (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w10pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w10pro64_ar (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w10pro64_ja (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w10pro64_zh_CN (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w11pro64_amd (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver2' does not exist.
=== w7u_2qxl (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w7u_adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w7u_el (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w8adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w864 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w1064v1507 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w1064v1809 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w1064_tsign (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w10pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w10pro64_en_AE_u8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w11pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w7pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w864 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w1064v1507 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w1064v1809 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w1064_2qxl (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w1064_adm (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w1064_tsign (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w10pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w10pro64_ar (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w10pro64_ja (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w10pro64_zh_CN (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w11pro64_amd (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver3' does not exist.
=== w7u_2qxl (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w7u_adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w7u_el (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w8adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w864 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w1064v1507 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w1064v1809 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w1064_tsign (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w10pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w10pro64_en_AE_u8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w11pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w7pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w864 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w1064v1507 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w1064v1809 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w1064_2qxl (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w1064_adm (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w1064_tsign (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w10pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w10pro64_ar (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w10pro64_ja (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w10pro64_zh_CN (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w11pro64_amd (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_netio' does not exist.
=== w7u_2qxl (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w7u_adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w7u_el (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w8adm (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w864 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w1064v1507 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w1064v1809 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w1064_tsign (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w10pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w10pro64_en_AE_u8 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w11pro64 (32 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w7pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w864 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w1064v1507 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w1064v1809 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w1064_2qxl (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w1064_adm (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w1064_tsign (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w10pro64 (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w10pro64_ar (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w10pro64_ja (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w10pro64_zh_CN (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w11pro64_amd (64 bit report) ===
ntoskrnl.exe: Fatal: test 'driver_pnp' does not exist.
=== w7u_2qxl (32 bit report) ===
ntoskrnl.exe: ntoskrnl.c:1584: Test failed: failed to register for device notifications, error 1066 ntoskrnl.c:1590: Test failed: got 0 custom event messages, expected 4 ntoskrnl.c:1719: Test failed: got size 488 ntoskrnl.c:1720: Test failed: got hardware IDs "\x05\x00\x00\x00\\x00w\x00\x04\x00\x00\x00e\x00t\x00e\x00s\x00\x04\x00\x00\x00\x9c\xed-\x00\x00\x00\x00\x00R\x00O\x00O\x00T\x00\\x00W\x00I\x00N\x00E\x00T\x00E\x00S\x00T\x00\\x000\x00\x00\x00i\x00n\x00e\x00t\x00e\x00s\x00t\x00.\x00i\x00n\x00f\x00\x00\x00\x00\x00\x00\x00\xcaj\x13u\xe4\x03\xdc"... ntoskrnl.c:1727: Test failed: got size 488 ntoskrnl.c:1728: Test failed: got container ID L"" ntoskrnl.c:1734: Test failed: got size 488 ntoskrnl.c:1735: Test failed: got compatible IDs "\x05\x00\x00\x00\\x00w\x00\x04\x00\x00\x00e\x00t\x00e\x00s\x00\x04\x00\x00\x00\x9c\xed-\x00\x00\x00\x00\x00R\x00O\x00O\x00T\x00\\x00W\x00I\x00N\x00E\x00T\x00E\x00S\x00T\x00\\x000\x00\x00\x00i\x00n\x00e\x00t\x00e\x00s\x00t\x00.\x00i\x00n\x00f\x00\x00\x00\x00\x00\x00\x00\xcaj\x13u\xe4\x03\xdc"... ntoskrnl.c:1769: Test failed: got size 488 ntoskrnl.c:1779: Test failed: got size 488 ntoskrnl.c:1798: Test failed: got size 488
=== w7u_el (32 bit report) ===
ntoskrnl.exe: ntoskrnl.c:1584: Test failed: failed to register for device notifications, error 1066 ntoskrnl.c:1590: Test failed: got 0 custom event messages, expected 4 ntoskrnl.c:1719: Test failed: got size 492 ntoskrnl.c:1720: Test failed: got hardware IDs "\x05\x00\x00\x00\\x00w\x00\x04\x00\x00\x00e\x00t\x00e\x00s\x00\x04\x00\x00\x008\xef<\x00\x00\x00\x00\x00R\x00O\x00O\x00T\x00\\x00W\x00I\x00N\x00E\x00T\x00E\x00S\x00T\x00\\x000\x00\x00\x00i\x00n\x00e\x00t\x00e\x00s\x00t\x00.\x00i\x00n\x00f\x00\x00\x00\x00\x00\x00\x00\xcaj\x0eu\xc3\xf4\xe3"... ntoskrnl.c:1727: Test failed: got size 492 ntoskrnl.c:1728: Test failed: got container ID L"" ntoskrnl.c:1734: Test failed: got size 492 ntoskrnl.c:1735: Test failed: got compatible IDs "\x05\x00\x00\x00\\x00w\x00\x04\x00\x00\x00e\x00t\x00e\x00s\x00\x04\x00\x00\x008\xef<\x00\x00\x00\x00\x00R\x00O\x00O\x00T\x00\\x00W\x00I\x00N\x00E\x00T\x00E\x00S\x00T\x00\\x000\x00\x00\x00i\x00n\x00e\x00t\x00e\x00s\x00t\x00.\x00i\x00n\x00f\x00\x00\x00\x00\x00\x00\x00\xcaj\x0eu\xc3\xf4\xe3"... ntoskrnl.c:1769: Test failed: got size 492 ntoskrnl.c:1779: Test failed: got size 492 ntoskrnl.c:1798: Test failed: got size 492
=== w8 (testbot log) ===
WineRunTask.pl:error: An error occurred while waiting for the test to complete: the 3156 process does not exist or is not a child process WineRunTask.pl:error: The test VM has crashed, rebooted or lost connectivity (or the TestAgent server died)
=== w1064_tsign (testbot log) ===
WineRunTask.pl:error: An error occurred while waiting for the test to complete: network read got a premature EOF (wait2/connect:AgentVersion.h:0/9) WineRunTask.pl:error: The test VM has crashed, rebooted or lost connectivity (or the TestAgent server died)
=== debian11b (64 bit WoW report) ===
ddraw: ddraw2.c:3814: Test failed: Expected (0,0)-(640,480), got (0,0)-(1024,768). ddraw2.c:3839: Test failed: Expected (0,0)-(640,480), got (0,0)-(1024,768).
user32: input.c:4305: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 00000000007D00E4, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
Rémi Bernon (@rbernon) commented about dlls/user32/input.c:
NULL );
handleA->dbch_size = offsetof( DEV_BROADCAST_HANDLE, dbch_data[handleW->dbch_nameoffset + lenA + 1] );
}
else
handleA->dbch_size = offsetof(DEV_BROADCAST_HANDLE, dbch_data[handleW->dbch_reserved]);
handleA->dbch_reserved = 0;
handleA->dbch_handle = handleW->dbch_handle;
handleA->dbch_hdevnotify = handleW->dbch_hdevnotify;
handleA->dbch_eventguid = handleW->dbch_eventguid;
handleA->dbch_nameoffset = handleW->dbch_nameoffset;
memcpy( &handleA->dbch_data, &handleW->dbch_data,
handleW->dbch_nameoffset == -1 ? data_size : handleW->dbch_nameoffset );
SendMessageTimeoutA( handle, WM_DEVICECHANGE, flags, (LPARAM)handleA, SMTO_ABORTIFHUNG,
2000, NULL );
free( handleA );
This looks more complicated that I would expect, can't we simply convert from dbch_nameoffset to dbch_size?. Is this because you are using dbch_reserved, why do you care about it? Where do these -1 values come from?
I still don't like the "simplification" much, sorry. What about something like https://gitlab.winehq.org/rbernon/wine/-/commits/wip/devnotify-simplify?
It filters the notification early, makes full copies of them to be called later, which makes the `device_notify_create_event` helper an appropriate place for client-specific fixups (like setting dbch_handle and dbch_hdevnotify).
The device paths are also optionally passed as an explicit parameter to the plugplay interface, which is probably better than shoving it into the events.
Then, I think you should also reorder the changes to put the tests first if possible, with some todo_wine, then the implementation of IoReportTargetDeviceChange which calls into plugplay but doesn't do anything else, and last the support for DBT_DEVTYP_HANDLE (assuming it's not making things outrageously complicated to handle in the tests).
The refactoring can come first or after, doesn't matter much.
On Tue Sep 3 15:15:10 2024 +0000, Rémi Bernon wrote:
I still don't like the "simplification" much, sorry. What about something like https://gitlab.winehq.org/rbernon/wine/-/commits/wip/devnotify-simplify? It filters the notification early, makes full copies of them to be called later, which makes the `device_notify_create_event` helper an appropriate place for client-specific fixups (like setting dbch_handle and dbch_hdevnotify). The device paths are also optionally passed as an explicit parameter to the plugplay interface, which is probably better than shoving it into the events. Then, I think you should also reorder the changes to put the tests first if possible, with some todo_wine, then the implementation of IoReportTargetDeviceChange which calls into plugplay but doesn't do anything else, and last the support for DBT_DEVTYP_HANDLE (assuming it's not making things outrageously complicated to handle in the tests). The refactoring can come first or after, doesn't matter much.
Hm, that is simpler, but would it make sense to generalize the `path` parameter in `plugplay_{send|get}_event` to a `BYTE *oob_data, SIZE_T *oob_data_size` instead? That way, the new parameters could be used to send additional data similar to `path` if the need ever arises in the future, plus it also makes the parameters more understandable.
On Tue Sep 3 15:15:10 2024 +0000, Vibhav Pant wrote:
Hm, that is simpler, but would it make sense to generalize the `path` parameter in `plugplay_{send|get}_event` to a `BYTE *oob_data, SIZE_T *oob_data_size` instead? That way, the new parameters could be used to send additional data similar to `path` if the need ever arises in the future, plus it also makes the parameters more understandable.
Well I think it can be changed later, if the need arises? You can rename the path to "device_path" if you prefer.
On Tue Sep 3 16:15:34 2024 +0000, Rémi Bernon wrote:
Well I think it can be changed later, if the need arises? You can rename the path to "device_path" if you prefer.
Note also that I didn't really test my changes, I can see already that there are bugs (event_create has some), and one might be that NULL paths maybe aren't allowed in RPC? Hopefully it should give you the idea.
Elizabeth Figura (@zfigura) commented about dlls/ntoskrnl.exe/pnp.c:
- if (notification->NameBufferOffset != -1 && notification->CustomDataBuffer[custom_buf_size - 1] != '\0')
data_size++;
- event_handle = heap_alloc_zero( offsetof( DEV_BROADCAST_HANDLE, dbch_data[data_size] ) );
- if (!event_handle)
- {
heap_free( name_info );
return STATUS_NO_MEMORY;
- }
- event_handle->dbch_size = offsetof( DEV_BROADCAST_HANDLE, dbch_data[data_size] );
- event_handle->dbch_devicetype = DBT_DEVTYP_HANDLE;
- event_handle->dbch_eventguid = notification->Event;
- event_handle->dbch_nameoffset = notification->NameBufferOffset;
- memcpy( event_handle->dbch_data, notification->CustomDataBuffer, custom_buf_size );
- if (notification->NameBufferOffset != -1 && notification->CustomDataBuffer[custom_buf_size - 1] != '\0')
notification->CustomDataBuffer[custom_buf_size++] = '\0';
I think you meant to modify dbch_data here?
Elizabeth Figura (@zfigura) commented about dlls/ntoskrnl.exe/pnp.c:
- }
- event_handle->dbch_size = offsetof( DEV_BROADCAST_HANDLE, dbch_data[data_size] );
- event_handle->dbch_devicetype = DBT_DEVTYP_HANDLE;
- event_handle->dbch_eventguid = notification->Event;
- event_handle->dbch_nameoffset = notification->NameBufferOffset;
- memcpy( event_handle->dbch_data, notification->CustomDataBuffer, custom_buf_size );
- if (notification->NameBufferOffset != -1 && notification->CustomDataBuffer[custom_buf_size - 1] != '\0')
notification->CustomDataBuffer[custom_buf_size++] = '\0';
- memcpy( &event_handle->dbch_data[custom_buf_size], name_info->Name.Buffer,
name_info->Name.Length * sizeof( WCHAR ) + 1);
- event_handle->dbch_reserved = custom_buf_size;
- heap_free( name_info );
- /* TODO: Note that this is not really synchronous. This will merely queue up an event in the
* plugplay service, which will be asynchronously broadcast to all listeners. */
Hmm. I know where this TODO is coming from, but is it really meaningful right now? The documentation suggests that the sync/async difference is meaningful for kernel-level listeners [i.e. IoRegisterPlugPlayNotification()], but the test suggests that the window message is sent asynchronously to userspace even for the synchronous function.
Elizabeth Figura (@zfigura) commented about dlls/ntoskrnl.exe/tests/ntoskrnl.c:
if (handle->dbch_size < expect_size) continue;
if (custom_events[i].data)
ok( !memcmp( handle->dbch_data, custom_events[i].data, custom_events[i].data_size ),
"Unexpected dbch_data contents.\n");
if (custom_events[i].strA)
{
const CHAR *strA = (CHAR *)&handle->dbch_data[handle->dbch_nameoffset];
ok( handle->dbch_nameoffset != -1, "Expected dbch_nameoffset = %lu, got -1.\n",
handle->dbch_nameoffset );
ok( !strcmp( strA, custom_events[i].strA ), "Expected %s, got %s\n",
debugstr_a( custom_events[i].strA ), debugstr_a( strA ) );
}
What's dbch_nameoffset otherwise? Might as well test that while you're here.