Based on a patch by Micah N Gorrell.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Instead of implementing the interface in services.exe, I've elected to use a separate RPC interface hosted by plugplay.exe, for the following reasons:
* device notifications don't have a lot to do with services; * the svcctl interface is already a (somewhat) documented interface which does not include any functions related to device notification; * it probably makes sense to move the BroadcastSystemMessage() call out of ntoskrnl and into the RPCserver [as in patch 0004], and services.exe can't import user32.
dlls/sechost/Makefile.in | 1 + dlls/sechost/plugplay.idl | 2 + dlls/sechost/sechost.spec | 4 +- dlls/sechost/service.c | 148 +++++++++++++++++++++++++++++++++ include/wine/plugplay.idl | 32 +++++++ programs/plugplay/Makefile.in | 5 +- programs/plugplay/main.c | 130 +++++++++++++++++++++++++++++ programs/plugplay/plugplay.idl | 2 + 8 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 dlls/sechost/plugplay.idl create mode 100644 include/wine/plugplay.idl create mode 100644 programs/plugplay/plugplay.idl
diff --git a/dlls/sechost/Makefile.in b/dlls/sechost/Makefile.in index 91016c67577..f4d88272857 100644 --- a/dlls/sechost/Makefile.in +++ b/dlls/sechost/Makefile.in @@ -11,4 +11,5 @@ C_SRCS = \ trace.c
IDL_SRCS = \ + plugplay.idl \ svcctl.idl diff --git a/dlls/sechost/plugplay.idl b/dlls/sechost/plugplay.idl new file mode 100644 index 00000000000..05e040388e4 --- /dev/null +++ b/dlls/sechost/plugplay.idl @@ -0,0 +1,2 @@ +#pragma makedep client +#include "wine/plugplay.idl" diff --git a/dlls/sechost/sechost.spec b/dlls/sechost/sechost.spec index f21e2f1cda8..0867412c1cb 100644 --- a/dlls/sechost/sechost.spec +++ b/dlls/sechost/sechost.spec @@ -108,14 +108,14 @@ @ stub I_ScIsSecurityProcess @ stub I_ScPnPGetServiceName @ stub I_ScQueryServiceConfig -@ stub I_ScRegisterDeviceNotification +@ stdcall I_ScRegisterDeviceNotification(ptr ptr long) @ stub I_ScRegisterPreshutdownRestart @ stub I_ScReparseServiceDatabase @ stub I_ScRpcBindA @ stub I_ScRpcBindW @ stub I_ScSendPnPMessage @ stub I_ScSendTSMessage -@ stub I_ScUnregisterDeviceNotification +@ stdcall I_ScUnregisterDeviceNotification(ptr) @ stub I_ScValidatePnPService @ stub LocalGetConditionForString @ stub LocalGetReferencedTokenTypesForCondition diff --git a/dlls/sechost/service.c b/dlls/sechost/service.c index 7a4211631e2..924a6c9264d 100644 --- a/dlls/sechost/service.c +++ b/dlls/sechost/service.c @@ -26,6 +26,8 @@ #include "winbase.h" #include "winsvc.h" #include "winternl.h" +#include "winuser.h" +#include "dbt.h"
#include "wine/debug.h" #include "wine/exception.h" @@ -33,6 +35,7 @@ #include "wine/list.h"
#include "svcctl.h" +#include "plugplay.h"
WINE_DEFAULT_DEBUG_CHANNEL(service);
@@ -1967,3 +1970,148 @@ 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; +}; + +static HANDLE device_notify_thread; +static struct list device_notify_list = LIST_INIT(device_notify_list); + +struct device_notify_registration +{ + struct list entry; + struct device_notification_details details; +}; + +static DWORD WINAPI device_notify_proc( void *arg ) +{ + WCHAR endpoint[] = L"\pipe\wine_plugplay"; + WCHAR protseq[] = L"ncalrpc"; + RPC_WSTR binding_str; + DWORD err = ERROR_SUCCESS; + struct device_notify_registration *registration; + plugplay_rpc_handle handle = NULL; + DWORD code = 0; + unsigned int size; + BYTE *buf; + + if ((err = RpcStringBindingComposeW( NULL, protseq, NULL, endpoint, NULL, &binding_str ))) + { + ERR("RpcStringBindingCompose() failed, error %#x\n", err); + return err; + } + err = RpcBindingFromStringBindingW( binding_str, &plugplay_binding_handle ); + RpcStringFreeW( &binding_str ); + if (err) + { + ERR("RpcBindingFromStringBinding() failed, error %#x\n", err); + return err; + } + + __TRY + { + handle = plugplay_register_listener(); + } + __EXCEPT(rpc_filter) + { + err = map_exception_code( GetExceptionCode() ); + } + __ENDTRY + + if (!handle) + { + ERR("failed to open RPC handle, error %u\n", err); + return 1; + } + + for (;;) + { + buf = NULL; + __TRY + { + code = plugplay_get_event( handle, &buf, &size ); + err = ERROR_SUCCESS; + } + __EXCEPT(rpc_filter) + { + err = map_exception_code( GetExceptionCode() ); + } + __ENDTRY + + if (err) + { + ERR("failed to get event, error %u\n", err); + break; + } + + EnterCriticalSection( &service_cs ); + LIST_FOR_EACH_ENTRY(registration, &device_notify_list, struct device_notify_registration, entry) + { + registration->details.cb( registration->details.handle, code, (DEV_BROADCAST_HDR *)buf ); + } + LeaveCriticalSection(&service_cs); + MIDL_user_free(buf); + } + + __TRY + { + plugplay_unregister_listener( handle ); + } + __EXCEPT(rpc_filter) + { + } + __ENDTRY + + RpcBindingFree( &plugplay_binding_handle ); + return 0; +} + +/****************************************************************************** + * I_ScRegisterDeviceNotification (sechost.@) + */ +HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification( struct device_notification_details *details, + void *filter, DWORD flags ) +{ + struct device_notify_registration *registration; + + TRACE("callback %p, handle %p, filter %p, flags %#x\n", details->cb, details->handle, filter, flags); + + if (filter) FIXME("Notification filters are not yet implemented.\n"); + + if (!(registration = heap_alloc(sizeof(struct device_notify_registration)))) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + registration->details = *details; + + EnterCriticalSection( &service_cs ); + list_add_tail( &device_notify_list, ®istration->entry ); + + if (!device_notify_thread) + device_notify_thread = CreateThread( NULL, 0, device_notify_proc, NULL, 0, NULL ); + + LeaveCriticalSection( &service_cs ); + + return registration; +} + +/****************************************************************************** + * I_ScUnregisterDeviceNotification (sechost.@) + */ +BOOL WINAPI I_ScUnregisterDeviceNotification( HDEVNOTIFY handle ) +{ + struct device_notify_registration *registration = handle; + + TRACE("%p\n", handle); + + EnterCriticalSection( &service_cs ); + list_remove( ®istration->entry ); + LeaveCriticalSection(&service_cs); + heap_free( registration ); + return TRUE; +} diff --git a/include/wine/plugplay.idl b/include/wine/plugplay.idl new file mode 100644 index 00000000000..7cc59191248 --- /dev/null +++ b/include/wine/plugplay.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 Zebediah Figura for CodeWeavers + * + * 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 + */ + +import "wtypes.idl"; + +[ + uuid(57c680ac-7bce-4f39-97fd-ffea566754d5), + implicit_handle(handle_t plugplay_binding_handle) +] +interface plugplay +{ + typedef [context_handle] void *plugplay_rpc_handle; + + plugplay_rpc_handle plugplay_register_listener(); + DWORD plugplay_get_event([in] plugplay_rpc_handle handle, [out, size_is(,*size)] BYTE **data, [out] unsigned int *size); + void plugplay_unregister_listener([in] plugplay_rpc_handle handle); +} diff --git a/programs/plugplay/Makefile.in b/programs/plugplay/Makefile.in index e5d37e66f1d..1bc2aa505ab 100644 --- a/programs/plugplay/Makefile.in +++ b/programs/plugplay/Makefile.in @@ -1,7 +1,10 @@ MODULE = plugplay.exe -IMPORTS = advapi32 +IMPORTS = advapi32 rpcrt4
EXTRADLLFLAGS = -mconsole -municode -mno-cygwin
C_SRCS = \ main.c + +IDL_SRCS = \ + plugplay.idl diff --git a/programs/plugplay/main.c b/programs/plugplay/main.c index 73bf46d6f99..59561dca503 100644 --- a/programs/plugplay/main.c +++ b/programs/plugplay/main.c @@ -21,6 +21,8 @@ #include <windows.h> #include "winsvc.h" #include "wine/debug.h" +#include "wine/list.h" +#include "plugplay.h"
WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
@@ -29,6 +31,111 @@ static WCHAR plugplayW[] = {'P','l','u','g','P','l','a','y',0}; static SERVICE_STATUS_HANDLE service_handle; static HANDLE stop_event;
+void __RPC_FAR * __RPC_USER MIDL_user_allocate( SIZE_T len ) +{ + return malloc( len ); +} + +void __RPC_USER MIDL_user_free( void __RPC_FAR *ptr ) +{ + free( ptr ); +} + +static CRITICAL_SECTION plugplay_cs; +static CRITICAL_SECTION_DEBUG plugplay_cs_debug = +{ + 0, 0, &plugplay_cs, + { &plugplay_cs_debug.ProcessLocksList, &plugplay_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": plugplay_cs") } +}; +static CRITICAL_SECTION plugplay_cs = { &plugplay_cs_debug, -1, 0, 0, 0, 0 }; + +static struct list listener_list = LIST_INIT(listener_list); + +struct listener +{ + struct list entry; + struct list events; + CONDITION_VARIABLE cv; +}; + +struct event +{ + struct list entry; + DWORD code; + BYTE *data; + unsigned int size; +}; + + +static void destroy_listener( struct listener *listener ) +{ + struct event *event, *next; + + EnterCriticalSection( &plugplay_cs ); + list_remove( &listener->entry ); + LeaveCriticalSection( &plugplay_cs ); + + LIST_FOR_EACH_ENTRY_SAFE(event, next, &listener->events, struct event, entry) + { + MIDL_user_free( event->data ); + list_remove( &event->entry ); + free( event ); + } + free( listener ); +} + +void __RPC_USER plugplay_rpc_handle_rundown( plugplay_rpc_handle handle ) +{ + destroy_listener( handle ); +} + +plugplay_rpc_handle __cdecl plugplay_register_listener(void) +{ + struct listener *listener; + + if (!(listener = calloc( 1, sizeof(*listener) ))) + return NULL; + + list_init( &listener->events ); + InitializeConditionVariable( &listener->cv ); + + EnterCriticalSection( &plugplay_cs ); + list_add_tail( &listener_list, &listener->entry ); + LeaveCriticalSection( &plugplay_cs ); + + return listener; +} + +DWORD __cdecl plugplay_get_event( plugplay_rpc_handle handle, BYTE **data, unsigned int *size ) +{ + struct listener *listener = handle; + struct event *event; + struct list *entry; + DWORD ret; + + EnterCriticalSection( &plugplay_cs ); + + while (!(entry = list_head( &listener->events ))) + SleepConditionVariableCS( &listener->cv, &plugplay_cs, INFINITE ); + + event = LIST_ENTRY(entry, struct event, entry); + list_remove( &event->entry ); + + LeaveCriticalSection( &plugplay_cs ); + + ret = event->code; + *data = event->data; + *size = event->size; + free( event ); + return ret; +} + +void __cdecl plugplay_unregister_listener( plugplay_rpc_handle handle ) +{ + destroy_listener( handle ); +} + static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context ) { SERVICE_STATUS status; @@ -60,10 +167,29 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_
static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) { + unsigned char endpoint[] = "\pipe\wine_plugplay"; + unsigned char protseq[] = "ncalrpc"; SERVICE_STATUS status; + RPC_STATUS err;
WINE_TRACE( "starting service\n" );
+ if ((err = RpcServerUseProtseqEpA( protseq, 0, endpoint, NULL ))) + { + ERR("RpcServerUseProtseqEp() failed, error %u\n", err); + return; + } + if ((err = RpcServerRegisterIf( plugplay_v0_0_s_ifspec, NULL, NULL ))) + { + ERR("RpcServerRegisterIf() failed, error %u\n", err); + return; + } + if ((err = RpcServerListen( 1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE ))) + { + ERR("RpcServerListen() failed, error %u\n", err); + return; + } + stop_event = CreateEventW( NULL, TRUE, FALSE, NULL );
service_handle = RegisterServiceCtrlHandlerExW( plugplayW, service_handler, NULL ); @@ -81,6 +207,10 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
WaitForSingleObject( stop_event, INFINITE );
+ RpcMgmtStopServerListening( NULL ); + RpcServerUnregisterIf( plugplay_v0_0_s_ifspec, NULL, TRUE ); + RpcMgmtWaitServerListen(); + status.dwCurrentState = SERVICE_STOPPED; status.dwControlsAccepted = 0; SetServiceStatus( service_handle, &status ); diff --git a/programs/plugplay/plugplay.idl b/programs/plugplay/plugplay.idl new file mode 100644 index 00000000000..0cb8884c2df --- /dev/null +++ b/programs/plugplay/plugplay.idl @@ -0,0 +1,2 @@ +#pragma makedep server +#include "wine/plugplay.idl"
Based on a patch by Micah N Gorrell.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntoskrnl.exe/Makefile.in | 5 +++- dlls/ntoskrnl.exe/plugplay.idl | 2 ++ dlls/ntoskrnl.exe/pnp.c | 50 ++++++++++++++++++++++++++++++++-- include/wine/plugplay.idl | 1 + programs/plugplay/main.c | 28 +++++++++++++++++++ 5 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 dlls/ntoskrnl.exe/plugplay.idl
diff --git a/dlls/ntoskrnl.exe/Makefile.in b/dlls/ntoskrnl.exe/Makefile.in index 67afa8f9d8a..85bc1ce6987 100644 --- a/dlls/ntoskrnl.exe/Makefile.in +++ b/dlls/ntoskrnl.exe/Makefile.in @@ -1,7 +1,7 @@ MODULE = ntoskrnl.exe IMPORTLIB = ntoskrnl IMPORTS = advapi32 hal msvcrt -DELAYIMPORTS = setupapi user32 +DELAYIMPORTS = rpcrt4 setupapi user32
EXTRADLLFLAGS = -mno-cygwin
@@ -12,3 +12,6 @@ C_SRCS = \ sync.c
RC_SRCS = ntoskrnl.rc + +IDL_SRCS = \ + plugplay.idl diff --git a/dlls/ntoskrnl.exe/plugplay.idl b/dlls/ntoskrnl.exe/plugplay.idl new file mode 100644 index 00000000000..05e040388e4 --- /dev/null +++ b/dlls/ntoskrnl.exe/plugplay.idl @@ -0,0 +1,2 @@ +#pragma makedep client +#include "wine/plugplay.idl" diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index 9e4c2c1416a..470e2368fb5 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -39,10 +39,12 @@ #include "ddk/wdm.h" #include "ddk/ntifs.h" #include "wine/debug.h" +#include "wine/exception.h" #include "wine/heap.h" #include "wine/rbtree.h"
#include "ntoskrnl_private.h" +#include "plugplay.h"
#include "initguid.h" DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); @@ -651,6 +653,35 @@ static NTSTATUS create_device_symlink( DEVICE_OBJECT *device, UNICODE_STRING *sy return ret; }
+void __RPC_FAR * __RPC_USER MIDL_user_allocate( SIZE_T len ) +{ + return heap_alloc( len ); +} + +void __RPC_USER MIDL_user_free( void __RPC_FAR *ptr ) +{ + heap_free( ptr ); +} + +static LONG WINAPI rpc_filter( EXCEPTION_POINTERS *eptr ) +{ + return I_RpcExceptionFilter( eptr->ExceptionRecord->ExceptionCode ); +} + +static void send_devicechange( DWORD code, void *data, unsigned int size ) +{ + BroadcastSystemMessageW( BSF_FORCEIFHUNG | BSF_QUERY, NULL, WM_DEVICECHANGE, code, (LPARAM)data ); + __TRY + { + plugplay_send_event( code, data, size ); + } + __EXCEPT(rpc_filter) + { + WARN("Failed to send event, exception %#x.\n", GetExceptionCode()); + } + __ENDTRY +} + /*********************************************************************** * IoSetDeviceInterfaceState (NTOSKRNL.EXE.@) */ @@ -756,9 +787,7 @@ NTSTATUS WINAPI IoSetDeviceInterfaceState( UNICODE_STRING *name, BOOLEAN enable broadcast->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; broadcast->dbcc_classguid = iface->interface_class; lstrcpynW( broadcast->dbcc_name, name->Buffer, namelen + 1 ); - BroadcastSystemMessageW( BSF_FORCEIFHUNG | BSF_QUERY, NULL, WM_DEVICECHANGE, - enable ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE, (LPARAM)broadcast ); - + send_devicechange( enable ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE, broadcast, len ); heap_free( broadcast ); } return ret; @@ -1002,12 +1031,26 @@ static NTSTATUS WINAPI pnp_manager_driver_entry( DRIVER_OBJECT *driver, UNICODE_ void pnp_manager_start(void) { static const WCHAR driver_nameW[] = {'\','D','r','i','v','e','r','\','P','n','p','M','a','n','a','g','e','r',0}; + WCHAR endpoint[] = L"\pipe\wine_plugplay"; + WCHAR protseq[] = L"ncalrpc"; UNICODE_STRING driver_nameU; + RPC_WSTR binding_str; NTSTATUS status; + RPC_STATUS err;
RtlInitUnicodeString( &driver_nameU, driver_nameW ); if ((status = IoCreateDriver( &driver_nameU, pnp_manager_driver_entry ))) ERR("Failed to create PnP manager driver, status %#x.\n", status); + + if ((err = RpcStringBindingComposeW( NULL, protseq, NULL, endpoint, NULL, &binding_str ))) + { + ERR("RpcStringBindingCompose() failed, error %#x\n", err); + return; + } + err = RpcBindingFromStringBindingW( binding_str, &plugplay_binding_handle ); + RpcStringFreeW( &binding_str ); + if (err) + ERR("RpcBindingFromStringBinding() failed, error %#x\n", err); }
static void destroy_root_pnp_device( struct wine_rb_entry *entry, void *context ) @@ -1020,6 +1063,7 @@ void pnp_manager_stop(void) { wine_rb_destroy( &root_pnp_devices, destroy_root_pnp_device, NULL ); IoDeleteDriver( pnp_manager ); + RpcBindingFree( &plugplay_binding_handle ); }
void pnp_manager_enumerate_root_devices( const WCHAR *driver_name ) diff --git a/include/wine/plugplay.idl b/include/wine/plugplay.idl index 7cc59191248..8123b733ad1 100644 --- a/include/wine/plugplay.idl +++ b/include/wine/plugplay.idl @@ -29,4 +29,5 @@ interface plugplay plugplay_rpc_handle plugplay_register_listener(); DWORD plugplay_get_event([in] plugplay_rpc_handle handle, [out, size_is(,*size)] BYTE **data, [out] unsigned int *size); void plugplay_unregister_listener([in] plugplay_rpc_handle handle); + void plugplay_send_event([in] DWORD event_code, [in, size_is(size)] const BYTE *data, [in] unsigned int size); } diff --git a/programs/plugplay/main.c b/programs/plugplay/main.c index 59561dca503..00af2b0adfa 100644 --- a/programs/plugplay/main.c +++ b/programs/plugplay/main.c @@ -136,6 +136,34 @@ void __cdecl plugplay_unregister_listener( plugplay_rpc_handle handle ) destroy_listener( handle ); }
+void __cdecl plugplay_send_event( DWORD code, const BYTE *data, unsigned int size ) +{ + struct listener *listener; + struct event *event; + + EnterCriticalSection( &plugplay_cs ); + + LIST_FOR_EACH_ENTRY(listener, &listener_list, struct listener, entry) + { + if (!(event = malloc( sizeof(*event) ))) + break; + + if (!(event->data = malloc( size ))) + { + free( event ); + break; + } + + event->code = code; + memcpy( event->data, data, size ); + event->size = size; + list_add_tail( &listener->events, &event->entry ); + WakeConditionVariable( &listener->cv ); + } + + LeaveCriticalSection( &plugplay_cs ); +} + static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context ) { SERVICE_STATUS status;
Based on a patch by Micah N Gorrell.
This fixes controller hotplugging in Into the Breach.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/user32/Makefile.in | 2 +- dlls/user32/misc.c | 81 +++++++++++++++++++++++++---------------- include/winuser.h | 4 +- 3 files changed, 53 insertions(+), 34 deletions(-)
diff --git a/dlls/user32/Makefile.in b/dlls/user32/Makefile.in index a91dd89c941..6814154b747 100644 --- a/dlls/user32/Makefile.in +++ b/dlls/user32/Makefile.in @@ -1,7 +1,7 @@ EXTRADEFS = -D_USER32_ -D_WINABLE_ MODULE = user32.dll IMPORTLIB = user32 -IMPORTS = setupapi gdi32 version advapi32 kernelbase +IMPORTS = setupapi gdi32 version advapi32 kernelbase sechost EXTRAINCL = $(PNG_CFLAGS) DELAYIMPORTS = hid imm32 usp10
diff --git a/dlls/user32/misc.c b/dlls/user32/misc.c index bed5812a47d..7229588c34a 100644 --- a/dlls/user32/misc.c +++ b/dlls/user32/misc.c @@ -4,6 +4,7 @@ * Copyright 1995 Thomas Sandford * Copyright 1997 Marcus Meissner * Copyright 1998 Turchanov Sergey + * Copyright 2019 Micah N Gorrell for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,6 +31,7 @@ #include "wingdi.h" #include "controls.h" #include "user_private.h" +#include "dbt.h"
#include "wine/unicode.h" #include "wine/debug.h" @@ -288,57 +290,72 @@ DWORD WINAPI RegisterTasklist (DWORD x) return TRUE; }
+static DWORD CALLBACK devnotify_window_callback(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header) +{ + SendMessageTimeoutW(handle, WM_DEVICECHANGE, flags, (LPARAM)header, SMTO_ABORTIFHUNG, 2000, NULL); + return 0; +} + +static DWORD CALLBACK devnotify_service_callback(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header) +{ + FIXME("Support for service handles is not yet implemented!\n"); + return 0; +} + +struct device_notification_details +{ + DWORD (CALLBACK *cb)(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header); + HANDLE handle; +}; + +extern HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification( struct device_notification_details *details, + void *filter, DWORD flags ); +extern BOOL WINAPI I_ScUnregisterDeviceNotification( HDEVNOTIFY handle );
/*********************************************************************** * RegisterDeviceNotificationA (USER32.@) * * See RegisterDeviceNotificationW. */ -HDEVNOTIFY WINAPI RegisterDeviceNotificationA(HANDLE hnd, LPVOID notifyfilter, DWORD flags) +HDEVNOTIFY WINAPI RegisterDeviceNotificationA(HANDLE hRecipient, LPVOID pNotificationFilter, DWORD dwFlags) { - FIXME("(hwnd=%p, filter=%p,flags=0x%08x) returns a fake device notification handle!\n", - hnd,notifyfilter,flags ); - return (HDEVNOTIFY) 0xcafecafe; + TRACE("(hwnd=%p, filter=%p,flags=0x%08x)\n", + hRecipient,pNotificationFilter,dwFlags); + if (pNotificationFilter) + FIXME("The notification filter will requires an A->W when filter support is implemented\n"); + return RegisterDeviceNotificationW(hRecipient, pNotificationFilter, dwFlags); }
/*********************************************************************** * RegisterDeviceNotificationW (USER32.@) - * - * Registers a window with the system so that it will receive - * notifications about a device. - * - * PARAMS - * hRecipient [I] Window or service status handle that - * will receive notifications. - * pNotificationFilter [I] DEV_BROADCAST_HDR followed by some - * type-specific data. - * dwFlags [I] See notes - * - * RETURNS - * - * A handle to the device notification. - * - * NOTES - * - * The dwFlags parameter can be one of two values: - *| DEVICE_NOTIFY_WINDOW_HANDLE - hRecipient is a window handle - *| DEVICE_NOTIFY_SERVICE_HANDLE - hRecipient is a service status handle */ -HDEVNOTIFY WINAPI RegisterDeviceNotificationW(HANDLE hRecipient, LPVOID pNotificationFilter, DWORD dwFlags) +HDEVNOTIFY WINAPI RegisterDeviceNotificationW( HANDLE handle, void *filter, DWORD flags ) { - FIXME("(hwnd=%p, filter=%p,flags=0x%08x) returns a fake device notification handle!\n", - hRecipient,pNotificationFilter,dwFlags ); - return (HDEVNOTIFY) 0xcafeaffe; + struct device_notification_details details; + + TRACE("handle %p, filter %p, flags %#x\n", handle, filter, flags); + + if (flags & ~(DEVICE_NOTIFY_SERVICE_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES)) + FIXME("unhandled flags %#x\n", flags); + + details.handle = handle; + + if (flags & DEVICE_NOTIFY_SERVICE_HANDLE) + details.cb = devnotify_service_callback; + else + details.cb = devnotify_window_callback; + + return I_ScRegisterDeviceNotification( &details, filter, 0 ); }
/*********************************************************************** * UnregisterDeviceNotification (USER32.@) - * */ -BOOL WINAPI UnregisterDeviceNotification(HDEVNOTIFY hnd) +BOOL WINAPI UnregisterDeviceNotification( HDEVNOTIFY handle ) { - FIXME("(handle=%p), STUB!\n", hnd); - return TRUE; + TRACE("%p\n", handle); + + return I_ScUnregisterDeviceNotification( handle ); }
/*********************************************************************** diff --git a/include/winuser.h b/include/winuser.h index 5fd774d2959..aa392d636c8 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -3114,7 +3114,9 @@ typedef struct tagTRACKMOUSEEVENT { typedef PVOID HDEVNOTIFY; typedef HDEVNOTIFY *PHDEVNOTIFY;
-#define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000 +#define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000 +#define DEVICE_NOTIFY_SERVICE_HANDLE 0x00000001 +#define DEVICE_NOTIFY_ALL_INTERFACE_CLASSES 0x00000004
/* used for GetWindowInfo() */
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntoskrnl.exe/Makefile.in | 2 +- dlls/ntoskrnl.exe/pnp.c | 1 - programs/plugplay/Makefile.in | 2 +- programs/plugplay/main.c | 2 ++ 4 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/dlls/ntoskrnl.exe/Makefile.in b/dlls/ntoskrnl.exe/Makefile.in index 85bc1ce6987..df48e44ede9 100644 --- a/dlls/ntoskrnl.exe/Makefile.in +++ b/dlls/ntoskrnl.exe/Makefile.in @@ -1,7 +1,7 @@ MODULE = ntoskrnl.exe IMPORTLIB = ntoskrnl IMPORTS = advapi32 hal msvcrt -DELAYIMPORTS = rpcrt4 setupapi user32 +DELAYIMPORTS = rpcrt4 setupapi
EXTRADLLFLAGS = -mno-cygwin
diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index 470e2368fb5..91b825dffc6 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -670,7 +670,6 @@ static LONG WINAPI rpc_filter( EXCEPTION_POINTERS *eptr )
static void send_devicechange( DWORD code, void *data, unsigned int size ) { - BroadcastSystemMessageW( BSF_FORCEIFHUNG | BSF_QUERY, NULL, WM_DEVICECHANGE, code, (LPARAM)data ); __TRY { plugplay_send_event( code, data, size ); diff --git a/programs/plugplay/Makefile.in b/programs/plugplay/Makefile.in index 1bc2aa505ab..001d81597f7 100644 --- a/programs/plugplay/Makefile.in +++ b/programs/plugplay/Makefile.in @@ -1,5 +1,5 @@ MODULE = plugplay.exe -IMPORTS = advapi32 rpcrt4 +IMPORTS = advapi32 rpcrt4 user32
EXTRADLLFLAGS = -mconsole -municode -mno-cygwin
diff --git a/programs/plugplay/main.c b/programs/plugplay/main.c index 00af2b0adfa..8e007048422 100644 --- a/programs/plugplay/main.c +++ b/programs/plugplay/main.c @@ -141,6 +141,8 @@ void __cdecl plugplay_send_event( DWORD code, const BYTE *data, unsigned int siz struct listener *listener; struct event *event;
+ BroadcastSystemMessageW( BSF_FORCEIFHUNG | BSF_QUERY, NULL, WM_DEVICECHANGE, code, (LPARAM)data ); + EnterCriticalSection( &plugplay_cs );
LIST_FOR_EACH_ENTRY(listener, &listener_list, struct listener, entry)