Send device notifications via RPC to services.exe
Signed-off-by: Micah N Gorrell mgorrell@codeweavers.com --- dlls/ntoskrnl.exe/Makefile.in | 4 +- dlls/ntoskrnl.exe/ntoskrnl_private.h | 5 + dlls/ntoskrnl.exe/pnp.c | 1 + dlls/ntoskrnl.exe/rpc.c | 182 +++++++++++++++++++++++++++ dlls/ntoskrnl.exe/svcctl.idl | 3 + include/wine/svcctl.idl | 7 ++ programs/services/rpc.c | 45 +++++++ 7 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 dlls/ntoskrnl.exe/rpc.c create mode 100644 dlls/ntoskrnl.exe/svcctl.idl
diff --git a/dlls/ntoskrnl.exe/Makefile.in b/dlls/ntoskrnl.exe/Makefile.in index a75ca9768d..441238177a 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 -DELAYIMPORTS = setupapi user32 +DELAYIMPORTS = setupapi user32 rpcrt4
EXTRADLLFLAGS = -mno-cygwin
@@ -9,6 +9,8 @@ C_SRCS = \ instr.c \ ntoskrnl.c \ pnp.c \ + rpc.c \ sync.c +IDL_SRCS = svcctl.idl
RC_SRCS = ntoskrnl.rc diff --git a/dlls/ntoskrnl.exe/ntoskrnl_private.h b/dlls/ntoskrnl.exe/ntoskrnl_private.h index b5244ef164..ded5c0bec4 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl_private.h +++ b/dlls/ntoskrnl.exe/ntoskrnl_private.h @@ -22,6 +22,8 @@ #define __WINE_NTOSKRNL_PRIVATE_H
#include "wine/asm.h" +#include "winuser.h" +#include "dbt.h"
static inline LPCSTR debugstr_us( const UNICODE_STRING *us ) { @@ -86,4 +88,7 @@ static const WCHAR servicesW[] = {'\','R','e','g','i','s','t','r','y', '\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t', '\','S','e','r','v','i','c','e','s', '\',0}; + +DWORD send_device_notification( DEV_BROADCAST_DEVICEINTERFACE_W *broadcast, BOOL enable ); + #endif diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index 03c4c401f9..645b04fd28 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -605,6 +605,7 @@ NTSTATUS WINAPI IoSetDeviceInterfaceState( UNICODE_STRING *name, BOOLEAN enable lstrcpynW( broadcast->dbcc_name, name->Buffer, namelen + 1 ); BroadcastSystemMessageW( BSF_FORCEIFHUNG | BSF_QUERY, NULL, WM_DEVICECHANGE, enable ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE, (LPARAM)broadcast ); + send_device_notification( broadcast, enable );
heap_free( broadcast ); } diff --git a/dlls/ntoskrnl.exe/rpc.c b/dlls/ntoskrnl.exe/rpc.c new file mode 100644 index 0000000000..a4779a168a --- /dev/null +++ b/dlls/ntoskrnl.exe/rpc.c @@ -0,0 +1,182 @@ +/* + * RPC connection with services.exe + * + * Copyright 1995 Sven Verdoolaege + * Copyright 2005 Mike McCormack + * Copyright 2007 Rolf Kalbermatter + * 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 + * 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 + */ + +#include <stdarg.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winsvc.h" +#include "winternl.h" +#include "dbt.h" +#include "svcctl.h" +#include "wine/exception.h" +#include "ddk/ntifs.h" +#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/rbtree.h" + +#include "ntoskrnl_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(plugplay); + +/****************************************************************************** + * RPC connection with services.exe + */ +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 handle_t rpc_wstr_bind(RPC_WSTR str) +{ + WCHAR transport[] = SVCCTL_TRANSPORT; + WCHAR endpoint[] = SVCCTL_ENDPOINT; + RPC_WSTR binding_str; + RPC_STATUS status; + handle_t rpc_handle; + + status = RpcStringBindingComposeW(NULL, transport, str, endpoint, NULL, &binding_str); + if (status != RPC_S_OK) + { + ERR("RpcStringBindingComposeW failed (%d)\n", (DWORD)status); + return NULL; + } + + status = RpcBindingFromStringBindingW(binding_str, &rpc_handle); + RpcStringFreeW(&binding_str); + + if (status != RPC_S_OK) + { + ERR("Couldn't connect to services.exe: error code %u\n", (DWORD)status); + return NULL; + } + + return rpc_handle; +} + +static handle_t rpc_cstr_bind(RPC_CSTR str) +{ + RPC_CSTR transport = (RPC_CSTR)SVCCTL_TRANSPORTA; + RPC_CSTR endpoint = (RPC_CSTR)SVCCTL_ENDPOINTA; + RPC_CSTR binding_str; + RPC_STATUS status; + handle_t rpc_handle; + + status = RpcStringBindingComposeA(NULL, transport, str, endpoint, NULL, &binding_str); + if (status != RPC_S_OK) + { + ERR("RpcStringBindingComposeW failed (%d)\n", (DWORD)status); + return NULL; + } + + status = RpcBindingFromStringBindingA(binding_str, &rpc_handle); + RpcStringFreeA(&binding_str); + + if (status != RPC_S_OK) + { + ERR("Couldn't connect to services.exe: error code %u\n", (DWORD)status); + return NULL; + } + + return rpc_handle; +} + +DECLSPEC_HIDDEN handle_t __RPC_USER MACHINE_HANDLEA_bind(MACHINE_HANDLEA MachineName) +{ + return rpc_cstr_bind((RPC_CSTR)MachineName); +} + +DECLSPEC_HIDDEN void __RPC_USER MACHINE_HANDLEA_unbind(MACHINE_HANDLEA MachineName, handle_t h) +{ + RpcBindingFree(&h); +} + +DECLSPEC_HIDDEN handle_t __RPC_USER MACHINE_HANDLEW_bind(MACHINE_HANDLEW MachineName) +{ + return rpc_wstr_bind((RPC_WSTR)MachineName); +} + +DECLSPEC_HIDDEN void __RPC_USER MACHINE_HANDLEW_unbind(MACHINE_HANDLEW MachineName, handle_t h) +{ + RpcBindingFree(&h); +} + +DECLSPEC_HIDDEN handle_t __RPC_USER SVCCTL_HANDLEW_bind(SVCCTL_HANDLEW MachineName) +{ + return rpc_wstr_bind((RPC_WSTR)MachineName); +} + +DECLSPEC_HIDDEN void __RPC_USER SVCCTL_HANDLEW_unbind(SVCCTL_HANDLEW MachineName, handle_t h) +{ + RpcBindingFree(&h); +} + +static LONG WINAPI rpc_filter(EXCEPTION_POINTERS *eptr) +{ + return I_RpcExceptionFilter(eptr->ExceptionRecord->ExceptionCode); +} + +static DWORD map_exception_code(DWORD exception_code) +{ + switch (exception_code) + { + case RPC_X_NULL_REF_POINTER: + return ERROR_INVALID_ADDRESS; + case RPC_X_ENUM_VALUE_OUT_OF_RANGE: + case RPC_X_BYTE_COUNT_TOO_SMALL: + return ERROR_INVALID_PARAMETER; + case RPC_S_INVALID_BINDING: + case RPC_X_SS_IN_NULL_CONTEXT: + return ERROR_INVALID_HANDLE; + default: + return exception_code; + } +} + +DWORD send_device_notification(DEV_BROADCAST_DEVICEINTERFACE_W *broadcast, BOOL enable) +{ + DWORD err; + + __TRY + { + err = svcctl_SendDeviceNotification(NULL, + enable ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE, + (const BYTE *) broadcast, broadcast->dbcc_size); + } + __EXCEPT(rpc_filter) + { + err = map_exception_code(GetExceptionCode()); + } + __ENDTRY + + TRACE("send result (%d)\n", err); + return err; +} diff --git a/dlls/ntoskrnl.exe/svcctl.idl b/dlls/ntoskrnl.exe/svcctl.idl new file mode 100644 index 0000000000..b1bc8545d7 --- /dev/null +++ b/dlls/ntoskrnl.exe/svcctl.idl @@ -0,0 +1,3 @@ +#pragma makedep client + +#include "wine/svcctl.idl" diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl index 0d75cf73c4..2399f3f5fd 100644 --- a/include/wine/svcctl.idl +++ b/include/wine/svcctl.idl @@ -818,4 +818,11 @@ typedef [switch_type(DWORD)] union _SC_RPC_SERVICE_CONTROL_OUT_PARAMSW { [out] DWORD *event_code, [out, size_is(,*event_size)] BYTE **event, [out] DWORD *event_size); + + /* Not compatible with Windows function 59 */ + DWORD svcctl_SendDeviceNotification( + [in, string, unique] SVCCTL_HANDLEW machinename, + [in] DWORD event_code, + [in, size_is(event_size)] const BYTE *event, + [in] DWORD event_size); } diff --git a/programs/services/rpc.c b/programs/services/rpc.c index 9bd43267b9..d66a575a13 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -2250,6 +2250,51 @@ DWORD __cdecl svcctl_GetDeviceNotificationResults( return ERROR_SUCCESS; }
+DWORD __cdecl svcctl_SendDeviceNotification( + MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */ + DWORD code, + const BYTE *event_buf, + DWORD event_buf_size) +{ + struct sc_dev_notify_handle *listener; + struct devnotify_event *event; + + if (!event_buf) + return ERROR_INVALID_PARAMETER; + + EnterCriticalSection(&device_notifications_cs); + LIST_FOR_EACH_ENTRY(listener, &devnotify_listeners, struct sc_dev_notify_handle, entry) + { + WINE_TRACE("Triggering listener %p\n", listener); + + event = HeapAlloc(GetProcessHeap(), 0, sizeof(struct devnotify_event)); + if (event) + event->data = MIDL_user_allocate(event_buf_size); + + if (!event || !event->data) + { + HeapFree(GetProcessHeap(), 0, event); + + LeaveCriticalSection(&device_notifications_cs); + return ERROR_NOT_ENOUGH_SERVER_MEMORY; + } + + event->code = code; + memcpy(event->data, event_buf, event_buf_size); + event->data_size = event_buf_size; + + EnterCriticalSection(&listener->cs); + list_add_tail(&listener->event_list, &event->entry); + LeaveCriticalSection(&listener->cs); + + SetEvent(listener->event); + } + WINE_TRACE("Done triggering registrations\n"); + LeaveCriticalSection(&device_notifications_cs); + + return ERROR_SUCCESS; +} + DWORD RPC_Init(void) { WCHAR transport[] = SVCCTL_TRANSPORT;