[PATCH 1/5] dinput8/tests: Add some RegisterDeviceNotificationA and hotplug tests.

Rémi Bernon rbernon at codeweavers.com
Fri Dec 24 08:49:09 CST 2021


Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/dinput8/tests/hid.c | 388 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 388 insertions(+)

diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c
index 3b88dc4ea21..bd51d27580a 100644
--- a/dlls/dinput8/tests/hid.c
+++ b/dlls/dinput8/tests/hid.c
@@ -44,6 +44,7 @@
 
 #define COBJMACROS
 #include "wingdi.h"
+#include "dbt.h"
 #include "dinput.h"
 #include "dinputd.h"
 #include "mmsystem.h"
@@ -9964,6 +9965,391 @@ done:
     SetCurrentDirectoryW( cwd );
 }
 
+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 DWORD WINAPI test_gamepad_driver_thread( void *arg )
+{
+#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;
+    HANDLE stop_event = arg;
+    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;
+}
+
+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, "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, "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, "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, "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, "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() );
+
+    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, test_gamepad_driver_thread, stop_event, 0, NULL );
+    ok( !!thread, "CreateThread failed, error %u\n", GetLastError() );
+
+    while (device_change_count < device_change_expect)
+    {
+        /* This may happen on a newly created prefix, if rpcss.exe started late and plugplay didn't connect */
+        if (MsgWaitForMultipleObjectsEx( 0, NULL, 1000, QS_ALLINPUT, 0 ) == WAIT_TIMEOUT)
+        {
+            todo_wine
+            ok( 0, "Timeout while waiting for WM_DEVICECHANGE message.\n" );
+            SetEvent( stop_event );
+            break;
+        }
+
+        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() );
+
+    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, test_gamepad_driver_thread, stop_event, 0, NULL );
+    ok( !!thread, "CreateThread failed, error %u\n", GetLastError() );
+
+    while (device_change_count < device_change_expect)
+    {
+        /* This may happen on a newly created prefix, if rpcss.exe started late and plugplay didn't connect */
+        if (MsgWaitForMultipleObjectsEx( 0, NULL, 1000, QS_ALLINPUT, 0 ) == WAIT_TIMEOUT)
+        {
+            todo_wine
+            ok( 0, "Timeout while waiting for WM_DEVICECHANGE message.\n" );
+            SetEvent( stop_event );
+            break;
+        }
+
+        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() );
+
+    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, test_gamepad_driver_thread, stop_event, 0, NULL );
+    ok( !!thread, "CreateThread failed, error %u\n", GetLastError() );
+
+    while (device_change_count < device_change_expect)
+    {
+        /* This may happen on a newly created prefix, if rpcss.exe started late and plugplay didn't connect */
+        if (MsgWaitForMultipleObjectsEx( 0, NULL, 1000, QS_ALLINPUT, 0 ) == WAIT_TIMEOUT)
+        {
+            todo_wine
+            ok( 0, "Timeout while waiting for WM_DEVICECHANGE message.\n" );
+            SetEvent( stop_event );
+            break;
+        }
+
+        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( hid )
 {
     HANDLE mapping;
@@ -10003,6 +10389,8 @@ START_TEST( hid )
     test_hid_driver( 0, TRUE );
     test_hid_driver( 1, TRUE );
 
+    test_RegisterDeviceNotification();
+
     CoInitialize( NULL );
     if (test_device_types( 0x800 ))
     {
-- 
2.34.1




More information about the wine-devel mailing list