[PATCH 3/3] dinput/tests: Add some RegisterDeviceNotificationA and hotplug tests.

Rémi Bernon rbernon at codeweavers.com
Tue Jan 25 03:55:50 CST 2022


Signed-off-by: Rémi Bernon <rbernon at 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();
 
-- 
2.34.1




More information about the wine-devel mailing list