[PATCH v2 5/8] ntoskrnl.exe: Implement sending of device notifications

Micah N Gorrell mgorrell at codeweavers.com
Tue Sep 10 16:59:15 CDT 2019


Send device notifications via RPC to services.exe

Signed-off-by: Micah N Gorrell <mgorrell at 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;
-- 
2.23.0




More information about the wine-devel mailing list