The methods are needed to implement Bluetooth device pairing in `bluetoothapis.dll` without having to create a dummy window for `RegisterDeviceNotification`, as that may break CLI applications.
`CM_Register_Notification` is implemented as a thin wrapper over `I_ScRegisterDeviceNotification`, mapping `DBT_*` flags to their associated `CM_NOTIFY_*` constants.
-- v2: user32: Remove incorrect FIXME warning while registering for DBT_DEVTYP_HANDLE notifications. cfgmgr32: Implement CM_Register_Notification and CM_Unregister_Notification. dinput/tests: Add tests for CM_Register_Notification. cfgmgr32/tests: Add basic tests for CM_(Un)Register_Notification.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/cfgmgr32.spec | 1 + dlls/cfgmgr32/main.c | 10 ++++++++++ include/cfgmgr32.h | 1 + 3 files changed, 12 insertions(+)
diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index a990b2538eb..d940bb63427 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -187,3 +187,4 @@ @ stub CM_Unregister_Device_InterfaceW @ stub CM_Unregister_Device_Interface_ExA @ stub CM_Unregister_Device_Interface_ExW +@ stdcall CM_Unregister_Notification(ptr) diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index b545d761f68..3206cd2ed7a 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -69,6 +69,16 @@ CONFIGRET WINAPI CM_Register_Notification( CM_NOTIFY_FILTER *filter, void *conte return CR_CALL_NOT_IMPLEMENTED; }
+/*********************************************************************** + * CM_Unregister_Notification (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Unregister_Notification( HCMNOTIFICATION notify ) +{ + FIXME( "(%p) stub!\n", notify ); + + return CR_CALL_NOT_IMPLEMENTED; +} + /*********************************************************************** * CM_Get_Device_Interface_PropertyW (cfgmgr32.@) */ diff --git a/include/cfgmgr32.h b/include/cfgmgr32.h index 7f1036f3510..269aec9873c 100644 --- a/include/cfgmgr32.h +++ b/include/cfgmgr32.h @@ -330,6 +330,7 @@ CMAPI DWORD WINAPI CM_MapCrToWin32Err(CONFIGRET,DWORD); CMAPI CONFIGRET WINAPI CM_Open_DevNode_Key(DEVINST dnDevInst, REGSAM access, ULONG ulHardwareProfile, REGDISPOSITION disposition, PHKEY phkDevice, ULONG ulFlags); CMAPI CONFIGRET WINAPI CM_Register_Notification(PCM_NOTIFY_FILTER,PVOID,PCM_NOTIFY_CALLBACK,PHCMNOTIFICATION); +CMAPI CONFIGRET WINAPI CM_Unregister_Notification(HCMNOTIFICATION); CMAPI CONFIGRET WINAPI CM_Request_Device_EjectA(DEVINST dev, PPNP_VETO_TYPE type, LPSTR name, ULONG length, ULONG flags); CMAPI CONFIGRET WINAPI CM_Request_Device_EjectW(DEVINST dev, PPNP_VETO_TYPE type, LPWSTR name, ULONG length, ULONG flags); #define CM_Request_Device_Eject WINELIB_NAME_AW(CM_Get_Device_ID_List_Ex)
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/tests/cfgmgr32.c | 38 ++++++++++++++++++++++++++++++++++ include/cfgmgr32.h | 1 + 2 files changed, 39 insertions(+)
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 1e949093689..b4942e1b26a 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -282,8 +282,46 @@ static void test_CM_Get_Device_ID_List(void) free(buf); }
+DWORD WINAPI notify_callback( HCMNOTIFICATION notify, void *ctx, CM_NOTIFY_ACTION action, + CM_NOTIFY_EVENT_DATA *data, DWORD size ) +{ + return ERROR_SUCCESS; +} + +static void test_CM_Register_Notification( void ) +{ + CM_NOTIFY_FILTER filter = {0}; + HCMNOTIFICATION notify; + CONFIGRET ret; + + filter.cbSize = sizeof( CM_NOTIFY_FILTER ); + filter.Flags = CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES; + filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE; + + ret = CM_Register_Notification( NULL, NULL, NULL, NULL ); + todo_wine ok( ret == CR_FAILURE, "Expected 0x13, got %#lx.\n", ret ); + + ret = CM_Register_Notification( NULL, NULL, NULL, ¬ify ); + todo_wine ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret ); + + ret = CM_Register_Notification( &filter, NULL, NULL, ¬ify ); + todo_wine ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret ); + + filter.cbSize += 1; + ret = CM_Register_Notification( &filter, NULL, notify_callback, ¬ify ); + todo_wine ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret ); + + filter.cbSize -= 1; + ret = CM_Register_Notification( &filter, NULL, notify_callback, ¬ify ); + todo_wine ok( !ret, "Expected 0, got %#lx.\n", ret ); + + ret = CM_Unregister_Notification( notify ); + todo_wine ok( !ret, "Expected 0, got %#lx.\n", ret ); +} + START_TEST(cfgmgr32) { test_CM_MapCrToWin32Err(); test_CM_Get_Device_ID_List(); + test_CM_Register_Notification(); } diff --git a/include/cfgmgr32.h b/include/cfgmgr32.h index 269aec9873c..3e814757e2b 100644 --- a/include/cfgmgr32.h +++ b/include/cfgmgr32.h @@ -204,6 +204,7 @@ typedef CHAR *DEVNODEID_A, *DEVINSTID_A; typedef WCHAR *DEVNODEID_W, *DEVINSTID_W; typedef ULONG REGDISPOSITION;
+#define CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES 0x0001 typedef enum _CM_NOTIFY_FILTER_TYPE { CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE,
From: Vibhav Pant vibhavp@gmail.com
--- dlls/dinput/tests/Makefile.in | 2 +- dlls/dinput/tests/hotplug.c | 183 ++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/tests/Makefile.in b/dlls/dinput/tests/Makefile.in index 608b817e0f4..c40300e619b 100644 --- a/dlls/dinput/tests/Makefile.in +++ b/dlls/dinput/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = dinput.dll -IMPORTS = dinput dinput8 ole32 version user32 advapi32 hid uuid crypt32 newdev setupapi wintrust winmm +IMPORTS = dinput dinput8 ole32 version user32 advapi32 hid uuid crypt32 newdev setupapi wintrust winmm cfgmgr32
driver_bus_IMPORTS = winecrt0 ntoskrnl hal driver_bus_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c index f7199d2b17e..a6855f2e4d9 100644 --- a/dlls/dinput/tests/hotplug.c +++ b/dlls/dinput/tests/hotplug.c @@ -34,6 +34,7 @@ #include "dbt.h" #include "unknwn.h" #include "winstring.h" +#include "cfgmgr32.h"
#include "wine/hid.h"
@@ -279,6 +280,7 @@ static const union device_change_event static const union device_change_event *device_change_expect_event; static HANDLE device_change_expect_handle; static HDEVNOTIFY handle_devnotify; +static HCMNOTIFICATION handle_cmnotify;
static LRESULT CALLBACK devnotify_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { @@ -365,6 +367,102 @@ static LRESULT CALLBACK devnotify_wndproc( HWND hwnd, UINT msg, WPARAM wparam, L return DefWindowProcW( hwnd, msg, wparam, lparam ); }
+struct cm_notify_callback_data +{ + HCMNOTIFICATION hnotify; + DWORD device_change_expect; + DWORD device_change_expect_custom; + BOOL device_change_all; + + DWORD device_change_count; + HANDLE device_change_sem; +}; + +static CALLBACK DWORD cm_notify_callback( HCMNOTIFICATION hnotify, void *ctx, CM_NOTIFY_ACTION action, + CM_NOTIFY_EVENT_DATA *data, DWORD size ) +{ + struct cm_notify_callback_data *cb_data = ctx; + + switch (action) + { + case CM_NOTIFY_ACTION_DEVICECUSTOMEVENT: + { + const union device_change_event *event; + SIZE_T idx; + + ok( hnotify == handle_cmnotify, "%p != %p\n", hnotify, handle_cmnotify ); + ok( !!cb_data->device_change_expect_custom, "unexpected event\n" ); + idx = (ARRAY_SIZE(device_change_events) + 1) - cb_data->device_change_expect_custom; + event = &device_change_events[idx]; + + winetest_push_context("%Id", idx); + ok( IsEqualGUID( &data->u.DeviceHandle.EventGuid, &event->Event ), "got DeviceHandle.EventGuid %s\n", + debugstr_guid( &data->u.DeviceHandle.EventGuid )); + ok( data->u.DeviceHandle.NameOffset == event->NameBufferOffset, "got DeviceHandle.NameOffset %ld\n", + data->u.DeviceHandle.NameOffset ); + ok( data->u.DeviceHandle.DataSize - 2 * sizeof(WCHAR) == + event->Size - offsetof(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer[0]), + "got DeviceHandle.DataSize %ld\n", data->u.DeviceHandle.DataSize ); + ok( !memcmp( data->u.DeviceHandle.Data, event->CustomDataBuffer, data->u.DeviceHandle.DataSize ), + "Unexpected DeviceHandle.Data\n" ); + winetest_pop_context(); + ok( ReleaseSemaphore( cb_data->device_change_sem, 1, NULL ), "ReleaseSemaphore failed, error %lu\n", + GetLastError() ); + cb_data->device_change_expect_custom--; + cb_data->device_change_expect++; + break; + } + case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL: + case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL: + { + GUID expect_guid; + const WCHAR *expect_prefix, *name, *upper_end, *name_end; + + ok( hnotify == cb_data->hnotify, "%p != %p\n", hnotify, cb_data->hnotify ); + name = data->u.DeviceInterface.SymbolicLink; + if (cb_data->device_change_all && (!cb_data->device_change_count || cb_data->device_change_count == 3)) + { + expect_guid = control_class; + expect_prefix = L"\\?\WINETEST#"; + } + else + { + expect_guid = GUID_DEVINTERFACE_HID; + expect_prefix = L"\\?\HID#"; + } + + winetest_push_context( "%lu", cb_data->device_change_count ); + ok( IsEqualGUID( &data->u.DeviceInterface.ClassGuid, &expect_guid ), + "got u.DeviceInterface.ClassGuid %s\n", debugstr_guid( &data->u.DeviceInterface.ClassGuid )); + ok( !wcsncmp( name, expect_prefix, wcslen( expect_prefix ) ), "got u.DeviceInterface.SymbolicLink %s\n", + debugstr_w( name )); + upper_end = wcschr( name + wcslen( expect_prefix ), '#' ); + name_end = name + wcslen( name ) + 1; + ok( !!upper_end, "got u.DeviceInterface.SymbolicLink %s\n", debugstr_w( name ) ); + ok( all_upper( name, upper_end ), "got u.DeviceInterface.SymbolicLink %s\n", debugstr_w( name ) ); + ok( all_lower( upper_end, name_end ), "got u.DeviceInterface.SymbolicLink %s\n", debugstr_w( name ) ); + + if (cb_data->device_change_count++ >= cb_data->device_change_expect / 2) + ok( action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL, "got action %d\n", action ); + else + { + CM_NOTIFY_ACTION exp = cb_data->device_change_expect_custom ? CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL + : CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL; + ok( action == exp, "got action %d\n", action ); + } + + winetest_pop_context(); + ok( ReleaseSemaphore( cb_data->device_change_sem, 1, NULL ), "ReleaseSemaphore failed, error %lu\n", + GetLastError() ); + break; + } + default: + ok( FALSE, "Unexpected CM_NOTIFY_ACTION %d\n", action ); + } + + return ERROR_SUCCESS; +} + static void test_RegisterDeviceNotification(void) { DEV_BROADCAST_DEVICEINTERFACE_A iface_filter_a = @@ -385,10 +483,26 @@ static void test_RegisterDeviceNotification(void) .lpszClassName = L"devnotify", .lpfnWndProc = devnotify_wndproc, }; + CM_NOTIFY_FILTER cm_iface_filter = + { + .cbSize = sizeof(CM_NOTIFY_FILTER), + .Flags = 0, + .FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE, + .Reserved = 0, + .u = {.DeviceInterface = {.ClassGuid = GUID_DEVINTERFACE_HID}} + }; + CM_NOTIFY_FILTER cm_handle_filter = + { + .cbSize = sizeof(CM_NOTIFY_FILTER), + .Flags = 0, + .FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE, + .Reserved = 0, + }; char buffer[1024] = {0}; DEV_BROADCAST_HDR *header = (DEV_BROADCAST_HDR *)buffer; HANDLE hwnd, thread, stop_event; HDEVNOTIFY devnotify; + struct cm_notify_callback_data notify_ctx = {0}; DWORD ret; MSG msg;
@@ -461,6 +575,16 @@ static void test_RegisterDeviceNotification(void) ok( !!devnotify, "RegisterDeviceNotificationA failed, error %lu\n", GetLastError() ); while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) DispatchMessageW( &msg );
+ notify_ctx.device_change_sem = CreateSemaphoreA( NULL, 0, 6, NULL ); + ok( !!notify_ctx.device_change_sem, "CreateSemaphoreA failed, error %lu\n", GetLastError() ); + + notify_ctx.device_change_all = FALSE; + notify_ctx.device_change_expect = 2; + notify_ctx.device_change_count = 0; + + ret = CM_Register_Notification( &cm_iface_filter, ¬ify_ctx, cm_notify_callback, ¬ify_ctx.hnotify ); + todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + device_change_count = 0; device_change_expect = 2; device_change_hwnd = hwnd; @@ -482,7 +606,14 @@ static void test_RegisterDeviceNotification(void) DispatchMessageW( &msg ); } if (device_change_count == device_change_expect / 2) SetEvent( stop_event ); + if (notify_ctx.hnotify) + { + ret = WaitForSingleObject( notify_ctx.device_change_sem, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + } } + todo_wine ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", + notify_ctx.device_change_count, notify_ctx.device_change_expect );
ret = WaitForSingleObject( thread, 5000 ); ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); @@ -490,6 +621,7 @@ static void test_RegisterDeviceNotification(void) CloseHandle( stop_event );
UnregisterDeviceNotification( devnotify ); + CM_Unregister_Notification( notify_ctx.hnotify );
memcpy( buffer, &iface_filter_a, sizeof(iface_filter_a) ); strcpy( ((DEV_BROADCAST_DEVICEINTERFACE_A *)buffer)->dbcc_name, "device name" ); @@ -498,6 +630,13 @@ static void test_RegisterDeviceNotification(void) ok( !!devnotify, "RegisterDeviceNotificationA failed, error %lu\n", GetLastError() ); while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) DispatchMessageW( &msg );
+ notify_ctx.device_change_all = FALSE; + notify_ctx.device_change_expect = 2; + notify_ctx.device_change_count = 0; + + ret = CM_Register_Notification( &cm_iface_filter, ¬ify_ctx, cm_notify_callback, ¬ify_ctx.hnotify ); + todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + device_change_count = 0; device_change_expect = 2; device_change_hwnd = hwnd; @@ -519,7 +658,14 @@ static void test_RegisterDeviceNotification(void) DispatchMessageW( &msg ); } if (device_change_count == device_change_expect / 2) SetEvent( stop_event ); + if (notify_ctx.hnotify) + { + ret = WaitForSingleObject( notify_ctx.device_change_sem, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + } } + todo_wine ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", + notify_ctx.device_change_count, notify_ctx.device_change_expect );
ret = WaitForSingleObject( thread, 5000 ); ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); @@ -527,11 +673,19 @@ static void test_RegisterDeviceNotification(void) CloseHandle( stop_event );
UnregisterDeviceNotification( devnotify ); + CM_Unregister_Notification( notify_ctx.hnotify );
devnotify = RegisterDeviceNotificationA( hwnd, &iface_filter_a, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES ); ok( !!devnotify, "RegisterDeviceNotificationA failed, error %lu\n", GetLastError() ); while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) DispatchMessageW( &msg );
+ notify_ctx.device_change_all = TRUE; + notify_ctx.device_change_expect = 4; + notify_ctx.device_change_count = 0; + cm_iface_filter.Flags |= CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES; + ret = CM_Register_Notification( &cm_iface_filter, ¬ify_ctx, cm_notify_callback, ¬ify_ctx.hnotify ); + todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + device_change_count = 0; device_change_expect = 4; device_change_hwnd = hwnd; @@ -553,7 +707,14 @@ static void test_RegisterDeviceNotification(void) DispatchMessageW( &msg ); } if (device_change_count == device_change_expect / 2) SetEvent( stop_event ); + if (notify_ctx.hnotify) + { + ret = WaitForSingleObject( notify_ctx.device_change_sem, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + } } + todo_wine ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", + notify_ctx.device_change_count, notify_ctx.device_change_expect );
ret = WaitForSingleObject( thread, 5000 ); ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); @@ -561,11 +722,19 @@ static void test_RegisterDeviceNotification(void) CloseHandle( stop_event );
UnregisterDeviceNotification( devnotify ); + CM_Unregister_Notification( notify_ctx.hnotify );
devnotify = RegisterDeviceNotificationA( hwnd, &iface_filter_a, DEVICE_NOTIFY_WINDOW_HANDLE ); ok( !!devnotify, "RegisterDeviceNotificationA failed, error %lu\n", GetLastError() ); while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) DispatchMessageW( &msg );
+ notify_ctx.device_change_all = FALSE; + notify_ctx.device_change_expect = 2; + notify_ctx.device_change_count = 0; + cm_iface_filter.Flags = 0; + ret = CM_Register_Notification( &cm_iface_filter, ¬ify_ctx, cm_notify_callback, ¬ify_ctx.hnotify ); + todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + device_change_count = 0; device_change_expect = 2; device_change_hwnd = hwnd; @@ -586,6 +755,12 @@ static void test_RegisterDeviceNotification(void) ok( msg.message != WM_DEVICECHANGE, "got WM_DEVICECHANGE\n" ); DispatchMessageW( &msg ); } + if (notify_ctx.hnotify) + { + ret = WaitForSingleObject( notify_ctx.device_change_sem, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + } + if (device_change_count == 1 && !device_change_expect_event) { WCHAR device_path[MAX_PATH]; @@ -604,8 +779,13 @@ static void test_RegisterDeviceNotification(void) handle_devnotify = RegisterDeviceNotificationA( hwnd, &handle_filter_a, DEVICE_NOTIFY_WINDOW_HANDLE ); ok( !!handle_devnotify, "RegisterDeviceNotificationA failed, error %lu\n", GetLastError() );
+ cm_handle_filter.u.DeviceHandle.hTarget = file; + ret = CM_Register_Notification( &cm_handle_filter, ¬ify_ctx, cm_notify_callback, &handle_cmnotify ); + todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + device_change_expect_handle = file; device_change_expect_event = device_change_events; + notify_ctx.device_change_expect_custom = ARRAY_SIZE(device_change_events) + 1; for (i = 0; i < ARRAY_SIZE(device_change_events) - 1; i++) { ret = sync_ioctl( file, IOCTL_WINETEST_DEVICE_CHANGE, (BYTE *)&device_change_events[i].notif, @@ -626,9 +806,12 @@ static void test_RegisterDeviceNotification(void) CloseHandle( stop_event );
if (handle_devnotify) UnregisterDeviceNotification( handle_devnotify ); + if (handle_cmnotify) CM_Unregister_Notification( handle_cmnotify ); UnregisterDeviceNotification( devnotify ); + CM_Unregister_Notification( notify_ctx.hnotify ); device_change_expect_event = NULL; handle_devnotify = 0; + CloseHandle( notify_ctx.device_change_sem );
DestroyWindow( hwnd ); UnregisterClassW( class.lpszClassName, class.hInstance );
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/Makefile.in | 2 +- dlls/cfgmgr32/main.c | 177 ++++++++++++++++++++++++++++++++- dlls/cfgmgr32/tests/cfgmgr32.c | 12 +-- dlls/dinput/tests/hotplug.c | 22 ++-- 4 files changed, 191 insertions(+), 22 deletions(-)
diff --git a/dlls/cfgmgr32/Makefile.in b/dlls/cfgmgr32/Makefile.in index 1ff06a63b5f..e8d9fecdcdd 100644 --- a/dlls/cfgmgr32/Makefile.in +++ b/dlls/cfgmgr32/Makefile.in @@ -1,6 +1,6 @@ MODULE = cfgmgr32.dll IMPORTLIB = cfgmgr32 -IMPORTS = setupapi +IMPORTS = setupapi sechost
SOURCES = \ main.c diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 3206cd2ed7a..5fcb6e07552 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2023 Mohamad Al-Jaf + * Copyright (C) 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,6 +20,9 @@ #include "wine/debug.h" #include "winreg.h" #include "cfgmgr32.h" +#include "winuser.h" +#include "dbt.h" +#include "wine/plugplay.h"
WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
@@ -58,15 +62,172 @@ DWORD WINAPI CM_MapCrToWin32Err( CONFIGRET code, DWORD default_error ) return default_error; }
+struct cm_notify_context +{ + HDEVNOTIFY notify; + void *user_data; + PCM_NOTIFY_CALLBACK callback; +}; + +CALLBACK DWORD devnotify_callback( HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header ) +{ + struct cm_notify_context *ctx = handle; + CM_NOTIFY_EVENT_DATA *event_data; + CM_NOTIFY_ACTION action; + DWORD size, ret; + + TRACE( "(%p, %#lx, %p)\n", handle, flags, header ); + + switch (flags) + { + case DBT_DEVICEARRIVAL: + action = CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL; + break; + case DBT_DEVICEREMOVECOMPLETE: + action = CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL; + break; + case DBT_CUSTOMEVENT: + action = CM_NOTIFY_ACTION_DEVICECUSTOMEVENT; + break; + default: + FIXME( "Unexpected flags value: %#lx\n", flags ); + return 0; + } + + switch (header->dbch_devicetype) + { + case DBT_DEVTYP_DEVICEINTERFACE: + { + const DEV_BROADCAST_DEVICEINTERFACE_W *iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)header; + + size = wcslen( iface->dbcc_name ); + event_data = + calloc( 1, offsetof( CM_NOTIFY_EVENT_DATA, u.DeviceInterface.SymbolicLink[size + 1] ) ); + if (!event_data) + return 0; + + event_data->FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE; + event_data->u.DeviceInterface.ClassGuid = iface->dbcc_classguid; + wcscpy( event_data->u.DeviceInterface.SymbolicLink, iface->dbcc_name ); + break; + } + case DBT_DEVTYP_HANDLE: + { + const DEV_BROADCAST_HANDLE *handle = (DEV_BROADCAST_HANDLE *)header; + + size = handle->dbch_size - offsetof( DEV_BROADCAST_HANDLE, dbch_data ); + event_data = calloc( 1, offsetof( CM_NOTIFY_EVENT_DATA, u.DeviceHandle.Data[size] ) ); + if (!event_data) + return 0; + + event_data->FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE; + event_data->u.DeviceHandle.EventGuid = handle->dbch_eventguid; + event_data->u.DeviceHandle.NameOffset = handle->dbch_nameoffset; + event_data->u.DeviceHandle.DataSize = size; + memcpy( event_data->u.DeviceHandle.Data, handle->dbch_data, size ); + break; + } + default: + FIXME( "Unexpected devicetype value: %#lx\n", header->dbch_devicetype ); + return 0; + } + + ret = ctx->callback( ctx, ctx->user_data, action, event_data, size ); + free( event_data ); + return ret; +} + +static const char *debugstr_CM_NOTIFY_FILTER( const CM_NOTIFY_FILTER *filter ) +{ + switch (filter->FilterType) + { + case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE: + return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE %lu {{%s}}}", filter->cbSize, + filter->Flags, filter->Reserved, + debugstr_guid( &filter->u.DeviceInterface.ClassGuid ) ); + case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE: + return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%p}}}", filter->cbSize, + filter->Flags, filter->Reserved, filter->u.DeviceHandle.hTarget ); + case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE: + return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE %lu {{%s}}}", filter->cbSize, + filter->Flags, filter->Reserved, debugstr_w( filter->u.DeviceInstance.InstanceId ) ); + default: + return wine_dbg_sprintf( "{%#lx %lx (unknown FilterType %d) %lu}", filter->cbSize, filter->Flags, + filter->FilterType, filter->Reserved ); + } +} + +static CONFIGRET create_notify_context( const CM_NOTIFY_FILTER *filter, HCMNOTIFICATION *notify_handle, + PCM_NOTIFY_CALLBACK callback, void *user_data ) +{ + union { + DEV_BROADCAST_HDR header; + DEV_BROADCAST_DEVICEINTERFACE_W iface; + DEV_BROADCAST_HANDLE handle; + } notify_filter = {0}; + struct cm_notify_context *ctx; + + switch (filter->FilterType) + { + case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE: + notify_filter.iface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + if (filter->Flags & CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES) + notify_filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_classguid ); + else + { + notify_filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name ); + notify_filter.iface.dbcc_classguid = filter->u.DeviceInterface.ClassGuid; + } + break; + case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE: + notify_filter.handle.dbch_devicetype = DBT_DEVTYP_HANDLE; + notify_filter.handle.dbch_size = sizeof( notify_filter.handle ); + notify_filter.handle.dbch_handle = filter->u.DeviceHandle.hTarget; + break; + case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE: + FIXME( "CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE is not supported!\n" ); + return CR_CALL_NOT_IMPLEMENTED; + default: + return CR_INVALID_DATA; + } + + if (!(ctx = calloc( 1, sizeof( *ctx ) ))) + return CR_OUT_OF_MEMORY; + + ctx->user_data = user_data; + ctx->callback = callback; + if (!(ctx->notify = I_ScRegisterDeviceNotification( ctx, ¬ify_filter.header, devnotify_callback ))) + { + free( ctx ); + switch (GetLastError()) + { + case ERROR_NOT_ENOUGH_MEMORY: + return CR_OUT_OF_MEMORY; + case ERROR_INVALID_PARAMETER: + return CR_INVALID_DATA; + default: + return CR_FAILURE; + } + } + *notify_handle = ctx; + return CR_SUCCESS; +} + /*********************************************************************** * CM_Register_Notification (cfgmgr32.@) */ CONFIGRET WINAPI CM_Register_Notification( CM_NOTIFY_FILTER *filter, void *context, PCM_NOTIFY_CALLBACK callback, HCMNOTIFICATION *notify_context ) { - FIXME("%p %p %p %p stub!\n", filter, context, callback, notify_context); + TRACE( "(%s %p %p %p)\n", debugstr_CM_NOTIFY_FILTER( filter ), context, callback, notify_context );
- return CR_CALL_NOT_IMPLEMENTED; + if (!notify_context) + return CR_FAILURE; + + if (!filter || !callback || filter->cbSize != sizeof( *filter )) + return CR_INVALID_DATA; + + return create_notify_context( filter, notify_context, callback, context ); }
/*********************************************************************** @@ -74,9 +235,17 @@ CONFIGRET WINAPI CM_Register_Notification( CM_NOTIFY_FILTER *filter, void *conte */ CONFIGRET WINAPI CM_Unregister_Notification( HCMNOTIFICATION notify ) { - FIXME( "(%p) stub!\n", notify ); + struct cm_notify_context *ctx = notify;
- return CR_CALL_NOT_IMPLEMENTED; + TRACE( "(%p)\n", notify ); + + if (!notify) + return CR_INVALID_DATA; + + I_ScUnregisterDeviceNotification( ctx->notify ); + free( ctx ); + + return CR_SUCCESS; }
/*********************************************************************** diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index b4942e1b26a..d843f5cccf3 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -299,24 +299,24 @@ static void test_CM_Register_Notification( void ) filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
ret = CM_Register_Notification( NULL, NULL, NULL, NULL ); - todo_wine ok( ret == CR_FAILURE, "Expected 0x13, got %#lx.\n", ret ); + ok( ret == CR_FAILURE, "Expected 0x13, got %#lx.\n", ret );
ret = CM_Register_Notification( NULL, NULL, NULL, ¬ify ); - todo_wine ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret ); + ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret );
ret = CM_Register_Notification( &filter, NULL, NULL, ¬ify ); - todo_wine ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret ); + ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret );
filter.cbSize += 1; ret = CM_Register_Notification( &filter, NULL, notify_callback, ¬ify ); - todo_wine ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret ); + ok( ret == CR_INVALID_DATA, "Expected 0x1f, got %#lx.\n", ret );
filter.cbSize -= 1; ret = CM_Register_Notification( &filter, NULL, notify_callback, ¬ify ); - todo_wine ok( !ret, "Expected 0, got %#lx.\n", ret ); + ok( !ret, "Expected 0, got %#lx.\n", ret );
ret = CM_Unregister_Notification( notify ); - todo_wine ok( !ret, "Expected 0, got %#lx.\n", ret ); + ok( !ret, "Expected 0, got %#lx.\n", ret ); }
START_TEST(cfgmgr32) diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c index a6855f2e4d9..04ef08c715c 100644 --- a/dlls/dinput/tests/hotplug.c +++ b/dlls/dinput/tests/hotplug.c @@ -583,7 +583,7 @@ static void test_RegisterDeviceNotification(void) notify_ctx.device_change_count = 0;
ret = CM_Register_Notification( &cm_iface_filter, ¬ify_ctx, cm_notify_callback, ¬ify_ctx.hnotify ); - todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + ok( !ret, "CM_Register_Notification failed, error %lu\n", ret );
device_change_count = 0; device_change_expect = 2; @@ -612,8 +612,8 @@ static void test_RegisterDeviceNotification(void) ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); } } - todo_wine ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", - notify_ctx.device_change_count, notify_ctx.device_change_expect ); + ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", + notify_ctx.device_change_count, notify_ctx.device_change_expect );
ret = WaitForSingleObject( thread, 5000 ); ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); @@ -635,7 +635,7 @@ static void test_RegisterDeviceNotification(void) notify_ctx.device_change_count = 0;
ret = CM_Register_Notification( &cm_iface_filter, ¬ify_ctx, cm_notify_callback, ¬ify_ctx.hnotify ); - todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + ok( !ret, "CM_Register_Notification failed, error %lu\n", ret );
device_change_count = 0; device_change_expect = 2; @@ -664,8 +664,8 @@ static void test_RegisterDeviceNotification(void) ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); } } - todo_wine ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", - notify_ctx.device_change_count, notify_ctx.device_change_expect ); + ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", + notify_ctx.device_change_count, notify_ctx.device_change_expect );
ret = WaitForSingleObject( thread, 5000 ); ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); @@ -684,7 +684,7 @@ static void test_RegisterDeviceNotification(void) notify_ctx.device_change_count = 0; cm_iface_filter.Flags |= CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES; ret = CM_Register_Notification( &cm_iface_filter, ¬ify_ctx, cm_notify_callback, ¬ify_ctx.hnotify ); - todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + ok( !ret, "CM_Register_Notification failed, error %lu\n", ret );
device_change_count = 0; device_change_expect = 4; @@ -713,8 +713,8 @@ static void test_RegisterDeviceNotification(void) ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); } } - todo_wine ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", - notify_ctx.device_change_count, notify_ctx.device_change_expect ); + ok( notify_ctx.device_change_count == notify_ctx.device_change_expect, "%lu != %lu\n", + notify_ctx.device_change_count, notify_ctx.device_change_expect );
ret = WaitForSingleObject( thread, 5000 ); ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); @@ -733,7 +733,7 @@ static void test_RegisterDeviceNotification(void) notify_ctx.device_change_count = 0; cm_iface_filter.Flags = 0; ret = CM_Register_Notification( &cm_iface_filter, ¬ify_ctx, cm_notify_callback, ¬ify_ctx.hnotify ); - todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + ok( !ret, "CM_Register_Notification failed, error %lu\n", ret );
device_change_count = 0; device_change_expect = 2; @@ -781,7 +781,7 @@ static void test_RegisterDeviceNotification(void)
cm_handle_filter.u.DeviceHandle.hTarget = file; ret = CM_Register_Notification( &cm_handle_filter, ¬ify_ctx, cm_notify_callback, &handle_cmnotify ); - todo_wine ok( !ret, "CM_Register_Notification failed, error %lu\n", ret ); + ok( !ret, "CM_Register_Notification failed, error %lu\n", ret );
device_change_expect_handle = file; device_change_expect_event = device_change_events;
From: Vibhav Pant vibhavp@gmail.com
--- dlls/user32/input.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 9e87d6664f5..d54d6abc0ab 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -577,10 +577,7 @@ HDEVNOTIFY WINAPI RegisterDeviceNotificationW( HANDLE handle, void *filter, DWOR return I_ScRegisterDeviceNotification( handle, (DEV_BROADCAST_HDR *)&iface, callback ); } if (header->dbch_devicetype == DBT_DEVTYP_HANDLE) - { - FIXME( "DBT_DEVTYP_HANDLE not implemented\n" ); return I_ScRegisterDeviceNotification( handle, header, callback ); - }
FIXME( "type %#lx not implemented\n", header->dbch_devicetype ); SetLastError( ERROR_INVALID_DATA );
Added another small commit that removes the incorrect `FIXME` in `RegisterDeviceNotificationW` for `DBT_DEVTYP_HANDLE` notifications.
On Mon Mar 17 12:58:50 2025 +0000, Rémi Bernon wrote:
Fwiw 1b714109854f9a4dd2eeec867c5025a5b4d9fa9b also temporarily breaks the tests on Linux, all tests are supposed to pass on every commit so that bisecting is possible.
cfgmgr32:cfgmgr32:0110 done (0) in 0s 488B dinput:device8:0118 done (0) in 5s 10654B dinput:dinput:0170 done (0) in 0s 2524B dinput:force_feedback:0184 done (0) in 1s 7897B dinput:hid:01d4 done (0) in 5s 2893B hotplug.c:600: Test failed: MsgWaitForMultipleObjects returned 0x102 hotplug.c:635: Test failed: CM_Register_Notification failed, error 52 hotplug.c:659: Test failed: WaitForSingleObject returned 0x102 hotplug.c:649: Test failed: MsgWaitForMultipleObjects returned 0x102 hotplug.c:661: Test failed: 0 != 2 hotplug.c:681: Test failed: CM_Register_Notification failed, error 52 hotplug.c:705: Test failed: WaitForSingleObject returned 0x102 hid.c:3802: Test failed: WaitForSingleObject returned 0x102 hotplug.c:362: Test failed: 1: got wparam 0x8004 hotplug.c:705: Test failed: WaitForSingleObject returned 0x102 hotplug.c:695: Test failed: MsgWaitForMultipleObjects returned 0x102 hotplug.c:707: Test failed: 0 != 4 hotplug.c:727: Test failed: CM_Register_Notification failed, error 52 hid.c:3802: Test failed: WaitForSingleObject returned 0x102 hotplug.c:750: Test failed: WaitForSingleObject returned 0x102 hotplug.c:760: Test failed: Failed to find HID device matching L"\\\\?\\hid#vid_1209&pid_0001" hotplug.c:764: Test failed: got error 2 hotplug.c:772: Test failed: CM_Register_Notification failed, error 52 hotplug.c:781: Test failed: IOCTL_WINETEST_DEVICE_CHANGE failed, last error 6 hotplug.c:781: Test failed: IOCTL_WINETEST_DEVICE_CHANGE failed, last error 6 hotplug.c:781: Test failed: IOCTL_WINETEST_DEVICE_CHANGE failed, last error 6 hotplug.c:781: Test failed: IOCTL_WINETEST_DEVICE_CHANGE failed, last error 6 hotplug.c:741: Test failed: MsgWaitForMultipleObjects returned 0x102 0208:hotplug: 614 tests executed (24 marked as todo, 0 as flaky, 23 failures), 0 skipped. dinput:hotplug:0208 done (23) in 48s 4594B dinput:joystick8:02ac done (0) in 5s 17980B
The tests were missing a couple of `todo_wines`, and the semaphore wait timing out was messing with `MsgWaitForMultipleObjects`' timing. Should be fixed now.