From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/tests/dinput_test.h | 6 + dlls/dinput/tests/driver_bus.c | 32 ++- dlls/dinput/tests/hid.c | 115 +++++++-- dlls/dinput/tests/hotplug.c | 19 +- dlls/dinput/tests/joystick8.c | 442 ++++++++++++++++++++++++++++++++ 5 files changed, 594 insertions(+), 20 deletions(-) diff --git a/dlls/dinput/tests/dinput_test.h b/dlls/dinput/tests/dinput_test.h index d19e1868d76..58030710310 100644 --- a/dlls/dinput/tests/dinput_test.h +++ b/dlls/dinput/tests/dinput_test.h @@ -64,9 +64,15 @@ void cleanup_registry_keys(void); void dinput_test_init_( const char *file, int line ); void dinput_test_exit(void); +HRESULT dinput_test_create_device_instance( DWORD version, const GUID *guid_inst, IDirectInputDevice8W **device ); HRESULT dinput_test_create_device( DWORD version, DIDEVICEINSTANCEW *devinst, IDirectInputDevice8W **device ); DWORD WINAPI dinput_test_device_thread( void *stop_event ); +#define dinput_test_get_device_hid_serial_string( a, b ) \ + dinput_test_get_device_hid_serial_string_( __FILE__, __LINE__, a, b ) +void dinput_test_get_device_hid_serial_string_( const char *file, int line, IDirectInputDevice8W *device, + WCHAR *serial_out ); + #define fill_context( a, b ) fill_context_( __FILE__, __LINE__, a, b ) void fill_context_( const char *file, int line, char *buffer, SIZE_T size ); diff --git a/dlls/dinput/tests/driver_bus.c b/dlls/dinput/tests/driver_bus.c index 780db34ab62..cf10e04e71f 100644 --- a/dlls/dinput/tests/driver_bus.c +++ b/dlls/dinput/tests/driver_bus.c @@ -1250,10 +1250,36 @@ static NTSTATUS pdo_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) } case IOCTL_HID_GET_STRING: - memcpy( irp->UserBuffer, L"Wine Test", sizeof(L"Wine Test") ); - irp->IoStatus.Information = sizeof(L"Wine Test"); - status = STATUS_SUCCESS; + { + UINT index = (UINT_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer & 0xffff; + UINT lcid = ((UINT_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer) >> 16; + + todo_wine ok( lcid, "Unexpected LCID %#x.\n", lcid ); + if (winetest_debug > 1) + trace( "%s: device %p, code %#lx %s, lcid %#x, idx %#x.\n", __func__, device, code, debugstr_ioctl(code), + lcid, index ); + switch (index) + { + case HID_STRING_ID_IMANUFACTURER: + case HID_STRING_ID_IPRODUCT: + memcpy( irp->UserBuffer, L"Wine Test", sizeof(L"Wine Test") ); + irp->IoStatus.Information = sizeof(L"Wine Test"); + status = STATUS_SUCCESS; + break; + + case HID_STRING_ID_ISERIALNUMBER: + wcscpy( irp->UserBuffer, impl->instance_id ); + irp->IoStatus.Information = (wcslen(impl->instance_id) + 1) * sizeof(WCHAR); + status = STATUS_SUCCESS; + break; + + default: + ok(0, "Unknown string type %#x.\n", index); + status = STATUS_NOT_IMPLEMENTED; + break; + } break; + } case IOCTL_GET_PHYSICAL_DESCRIPTOR: irp->IoStatus.Information = 0; diff --git a/dlls/dinput/tests/hid.c b/dlls/dinput/tests/hid.c index c9cdb649f26..966fa29c8fd 100644 --- a/dlls/dinput/tests/hid.c +++ b/dlls/dinput/tests/hid.c @@ -3587,13 +3587,36 @@ done: hid_device_stop( &desc, 1 ); } +static BOOL cleanup_test_device_keys(HKEY key, const WCHAR *path) +{ + WCHAR buf[MAX_PATH]; + BOOL ret = FALSE; + unsigned int i; + HKEY root_key; + DWORD size; + + RegCreateKeyExW( key, path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); + size = ARRAY_SIZE(buf); + for (i = 0; !RegEnumKeyExW( root_key, i, buf, &size, NULL, NULL, NULL, NULL ); i++) + { + if (!wcsnicmp( buf, L"VID_1209", 8 )) + { + RegDeleteTreeW( root_key, buf ); + ret = TRUE; + break; + } + size = ARRAY_SIZE(buf); + } + RegCloseKey( root_key ); + return ret; +} + void cleanup_registry_keys(void) { static const WCHAR joystick_oem_path[] = L"System\\CurrentControlSet\\Control\\MediaProperties\\" "PrivateProperties\\Joystick\\OEM"; static const WCHAR dinput_path[] = L"System\\CurrentControlSet\\Control\\MediaProperties\\" "PrivateProperties\\DirectInput"; - HKEY root_key; /* These keys are automatically created by DInput and they store the list of supported force-feedback effects. OEM drivers are supposed @@ -3603,21 +3626,10 @@ void cleanup_registry_keys(void) We need to clean them up, or DInput will not refresh the list of effects from the PID report changes. */ - RegCreateKeyExW( HKEY_CURRENT_USER, joystick_oem_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); - RegDeleteTreeW( root_key, expect_vidpid_str ); - RegCloseKey( root_key ); - - RegCreateKeyExW( HKEY_CURRENT_USER, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); - RegDeleteTreeW( root_key, expect_vidpid_str ); - RegCloseKey( root_key ); - - RegCreateKeyExW( HKEY_LOCAL_MACHINE, joystick_oem_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); - RegDeleteTreeW( root_key, expect_vidpid_str ); - RegCloseKey( root_key ); - - RegCreateKeyExW( HKEY_LOCAL_MACHINE, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); - RegDeleteTreeW( root_key, expect_vidpid_str ); - RegCloseKey( root_key ); + while (cleanup_test_device_keys( HKEY_CURRENT_USER, joystick_oem_path )) {} + while (cleanup_test_device_keys( HKEY_CURRENT_USER, dinput_path )) {} + while (cleanup_test_device_keys( HKEY_LOCAL_MACHINE, joystick_oem_path )) {} + while (cleanup_test_device_keys( HKEY_LOCAL_MACHINE, dinput_path )) {} } static LRESULT CALLBACK monitor_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) @@ -3750,6 +3762,44 @@ void dinput_test_exit(void) CoUninitialize(); } +HRESULT dinput_test_create_device_instance( DWORD version, const GUID *guid_inst, IDirectInputDevice8W **device ) +{ + IDirectInput8W *di8; + IDirectInputW *di; + HRESULT hr; + ULONG ref; + + *device = NULL; + if (version >= 0x800) + { + hr = DirectInput8Create( instance, version, &IID_IDirectInput8W, (void **)&di8, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInput8Create returned %#lx\n", hr ); + return hr; + } + + hr = IDirectInput8_CreateDevice( di8, guid_inst, device, NULL ); + ref = IDirectInput8_Release( di8 ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + else + { + hr = DirectInputCreateEx( instance, version, &IID_IDirectInput2W, (void **)&di, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInputCreateEx returned %#lx\n", hr ); + return hr; + } + + hr = IDirectInput_CreateDevice( di, guid_inst, (IDirectInputDeviceW **)device, NULL ); + ref = IDirectInput_Release( di ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + + return hr; +} + BOOL CALLBACK find_test_device( const DIDEVICEINSTANCEW *devinst, void *context ) { if (IsEqualGUID( &devinst->guidProduct, &expect_guid_product )) @@ -3819,6 +3869,39 @@ HRESULT dinput_test_create_device( DWORD version, DIDEVICEINSTANCEW *devinst, ID return DI_OK; } +void dinput_test_get_device_hid_serial_string_( const char *file, int line, IDirectInputDevice8W *device, + WCHAR *serial_out) +{ + DIPROPGUIDANDPATH prop_guid_path = + { + .diph = + { + .dwSize = sizeof(DIPROPGUIDANDPATH), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + WCHAR device_id[MAX_PATH] = { 0 }; + HANDLE file_handle; + HRESULT hr; + BOOL ret; + + serial_out[0] = 0; + hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph ); + ok_(file, line)( hr == S_OK, "Unexpected hr %#lx.\n", hr ); + if (FAILED(hr)) return; + + file_handle = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + ok_(file, line)( file_handle != INVALID_HANDLE_VALUE, "Got error %lu.\n", GetLastError() ); + if (file_handle == INVALID_HANDLE_VALUE) return; + + ret = HidD_GetSerialNumberString( file_handle, device_id, ARRAY_SIZE(device_id) ); + ok_(file, line)( ret, "Failed to get HID serial number string.\n" ); + if (ret) wcscpy( serial_out, device_id ); + CloseHandle( file_handle ); +} + DWORD WINAPI dinput_test_device_thread( void *stop_event ) { #include "psh_hid_macros.h" diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c index ce44bae754c..350050c0a42 100644 --- a/dlls/dinput/tests/hotplug.c +++ b/dlls/dinput/tests/hotplug.c @@ -156,8 +156,9 @@ static BOOL test_input_lost( DWORD version ) DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; DIDEVICEOBJECTDATA objdata[32] = {{0}}; - IDirectInputDevice8W *device = NULL; + IDirectInputDevice8W *device, *device2; ULONG ref, count, size; + GUID guid_instance; DIJOYSTATE2 state; HRESULT hr; @@ -169,6 +170,7 @@ static BOOL test_input_lost( DWORD version ) memcpy( desc.report_descriptor_buf, report_desc, sizeof(report_desc) ); fill_context( desc.context, ARRAY_SIZE(desc.context) ); + device = device2 = NULL; if (!hid_device_start( &desc, 1 )) goto done; if (FAILED(hr = dinput_test_create_device( version, &devinst, &device ))) goto done; @@ -220,6 +222,21 @@ static BOOL test_input_lost( DWORD version ) ref = IDirectInputDevice8_Release( device ); ok( ref == 0, "Release returned %ld\n", ref ); + /* Test guidInstance across hotplugs. It should remain the same. */ + guid_instance = devinst.guidInstance; + memset( &devinst, 0, sizeof(devinst) ); + devinst.dwSize = sizeof(DIDEVICEINSTANCEW); + hr = dinput_test_create_device( version, &devinst, &device2 ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( !!device2, "device2 is NULL.\n" ); + if (device2) + { + todo_wine ok( !memcmp( &guid_instance, &devinst.guidInstance, sizeof(guid_instance) ), + "Unexpected guidInstance.\n" ); + ref = IDirectInputDevice8_Release( device2 ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + done: hid_device_stop( &desc, 1 ); cleanup_registry_keys(); diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index d267aff724f..c5dc5c066bf 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -5981,6 +5981,444 @@ static void test_rawinput_desktop( const char *path, BOOL input ) DestroyWindow( hwnd ); } +static void init_hid_device_desc(struct hid_device_desc *desc, const WCHAR *instance_id, WORD pid) +{ +#include "psh_hid_macros.h" + static const unsigned char report_desc[] = + { + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Application), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Physical), + 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, + }; + C_ASSERT(sizeof(report_desc) < MAX_HID_DESCRIPTOR_LEN); +#include "pop_hid_macros.h" + struct hid_device_desc init_desc = + { + .use_report_id = TRUE, + .caps = { .InputReportByteLength = 1 }, + .attributes = default_attributes, + }; + + *desc = init_desc; + desc->report_descriptor_len = sizeof(report_desc); + memcpy( desc->report_descriptor_buf, report_desc, sizeof(report_desc) ); + fill_context( desc->context, ARRAY_SIZE(desc->context) ); + if (instance_id) wcscpy(desc->instance_id, instance_id); + desc->attributes.ProductID = pid; +} + +struct joystick_test_device +{ + struct hid_device_desc desc; + BOOL started; +}; + +static BOOL start_joystick_test_device(struct joystick_test_device *device) +{ + device->started = hid_device_start( &device->desc, 1 ); + return device->started; +} + +static void stop_joystick_test_device(struct joystick_test_device *device) +{ + if (device->started) hid_device_stop( &device->desc, 1 ); + device->started = FALSE; +} + +struct dinput_test_devices +{ + DIDEVICEINSTANCEW *instances; + unsigned int instances_size; + unsigned int instances_count; +}; + +static void dinput_test_devices_struct_init(struct dinput_test_devices *devices, DIDEVICEINSTANCEW *insts, + unsigned int insts_size) +{ + const DIDEVICEINSTANCEW devinst_init = { .dwSize = sizeof(DIDEVICEINSTANCEW) }; + unsigned int i; + + for (i = 0; i < insts_size; i++) insts[i] = devinst_init; + devices->instances = insts; + devices->instances_size = insts_size; + devices->instances_count = 0; +} + +BOOL CALLBACK find_test_devices( const DIDEVICEINSTANCEW *devinst, void *context ) +{ + struct dinput_test_devices *devices = (struct dinput_test_devices *)context; + + if (!memcmp(devinst->guidProduct.Data4, expect_guid_product.Data4, sizeof(expect_guid_product.Data4)) + && (LOWORD(devinst->guidProduct.Data1) == LOWORD(EXPECT_VIDPID))) + { + devices->instances[devices->instances_count++] = *devinst; + if (devices->instances_count >= devices->instances_size) return DIENUM_STOP; + } + return DIENUM_CONTINUE; +} + +static HRESULT dinput_test_enum_devices( DWORD version, struct dinput_test_devices *context ) +{ + IDirectInput8W *di8; + IDirectInputW *di; + HRESULT hr; + ULONG ref; + + context->instances_count = 0; + if (version >= 0x800) + { + hr = DirectInput8Create( instance, version, &IID_IDirectInput8W, (void **)&di8, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInput8Create returned %#lx\n", hr ); + return hr; + } + + hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, find_test_devices, context, DIEDFL_ALLDEVICES ); + ok( hr == DI_OK, "EnumDevices returned: %#lx\n", hr ); + + ref = IDirectInput8_Release( di8 ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + else + { + hr = DirectInputCreateEx( instance, version, &IID_IDirectInput2W, (void **)&di, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInputCreateEx returned %#lx\n", hr ); + return hr; + } + + hr = IDirectInput_EnumDevices( di, 0, find_test_devices, context, DIEDFL_ALLDEVICES ); + ok( hr == DI_OK, "EnumDevices returned: %#lx\n", hr ); + + ref = IDirectInput_Release( di ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + + return DI_OK; +} + +static void test_joystick_instance_guid( DWORD version ) +{ + struct dinput_test_devices dinput_test_devs; + struct joystick_test_device test_devs[4]; + WCHAR device_serial[MAX_PATH]; + DIDEVICEINSTANCEW devinsts[4]; + IDirectInputDevice8W *device; + GUID guid_instances[4]; + unsigned int i; + HRESULT hr; + + winetest_push_context( "%#lx", version ); + cleanup_registry_keys(); + + memset( test_devs, 0, sizeof(test_devs) ); + init_hid_device_desc( &test_devs[0].desc, L"1&2345&6", 0x0001 ); + init_hid_device_desc( &test_devs[1].desc, L"2&7812&1", 0x0001 ); + init_hid_device_desc( &test_devs[2].desc, L"3&4580&7", 0x0002 ); + init_hid_device_desc( &test_devs[3].desc, L"4&5880&3", 0x0002 ); + + for (i = 0; i < ARRAY_SIZE(test_devs); i++) + { + if (!start_joystick_test_device( &test_devs[i] )) + goto done; + } + + /* Enumerate all devices, each with a unique guidInstance. */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( dinput_test_devs.instances_count == 4, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + guid_instances[i] = devinsts[i].guidInstance; + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Delete the existing registry keys, this will result in a new set of + * guidInstance values being generated on the next call to EnumDevices(). + */ + cleanup_registry_keys(); + + /* + * EnumDevices() hasn't been called yet, guidInstance values are still + * valid. + */ + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Call EnumDevices(), a new set of guidInstance values and registry entries + * will be created. + */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( dinput_test_devs.instances_count == 4, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + winetest_push_context( "device %d", i ); + + todo_wine ok( !IsEqualGUID( &guid_instances[i], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", + debugstr_guid(&devinsts[i].guidInstance) ); + + /* Old guidInstance no longer works. */ + device = NULL; + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + todo_wine ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); + if (device) IDirectInputDevice8_Release(device); + guid_instances[i] = devinsts[i].guidInstance; + + /* New guidInstance, same device. */ + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Stop devices 0 and 2. After enumeration, their guidInstance values will + * be assigned to devices 1 and 3. + */ + stop_joystick_test_device( &test_devs[0] ); + stop_joystick_test_device( &test_devs[2] ); + + /* Stopped devices should return E_FAIL. */ + hr = dinput_test_create_device_instance( version, &devinsts[0].guidInstance, &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + hr = dinput_test_create_device_instance( version, &devinsts[2].guidInstance, &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + + /* After calling EnumDevices(), guidInstance values will be reassigned. */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( dinput_test_devs.instances_count == 2, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + const unsigned int expected_joystick = !i ? 1 : 3; + + winetest_push_context( "device %d", i ); + + todo_wine ok( IsEqualGUID( &guid_instances[expected_joystick - 1], &devinsts[i].guidInstance ), + "Unexpected guidInstance %s.\n", debugstr_guid(&devinsts[i].guidInstance) ); + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[expected_joystick].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Restart devices 0 and 2, they should get their old guidInstance values + * back after another call to EnumDevices(). + */ + if (!start_joystick_test_device( &test_devs[0] )) goto done; + if (!start_joystick_test_device( &test_devs[2] )) goto done; + + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + const unsigned int expected_joystick = !i ? 1 : 3; + + winetest_push_context( "device %d", i ); + + todo_wine ok( IsEqualGUID( &guid_instances[expected_joystick - 1], &devinsts[i].guidInstance ), + "Unexpected guidInstance %s.\n", debugstr_guid(&devinsts[i].guidInstance) ); + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[expected_joystick].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* guidInstance values are all back to what they were. */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( dinput_test_devs.instances_count == 4, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + winetest_push_context( "device %d", i ); + + todo_wine_if(!(i & 0x1)) ok( IsEqualGUID( &guid_instances[i], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", + debugstr_guid(&devinsts[i].guidInstance) ); + + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* Stop all devices. */ + for (i = 0; i < ARRAY_SIZE(test_devs); i++) stop_joystick_test_device( &test_devs[i] ); + + /* + * Call EnumDevices() with all devices stopped. Test behavior of device + * creation without calling EnumDevices() with the devices present + * beforehand. + */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( !dinput_test_devs.instances_count, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < ARRAY_SIZE(test_devs); i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); + + /* + * Start the joystick device. This will succeed, even without having + * called EnumDevices() beforehand. + */ + if (!start_joystick_test_device( &test_devs[i] )) goto done; + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + if (device) + { + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + } + stop_joystick_test_device( &test_devs[i] ); + + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + + if (!start_joystick_test_device( &test_devs[i] )) goto done; + winetest_pop_context(); + } + + for (i = 0; i < ARRAY_SIZE(test_devs); i++) stop_joystick_test_device( &test_devs[i] ); + + /* Reset device list. */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( !dinput_test_devs.instances_count, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + + /* + * Start device 0, create a device with guidInstance 0. + * Stop device 0. + * Start device 1, attempt to create a device with guidInstance 1. + * Fails. + * Try to create a device with guidInstance 0, which is now associated + * with device 1. + */ + if (!start_joystick_test_device( &test_devs[0] )) goto done; + + hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); + todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + if (device) + { + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[0].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + } + stop_joystick_test_device( &test_devs[0] ); + + hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + + /* Start device 1. */ + if (!start_joystick_test_device( &test_devs[1] )) goto done; + + /* + * This still fails with E_FAIL, as guid_instances[0] is still associated + * with device 0. + */ + hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + + /* + * guid_instances[1] is not currently associated with a device, which + * means attempting to use this GUID results in what seems to be an + * internal call to EnumDevices(). With device 0 currently stopped, + * guid_instances[0] gets reassigned to device 1. + */ + hr = dinput_test_create_device_instance( version, &guid_instances[1], &device ); + ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); + + hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); + todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + if (device) + { + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[1].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + } + stop_joystick_test_device( &test_devs[1] ); + +done: + for (i = 0; i < ARRAY_SIZE(test_devs); i++) + stop_joystick_test_device( &test_devs[i] ); + cleanup_registry_keys(); + winetest_pop_context(); +} + START_TEST( joystick8 ) { char **argv; @@ -6008,6 +6446,10 @@ START_TEST( joystick8 ) test_simple_joystick( 0x700 ); test_simple_joystick( 0x800 ); + test_joystick_instance_guid( 0x500 ); + test_joystick_instance_guid( 0x700 ); + test_joystick_instance_guid( 0x800 ); + test_many_axes_joystick(); test_driving_wheel_axes(); test_rawinput( argv ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10364