Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
Still not sure what the difference between ncacn_np and ncalrpc is, but this seems to just work. The services manager also uses ncacn_np instead of ncalrpc. I don't think we want to rely on rpcss.exe, for these PnP notifications, as it creates an additional service dependency which is going to be hard to solve.
FWIW, relying on rpcss.exe also causes some prefix shutdown issues, as the PnP device removal involves sending some notifications. As rpcss.exe usually exits at the same time, plugplay.exe is then waiting on it and may block if it's not responding anymore.
This was resolved for a while by wineserver killing and reaping the services after its shutdown timeout, but caused an additional 1s delay, and later with 7f749d58d9cbb0b3d54f2db75dbd88695b20901a, by the service manager terminating all the service processes on shutdown instead.
For proper service shutdown, the manager should instead wait for the services to exit, and in this case not relying on rpcss.exe will help plugplay exit itself quickly.
dlls/ntoskrnl.exe/pnp.c | 2 +- dlls/sechost/service.c | 2 +- include/wine/plugplay.idl | 1 + programs/plugplay/main.c | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index 6d53d79267e..8d3481b2a72 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -1084,7 +1084,7 @@ 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"; + WCHAR protseq[] = L"ncacn_np"; UNICODE_STRING driver_nameU; RPC_WSTR binding_str; NTSTATUS status; diff --git a/dlls/sechost/service.c b/dlls/sechost/service.c index e6f4eb75db0..4dfb40b4bac 100644 --- a/dlls/sechost/service.c +++ b/dlls/sechost/service.c @@ -1988,7 +1988,7 @@ struct device_notify_registration static DWORD WINAPI device_notify_proc( void *arg ) { WCHAR endpoint[] = L"\pipe\wine_plugplay"; - WCHAR protseq[] = L"ncalrpc"; + WCHAR protseq[] = L"ncacn_np"; RPC_WSTR binding_str; DWORD err = ERROR_SUCCESS; struct device_notify_registration *registration; diff --git a/include/wine/plugplay.idl b/include/wine/plugplay.idl index 8123b733ad1..a3e7b04bf30 100644 --- a/include/wine/plugplay.idl +++ b/include/wine/plugplay.idl @@ -20,6 +20,7 @@ import "wtypes.idl";
[ uuid(57c680ac-7bce-4f39-97fd-ffea566754d5), + endpoint("ncacn_np:[\pipe\wine_plugplay]"), implicit_handle(handle_t plugplay_binding_handle) ] interface plugplay diff --git a/programs/plugplay/main.c b/programs/plugplay/main.c index 1156cb9f036..366be9c2b46 100644 --- a/programs/plugplay/main.c +++ b/programs/plugplay/main.c @@ -200,7 +200,7 @@ 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"; + unsigned char protseq[] = "ncacn_np"; SERVICE_STATUS status; RPC_STATUS err;
Device drivers may be installed and started on prefix initialization but if PlugPlay is missing they won't be able to send plugplay notifications until the prefix is restarted.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
Note that this, and the previous patch, will not fix the notification failure on prefix startup for already pluged devices, which often translate to a few 0x6ba exceptions.
These exceptions are a race with service startup ordering, between the device services and plugplay.exe service. That race should not be an issue, as nothing will be waiting for notifications that early, although it could maybe be nice to fix it too to get rid of these exceptions.
loader/wine.inf.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/loader/wine.inf.in b/loader/wine.inf.in index c0251934dfc..3af8dabca64 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -178,7 +178,7 @@ AddService=RpcSs,0,RpcSsService AddService=Spooler,0,SpoolerService AddService=StiSvc,0,StiService AddService=TermService,0,TerminalServices -AddService=PlugPlay,0,PlugPlayService +AddService=PlugPlay,0x800,PlugPlayService AddService=FontCache3.0.0.0,0,WPFFontCacheService AddService=LanmanServer,0,LanmanServerService AddService=FontCache,0,FontCacheService @@ -197,7 +197,7 @@ AddService=RpcSs,0,RpcSsService AddService=Spooler,0,SpoolerService AddService=StiSvc,0,StiService AddService=TermService,0,TerminalServices -AddService=PlugPlay,0,PlugPlayService +AddService=PlugPlay,0x800,PlugPlayService AddService=FontCache3.0.0.0,0,WPFFontCacheService AddService=LanmanServer,0,LanmanServerService AddService=FontCache,0,FontCacheService @@ -216,7 +216,7 @@ AddService=RpcSs,0,RpcSsService AddService=Spooler,0,SpoolerService AddService=StiSvc,0,StiService AddService=TermService,0,TerminalServices -AddService=PlugPlay,0,PlugPlayService +AddService=PlugPlay,0x800,PlugPlayService AddService=FontCache3.0.0.0,0,WPFFontCacheService AddService=LanmanServer,0,LanmanServerService AddService=FontCache,0,FontCacheService @@ -235,7 +235,7 @@ AddService=RpcSs,0,RpcSsService AddService=Spooler,0,SpoolerService AddService=StiSvc,0,StiService AddService=TermService,0,TerminalServices -AddService=PlugPlay,0,PlugPlayService +AddService=PlugPlay,0x800,PlugPlayService AddService=FontCache3.0.0.0,0,WPFFontCacheService AddService=LanmanServer,0,LanmanServerService AddService=FontCache,0,FontCacheService
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/dinput_test.h | 1 + dlls/dinput/tests/hid.c | 86 ++++++++++ dlls/dinput/tests/hotplug.c | 283 +++++++++++++++++++++++++++++++- 3 files changed, 369 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/tests/dinput_test.h b/dlls/dinput/tests/dinput_test.h index 0a4c9ba5fa4..97eed949a36 100644 --- a/dlls/dinput/tests/dinput_test.h +++ b/dlls/dinput/tests/dinput_test.h @@ -66,6 +66,7 @@ BOOL dinput_test_init_( const char *file, int line ); void dinput_test_exit(void);
HRESULT dinput_test_create_device( DWORD version, DIDEVICEINSTANCEW *devinst, IDirectInputDevice8W **device ); +DWORD WINAPI dinput_test_device_thread( void *stop_event );
#define check_member_( file, line, val, exp, fmt, member ) \ ok_(file, line)( (val).member == (exp).member, "got " #member " " fmt "\n", (val).member ) diff --git a/dlls/dinput/tests/hid.c b/dlls/dinput/tests/hid.c index 0e94ce63aab..7e4ab3dc1f3 100644 --- a/dlls/dinput/tests/hid.c +++ b/dlls/dinput/tests/hid.c @@ -3478,6 +3478,92 @@ HRESULT dinput_test_create_device( DWORD version, DIDEVICEINSTANCEW *devinst, ID return DI_OK; }
+DWORD WINAPI dinput_test_device_thread( void *stop_event ) +{ +#include "psh_hid_macros.h" + static const unsigned char gamepad_desc[] = + { + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_GAMEPAD), + COLLECTION(1, Application), + USAGE(1, HID_USAGE_GENERIC_GAMEPAD), + COLLECTION(1, Physical), + USAGE(1, HID_USAGE_GENERIC_X), + USAGE(1, HID_USAGE_GENERIC_Y), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 127), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 127), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 2), + INPUT(1, Data|Var|Abs), + + USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON), + USAGE_MINIMUM(1, 1), + USAGE_MAXIMUM(1, 6), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 8), + INPUT(1, Data|Var|Abs), + END_COLLECTION, + END_COLLECTION, + }; +#include "pop_hid_macros.h" + static const HID_DEVICE_ATTRIBUTES attributes = + { + .Size = sizeof(HID_DEVICE_ATTRIBUTES), + .VendorID = LOWORD(EXPECT_VIDPID), + .ProductID = HIWORD(EXPECT_VIDPID), + .VersionNumber = 0x0100, + }; + static const HIDP_CAPS caps = + { + .InputReportByteLength = 3, + }; + + WCHAR cwd[MAX_PATH], tempdir[MAX_PATH]; + DWORD report_id = 1, polled = 0; + char context[64]; + LSTATUS status; + HKEY hkey; + + GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd ); + GetTempPathW( ARRAY_SIZE(tempdir), tempdir ); + SetCurrentDirectoryW( tempdir ); + + status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\CurrentControlSet\Services\winetest", + 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ); + ok( !status, "RegCreateKeyExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"ReportID", 0, REG_DWORD, (void *)&report_id, sizeof(report_id) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"PolledMode", 0, REG_DWORD, (void *)&polled, sizeof(polled) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Descriptor", 0, REG_BINARY, (void *)gamepad_desc, sizeof(gamepad_desc) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Attributes", 0, REG_BINARY, (void *)&attributes, sizeof(attributes) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Caps", 0, REG_BINARY, (void *)&caps, sizeof(caps) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Expect", 0, REG_BINARY, (void *)NULL, 0 ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Input", 0, REG_BINARY, NULL, 0 ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + fill_context( __LINE__, context, ARRAY_SIZE(context) ); + status = RegSetValueExW( hkey, L"Context", 0, REG_BINARY, (void *)context, sizeof(context) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + pnp_driver_start( L"driver_hid.dll" ); + WaitForSingleObject( stop_event, INFINITE ); + pnp_driver_stop(); + + SetCurrentDirectoryW( cwd ); + + return 0; +} + START_TEST( hid ) { if (!dinput_test_init()) return; diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c index afe51cf0e97..d271d9bc1db 100644 --- a/dlls/dinput/tests/hotplug.c +++ b/dlls/dinput/tests/hotplug.c @@ -31,7 +31,7 @@ #include "dinput.h" #include "dinputd.h" #include "devguid.h" -#include "mmsystem.h" +#include "dbt.h"
#include "wine/hid.h"
@@ -154,6 +154,285 @@ done: return device != NULL; }
+static int device_change_count; +static int device_change_expect; +static HWND device_change_hwnd; +static BOOL device_change_all; + +static BOOL all_upper( const WCHAR *str, const WCHAR *end ) +{ + while (str++ != end) if (towupper( str[-1] ) != str[-1]) return FALSE; + return TRUE; +} + +static BOOL all_lower( const WCHAR *str, const WCHAR *end ) +{ + while (str++ != end) if (towlower( str[-1] ) != str[-1]) return FALSE; + return TRUE; +} + +static LRESULT CALLBACK devnotify_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + if (msg == WM_DEVICECHANGE) + { + DEV_BROADCAST_HDR *header = (DEV_BROADCAST_HDR *)lparam; + DEV_BROADCAST_DEVICEINTERFACE_W *iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)lparam; + const WCHAR *upper_end, *name_end, *expect_prefix; + GUID expect_guid; + + if (device_change_all && (device_change_count == 0 || device_change_count == 3)) + { + expect_guid = control_class; + expect_prefix = L"\\?\ROOT#"; + } + else + { + expect_guid = GUID_DEVINTERFACE_HID; + expect_prefix = L"\\?\HID#"; + } + + ok( hwnd == device_change_hwnd, "got hwnd %p\n", hwnd ); + ok( header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE, "got dbch_devicetype %u\n", + header->dbch_devicetype ); + + winetest_push_context( "%u", device_change_count ); + + todo_wine_if( IsEqualGUID( &iface->dbcc_classguid, &control_class ) && !device_change_all ) + ok( IsEqualGUID( &iface->dbcc_classguid, &expect_guid ), "got dbch_classguid %s\n", + debugstr_guid( &iface->dbcc_classguid ) ); + ok( iface->dbcc_size >= offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name[wcslen( iface->dbcc_name ) + 1] ), + "got dbcc_size %u\n", iface->dbcc_size ); + todo_wine + ok( !wcsncmp( iface->dbcc_name, expect_prefix, wcslen( expect_prefix ) ), + "got dbcc_name %s\n", debugstr_w(iface->dbcc_name) ); + + upper_end = wcschr( iface->dbcc_name + wcslen( expect_prefix ), '#' ); + name_end = iface->dbcc_name + wcslen( iface->dbcc_name ) + 1; + ok( !!upper_end, "got dbcc_name %s\n", debugstr_w(iface->dbcc_name) ); + todo_wine + ok( all_upper( iface->dbcc_name, upper_end ), "got dbcc_name %s\n", debugstr_w(iface->dbcc_name) ); + ok( all_lower( upper_end, name_end ), "got dbcc_name %s\n", debugstr_w(iface->dbcc_name) ); + + if (device_change_count++ >= device_change_expect / 2) + ok( wparam == DBT_DEVICEREMOVECOMPLETE, "got wparam %#x\n", (DWORD)wparam ); + else + ok( wparam == DBT_DEVICEARRIVAL, "got wparam %#x\n", (DWORD)wparam ); + + winetest_pop_context(); + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +static void test_RegisterDeviceNotification(void) +{ + DEV_BROADCAST_DEVICEINTERFACE_A iface_filter_a = + { + .dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_A), + .dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE, + .dbcc_classguid = GUID_DEVINTERFACE_HID, + }; + WNDCLASSEXW class = + { + .cbSize = sizeof(WNDCLASSEXW), + .hInstance = GetModuleHandleW( NULL ), + .lpszClassName = L"devnotify", + .lpfnWndProc = devnotify_wndproc, + }; + char buffer[1024] = {0}; + DEV_BROADCAST_HDR *header = (DEV_BROADCAST_HDR *)buffer; + HANDLE hwnd, thread, stop_event; + HDEVNOTIFY devnotify; + MSG msg; + + RegisterClassExW( &class ); + + hwnd = CreateWindowW( class.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %u\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + devnotify = RegisterDeviceNotificationA( NULL, NULL, 0 ); + todo_wine + ok( !devnotify, "RegisterDeviceNotificationA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %u\n", GetLastError() ); + if (devnotify) UnregisterDeviceNotification( devnotify ); + + SetLastError( 0xdeadbeef ); + devnotify = RegisterDeviceNotificationA( (HWND)0xdeadbeef, NULL, 0 ); + todo_wine + ok( !devnotify, "RegisterDeviceNotificationA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %u\n", GetLastError() ); + if (devnotify) UnregisterDeviceNotification( devnotify ); + + SetLastError( 0xdeadbeef ); + devnotify = RegisterDeviceNotificationA( hwnd, NULL, 2 ); + todo_wine + ok( !devnotify, "RegisterDeviceNotificationA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %u\n", GetLastError() ); + if (devnotify) UnregisterDeviceNotification( devnotify ); + + SetLastError( 0xdeadbeef ); + memset( header, 0, sizeof(DEV_BROADCAST_OEM) ); + header->dbch_size = sizeof(DEV_BROADCAST_OEM); + header->dbch_devicetype = DBT_DEVTYP_OEM; + devnotify = RegisterDeviceNotificationA( hwnd, header, 0 ); + todo_wine + ok( !devnotify, "RegisterDeviceNotificationA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_DATA || GetLastError() == ERROR_SERVICE_SPECIFIC_ERROR, + "got error %u\n", GetLastError() ); + if (devnotify) UnregisterDeviceNotification( devnotify ); + + SetLastError( 0xdeadbeef ); + memset( header, 0, sizeof(DEV_BROADCAST_DEVNODE) ); + header->dbch_size = sizeof(DEV_BROADCAST_DEVNODE); + header->dbch_devicetype = DBT_DEVTYP_DEVNODE; + devnotify = RegisterDeviceNotificationA( hwnd, header, 0 ); + todo_wine + ok( !devnotify, "RegisterDeviceNotificationA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_DATA || GetLastError() == ERROR_SERVICE_SPECIFIC_ERROR, + "got error %u\n", GetLastError() ); + if (devnotify) UnregisterDeviceNotification( devnotify ); + + SetLastError( 0xdeadbeef ); + memset( header, 0, sizeof(DEV_BROADCAST_VOLUME) ); + header->dbch_size = sizeof(DEV_BROADCAST_VOLUME); + header->dbch_devicetype = DBT_DEVTYP_VOLUME; + devnotify = RegisterDeviceNotificationA( hwnd, header, 0 ); + todo_wine + ok( !devnotify, "RegisterDeviceNotificationA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_DATA || GetLastError() == ERROR_SERVICE_SPECIFIC_ERROR, + "got error %u\n", GetLastError() ); + if (devnotify) UnregisterDeviceNotification( devnotify ); + + SetLastError( 0xdeadbeef ); + memset( header, 0, sizeof(DEV_BROADCAST_PORT_A) ); + header->dbch_size = sizeof(DEV_BROADCAST_PORT_A); + header->dbch_devicetype = DBT_DEVTYP_PORT; + devnotify = RegisterDeviceNotificationA( hwnd, header, 0 ); + todo_wine + ok( !devnotify, "RegisterDeviceNotificationA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_DATA || GetLastError() == ERROR_SERVICE_SPECIFIC_ERROR, + "got error %u\n", GetLastError() ); + if (devnotify) UnregisterDeviceNotification( devnotify ); + + SetLastError( 0xdeadbeef ); + memset( header, 0, sizeof(DEV_BROADCAST_NET) ); + header->dbch_size = sizeof(DEV_BROADCAST_NET); + header->dbch_devicetype = DBT_DEVTYP_NET; + devnotify = RegisterDeviceNotificationA( hwnd, header, 0 ); + todo_wine + ok( !devnotify, "RegisterDeviceNotificationA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_DATA || GetLastError() == ERROR_SERVICE_SPECIFIC_ERROR, + "got error %u\n", GetLastError() ); + if (devnotify) UnregisterDeviceNotification( devnotify ); + + devnotify = RegisterDeviceNotificationA( hwnd, &iface_filter_a, DEVICE_NOTIFY_WINDOW_HANDLE ); + ok( !!devnotify, "RegisterDeviceNotificationA failed, error %u\n", GetLastError() ); + while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) DispatchMessageW( &msg ); + + device_change_count = 0; + if (!strcmp( winetest_platform, "wine" )) device_change_expect = 4; + else device_change_expect = 2; + device_change_hwnd = hwnd; + device_change_all = FALSE; + stop_event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!stop_event, "CreateEventW failed, error %u\n", GetLastError() ); + thread = CreateThread( NULL, 0, dinput_test_device_thread, stop_event, 0, NULL ); + ok( !!thread, "CreateThread failed, error %u\n", GetLastError() ); + + while (device_change_count < device_change_expect) + { + while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + ok( msg.message != WM_DEVICECHANGE, "got WM_DEVICECHANGE\n" ); + DispatchMessageW( &msg ); + } + if (device_change_count == device_change_expect / 2) SetEvent( stop_event ); + } + + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( stop_event ); + + UnregisterDeviceNotification( devnotify ); + + memcpy( buffer, &iface_filter_a, sizeof(iface_filter_a) ); + strcpy( ((DEV_BROADCAST_DEVICEINTERFACE_A *)buffer)->dbcc_name, "device name" ); + ((DEV_BROADCAST_DEVICEINTERFACE_A *)buffer)->dbcc_size += strlen( "device name" ) + 1; + devnotify = RegisterDeviceNotificationA( hwnd, buffer, DEVICE_NOTIFY_WINDOW_HANDLE ); + ok( !!devnotify, "RegisterDeviceNotificationA failed, error %u\n", GetLastError() ); + while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) DispatchMessageW( &msg ); + + device_change_count = 0; + if (!strcmp( winetest_platform, "wine" )) device_change_expect = 4; + else device_change_expect = 2; + device_change_hwnd = hwnd; + device_change_all = FALSE; + stop_event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!stop_event, "CreateEventW failed, error %u\n", GetLastError() ); + thread = CreateThread( NULL, 0, dinput_test_device_thread, stop_event, 0, NULL ); + ok( !!thread, "CreateThread failed, error %u\n", GetLastError() ); + + while (device_change_count < device_change_expect) + { + while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + ok( msg.message != WM_DEVICECHANGE, "got WM_DEVICECHANGE\n" ); + DispatchMessageW( &msg ); + } + if (device_change_count == device_change_expect / 2) SetEvent( stop_event ); + } + + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( stop_event ); + + UnregisterDeviceNotification( devnotify ); + + devnotify = RegisterDeviceNotificationA( hwnd, &iface_filter_a, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES ); + ok( !!devnotify, "RegisterDeviceNotificationA failed, error %u\n", GetLastError() ); + while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) DispatchMessageW( &msg ); + + device_change_count = 0; + device_change_expect = 4; + device_change_hwnd = hwnd; + device_change_all = TRUE; + stop_event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!stop_event, "CreateEventW failed, error %u\n", GetLastError() ); + thread = CreateThread( NULL, 0, dinput_test_device_thread, stop_event, 0, NULL ); + ok( !!thread, "CreateThread failed, error %u\n", GetLastError() ); + + while (device_change_count < device_change_expect) + { + while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + ok( msg.message != WM_DEVICECHANGE, "got WM_DEVICECHANGE\n" ); + DispatchMessageW( &msg ); + } + if (device_change_count == device_change_expect / 2) SetEvent( stop_event ); + } + + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( stop_event ); + + UnregisterDeviceNotification( devnotify ); + + DestroyWindow( hwnd ); + UnregisterClassW( class.lpszClassName, class.hInstance ); +} + START_TEST( hotplug ) { if (!dinput_test_init()) return; @@ -163,6 +442,8 @@ START_TEST( hotplug ) { test_input_lost( 0x700 ); test_input_lost( 0x800 ); + + test_RegisterDeviceNotification(); } CoUninitialize();
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=105926
Your paranoid android.
=== w8 (32 bit report) ===
dinput: joystick8.c:1588: Test failed: 0x500: got lX 0 joystick8.c:1589: Test failed: 0x500: got lY 0
After a better look it seems like this is not actually required for things to work properly, and what I thought was a dependency on rpcss was apparently an unfortunate and rare race condition during services shutdown (winedevice.exe getting stuck calling QueryServiceStatusEx), which has been resolved by terminating the services.
I still believe this is somehow a valid change though the title is incorrect:
According to MSDN, "\pipe\xxx" endpoints are only supposed to be used with ncacn_np whereas ncalrpc is supposed so be used with arbitrary "port" which must not contain any .
The ncalrpc transport actually builds an internal \.\pipe\lrpc\xxx pipe name from the arbitrary port name, and this ends up being \.\pipe\lrpc\pipe\wine_plugplay for the plugplay service. It works, but maybe it's not even supposed to.
I'll update the commit message and resend the series, reordered.