[PATCH v2 2/7] dinput/tests: Split joystick tests into separate sources.

Rémi Bernon rbernon at codeweavers.com
Mon Jan 3 07:43:35 CST 2022


Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/dinput/tests/Makefile.in      |    2 +
 dlls/dinput/tests/dinput_test.h    |  100 +
 dlls/dinput/tests/driver_hid.c     |    1 +
 dlls/dinput/tests/driver_hid.h     |    2 -
 dlls/dinput/tests/force_feedback.c | 4337 ++++++++++++++++++
 dlls/dinput/tests/hid.c            | 6767 +---------------------------
 dlls/dinput/tests/joystick8.c      | 2594 +++++++++++
 7 files changed, 7125 insertions(+), 6678 deletions(-)
 create mode 100644 dlls/dinput/tests/dinput_test.h
 create mode 100644 dlls/dinput/tests/force_feedback.c
 create mode 100644 dlls/dinput/tests/joystick8.c

diff --git a/dlls/dinput/tests/Makefile.in b/dlls/dinput/tests/Makefile.in
index 87a4dad2708..b6648008115 100644
--- a/dlls/dinput/tests/Makefile.in
+++ b/dlls/dinput/tests/Makefile.in
@@ -11,7 +11,9 @@ SOURCES = \
 	dinput8.c \
 	driver_hid.c \
 	driver_hid.spec \
+	force_feedback.c \
 	hid.c \
 	joystick.c \
+	joystick8.c \
 	keyboard.c \
 	mouse.c
diff --git a/dlls/dinput/tests/dinput_test.h b/dlls/dinput/tests/dinput_test.h
new file mode 100644
index 00000000000..0a4c9ba5fa4
--- /dev/null
+++ b/dlls/dinput/tests/dinput_test.h
@@ -0,0 +1,100 @@
+/*
+ * DInput driver-based testing framework
+ *
+ * Copyright 2015 Sebastian Lackner
+ * Copyright 2015 Michael Müller
+ * Copyright 2015 Christian Costa
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __WINE_DINPUT_TEST_H
+#define __WINE_DINPUT_TEST_H
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winioctl.h"
+#include "winternl.h"
+
+#include "dinput.h"
+
+#include "ddk/wdm.h"
+#include "ddk/hidsdi.h"
+#include "ddk/hidport.h"
+
+#include "wine/test.h"
+
+#include "driver_hid.h"
+
+#define EXPECT_VIDPID MAKELONG( 0x1209, 0x0001 )
+extern const WCHAR expect_vidpid_str[];
+extern const GUID expect_guid_product;
+extern const WCHAR expect_path[];
+extern const WCHAR expect_path_end[];
+
+extern HINSTANCE instance;
+extern BOOL localized; /* object names get translated */
+
+BOOL pnp_driver_start( const WCHAR *resource );
+void pnp_driver_stop(void);
+
+void cleanup_registry_keys(void);
+
+#define dinput_driver_start( a, b, c, d, e ) dinput_driver_start_( __FILE__, __LINE__, a, b, c, d, e )
+BOOL dinput_driver_start_( const char *file, int line, const BYTE *desc_buf, ULONG desc_len,
+                           const HIDP_CAPS *caps, struct hid_expect *expect, ULONG expect_size );
+
+#define dinput_test_init() dinput_test_init_( __FILE__, __LINE__ )
+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 );
+
+#define check_member_( file, line, val, exp, fmt, member )                                         \
+    ok_(file, line)( (val).member == (exp).member, "got " #member " " fmt "\n", (val).member )
+#define check_member( val, exp, fmt, member )                                                      \
+    check_member_( __FILE__, __LINE__, val, exp, fmt, member )
+
+#define check_member_guid_( file, line, val, exp, member )                                         \
+    ok_(file, line)( IsEqualGUID( &(val).member, &(exp).member ), "got " #member " %s\n",         \
+                     debugstr_guid( &(val).member ) )
+#define check_member_guid( val, exp, member )                                                      \
+    check_member_guid_( __FILE__, __LINE__, val, exp, member )
+
+#define check_member_wstr_( file, line, val, exp, member )                                         \
+    ok_(file, line)( !wcscmp( (val).member, (exp).member ), "got " #member " %s\n",                \
+                     debugstr_w((val).member) )
+#define check_member_wstr( val, exp, member )                                                      \
+    check_member_wstr_( __FILE__, __LINE__, val, exp, member )
+
+#define sync_ioctl( a, b, c, d, e, f, g ) sync_ioctl_( __FILE__, __LINE__, a, b, c, d, e, f, g )
+BOOL sync_ioctl_( const char *file, int line, HANDLE device, DWORD code, void *in_buf, DWORD in_len,
+                  void *out_buf, DWORD *ret_len, DWORD timeout );
+
+#define set_hid_expect( a, b, c ) set_hid_expect_( __FILE__, __LINE__, a, b, c )
+void set_hid_expect_( const char *file, int line, HANDLE device, struct hid_expect *expect, DWORD expect_size );
+
+#define wait_hid_expect( a, b ) wait_hid_expect_( __FILE__, __LINE__, a, b )
+void wait_hid_expect_( const char *file, int line, HANDLE device, DWORD timeout );
+
+#define send_hid_input( a, b, c ) send_hid_input_( __FILE__, __LINE__, a, b, c )
+void send_hid_input_( const char *file, int line, HANDLE device, struct hid_expect *expect, DWORD expect_size );
+
+#endif /* __WINE_DINPUT_TEST_H */
diff --git a/dlls/dinput/tests/driver_hid.c b/dlls/dinput/tests/driver_hid.c
index 545dff0c2dc..84b84101592 100644
--- a/dlls/dinput/tests/driver_hid.c
+++ b/dlls/dinput/tests/driver_hid.c
@@ -34,6 +34,7 @@
 
 #include "wine/list.h"
 
+#include "initguid.h"
 #include "driver_hid.h"
 
 static UNICODE_STRING control_symlink;
diff --git a/dlls/dinput/tests/driver_hid.h b/dlls/dinput/tests/driver_hid.h
index 3d31b4ee824..963f7619b1f 100644
--- a/dlls/dinput/tests/driver_hid.h
+++ b/dlls/dinput/tests/driver_hid.h
@@ -35,8 +35,6 @@
 
 #include "ddk/wdm.h"
 
-#include "initguid.h"
-
 DEFINE_GUID(control_class,0xdeadbeef,0x29ef,0x4538,0xa5,0xfd,0xb6,0x95,0x73,0xa3,0x62,0xc0);
 
 #define IOCTL_WINETEST_HID_SET_EXPECT    CTL_CODE(FILE_DEVICE_KEYBOARD, 0x800, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c
new file mode 100644
index 00000000000..bc5161a9b63
--- /dev/null
+++ b/dlls/dinput/tests/force_feedback.c
@@ -0,0 +1,4337 @@
+/*
+ * Copyright 2022 Rémi Bernon for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define DIRECTINPUT_VERSION 0x0800
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+
+#define COBJMACROS
+#include "dinput.h"
+#include "dinputd.h"
+
+#include "wine/hid.h"
+
+#include "dinput_test.h"
+
+struct check_objects_todos
+{
+    BOOL type;
+    BOOL guid;
+    BOOL usage;
+    BOOL name;
+};
+
+struct check_objects_params
+{
+    DWORD version;
+    UINT index;
+    UINT expect_count;
+    const DIDEVICEOBJECTINSTANCEW *expect_objs;
+    const struct check_objects_todos *todo_objs;
+    BOOL todo_extra;
+};
+
+static BOOL CALLBACK check_objects( const DIDEVICEOBJECTINSTANCEW *obj, void *args )
+{
+    static const DIDEVICEOBJECTINSTANCEW unexpected_obj = {0};
+    static const struct check_objects_todos todo_none = {0};
+    struct check_objects_params *params = args;
+    const DIDEVICEOBJECTINSTANCEW *exp = params->expect_objs + params->index;
+    const struct check_objects_todos *todo;
+
+    if (!params->todo_objs) todo = &todo_none;
+    else todo = params->todo_objs + params->index;
+
+    todo_wine_if( params->todo_extra && params->index >= params->expect_count )
+    ok( params->index < params->expect_count, "unexpected extra object\n" );
+    if (params->index >= params->expect_count) return DIENUM_STOP;
+
+    winetest_push_context( "obj[%d]", params->index );
+
+    ok( params->index < params->expect_count, "unexpected extra object\n" );
+    if (params->index >= params->expect_count) exp = &unexpected_obj;
+
+    check_member( *obj, *exp, "%u", dwSize );
+    todo_wine_if( todo->guid )
+    check_member_guid( *obj, *exp, guidType );
+    todo_wine_if( params->version < 0x700 && (obj->dwType & DIDFT_BUTTON) )
+    check_member( *obj, *exp, "%#x", dwOfs );
+    todo_wine_if( todo->type )
+    check_member( *obj, *exp, "%#x", dwType );
+    check_member( *obj, *exp, "%#x", dwFlags );
+    if (!localized) todo_wine_if( todo->name ) check_member_wstr( *obj, *exp, tszName );
+    check_member( *obj, *exp, "%u", dwFFMaxForce );
+    check_member( *obj, *exp, "%u", dwFFForceResolution );
+    check_member( *obj, *exp, "%u", wCollectionNumber );
+    check_member( *obj, *exp, "%u", wDesignatorIndex );
+    check_member( *obj, *exp, "%#04x", wUsagePage );
+    todo_wine_if( todo->usage )
+    check_member( *obj, *exp, "%#04x", wUsage );
+    check_member( *obj, *exp, "%#04x", dwDimension );
+    check_member( *obj, *exp, "%#04x", wExponent );
+    check_member( *obj, *exp, "%u", wReportId );
+
+    winetest_pop_context();
+
+    params->index++;
+    return DIENUM_CONTINUE;
+}
+
+struct check_effects_params
+{
+    UINT index;
+    UINT expect_count;
+    const DIEFFECTINFOW *expect_effects;
+};
+
+static BOOL CALLBACK check_effects( const DIEFFECTINFOW *effect, void *args )
+{
+    static const DIEFFECTINFOW unexpected_effect = {0};
+    struct check_effects_params *params = args;
+    const DIEFFECTINFOW *exp = params->expect_effects + params->index;
+
+    winetest_push_context( "effect[%d]", params->index );
+
+    ok( params->index < params->expect_count, "unexpected extra object\n" );
+    if (params->index >= params->expect_count) exp = &unexpected_effect;
+
+    check_member( *effect, *exp, "%u", dwSize );
+    check_member_guid( *effect, *exp, guid );
+    check_member( *effect, *exp, "%#x", dwEffType );
+    check_member( *effect, *exp, "%#x", dwStaticParams );
+    check_member( *effect, *exp, "%#x", dwDynamicParams );
+    check_member_wstr( *effect, *exp, tszName );
+
+    winetest_pop_context();
+    params->index++;
+
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK check_effect_count( const DIEFFECTINFOW *effect, void *args )
+{
+    DWORD *count = args;
+    *count = *count + 1;
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK check_no_created_effect_objects( IDirectInputEffect *effect, void *context )
+{
+    ok( 0, "unexpected effect %p\n", effect );
+    return DIENUM_CONTINUE;
+}
+
+struct check_created_effect_params
+{
+    IDirectInputEffect *expect_effect;
+    DWORD count;
+};
+
+static BOOL CALLBACK check_created_effect_objects( IDirectInputEffect *effect, void *context )
+{
+    struct check_created_effect_params *params = context;
+    ULONG ref;
+
+    ok( effect == params->expect_effect, "got effect %p, expected %p\n", effect, params->expect_effect );
+    params->count++;
+
+    IDirectInputEffect_AddRef( effect );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 1, "got ref %u, expected 1\n", ref );
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK enum_device_count( const DIDEVICEINSTANCEW *devinst, void *context )
+{
+    DWORD *count = context;
+    *count = *count + 1;
+    return DIENUM_CONTINUE;
+}
+
+static void check_dinput_devices( DWORD version, DIDEVICEINSTANCEW *devinst )
+{
+    IDirectInput8W *di8;
+    IDirectInputW *di;
+    ULONG count;
+    HRESULT hr;
+
+    if (version >= 0x800)
+    {
+        hr = DirectInput8Create( instance, version, &IID_IDirectInput8W, (void **)&di8, NULL );
+        ok( hr == DI_OK, "DirectInput8Create returned %#x\n", hr );
+
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, NULL, NULL, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, enum_device_count, &count, 0xdeadbeef );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput8_EnumDevices( di8, 0xdeadbeef, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 3, "got count %u, expected 0\n", count );
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_DEVICE, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 0, "got count %u, expected 0\n", count );
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_POINTER, enum_device_count, &count,
+                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        todo_wine
+        ok( count == 3, "got count %u, expected 3\n", count );
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_KEYBOARD, enum_device_count, &count,
+                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        todo_wine
+        ok( count == 3, "got count %u, expected 3\n", count );
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_GAMECTRL, enum_device_count, &count,
+                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_FORCEFEEDBACK );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        if (IsEqualGUID( &devinst->guidFFDriver, &GUID_NULL )) ok( count == 0, "got count %u, expected 0\n", count );
+        else ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff) + 1, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        if ((devinst->dwDevType & 0xff) != DI8DEVTYPE_SUPPLEMENTAL) ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        else ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        ok( count == 0, "got count %u, expected 0\n", count );
+    }
+    else
+    {
+        hr = DirectInputCreateEx( instance, version, &IID_IDirectInput2W, (void **)&di, NULL );
+        ok( hr == DI_OK, "DirectInputCreateEx returned %#x\n", hr );
+
+        hr = IDirectInput_EnumDevices( di, 0, NULL, NULL, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, 0xdeadbeef );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput_EnumDevices( di, 0xdeadbeef, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, DIEDFL_INCLUDEHIDDEN );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 3, "got count %u, expected 0\n", count );
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_DEVICE, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 0, "got count %u, expected 0\n", count );
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_MOUSE, enum_device_count, &count,
+                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        todo_wine
+        ok( count == 3, "got count %u, expected 3\n", count );
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_KEYBOARD, enum_device_count, &count,
+                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        todo_wine
+        ok( count == 3, "got count %u, expected 3\n", count );
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_JOYSTICK, enum_device_count, &count,
+                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_FORCEFEEDBACK );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        if (IsEqualGUID( &devinst->guidFFDriver, &GUID_NULL )) ok( count == 0, "got count %u, expected 0\n", count );
+        else ok( count == 1, "got count %u, expected 1\n", count );
+
+        hr = IDirectInput_EnumDevices( di, 0x14, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+    }
+}
+
+static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file, DWORD version )
+{
+    struct hid_expect expect_download[] =
+    {
+        /* set periodic */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 5,
+            .report_len = 2,
+            .report_buf = {0x05,0x19},
+        },
+        /* set envelope */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 6,
+            .report_len = 7,
+            .report_buf = {0x06,0x19,0x4c,0x02,0x00,0x04,0x00},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03,0x01,0x01,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0xd5},
+        },
+        /* start command when DIEP_START is set */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 2,
+            .report_len = 4,
+            .report_buf = {0x02,0x01,0x01,0x01},
+        },
+    };
+    struct hid_expect expect_download_2[] =
+    {
+        /* set periodic */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 5,
+            .report_len = 2,
+            .report_buf = {0x05,0x19},
+        },
+        /* set envelope */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 6,
+            .report_len = 7,
+            .report_buf = {0x06,0x19,0x4c,0x02,0x00,0x04,0x00},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03,0x01,0x02,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0xd5},
+        },
+    };
+    struct hid_expect expect_update[] =
+    {
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03, 0x01, 0x02, 0x08, 0xff, 0xff, version >= 0x700 ? 0x06 : 0x00, 0x00, 0x01, 0x55, 0xd5},
+        },
+    };
+    struct hid_expect expect_set_envelope[] =
+    {
+        /* set envelope */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 6,
+            .report_len = 7,
+            .report_buf = {0x06, 0x19, 0x4c, 0x01, 0x00, 0x04, 0x00},
+        },
+    };
+    struct hid_expect expect_start =
+    {
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {0x02, 0x01, 0x01, 0x01},
+    };
+    struct hid_expect expect_start_solo =
+    {
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {0x02, 0x01, 0x02, 0x01},
+    };
+    struct hid_expect expect_start_0 =
+    {
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {0x02, 0x01, 0x01, 0x00},
+    };
+    struct hid_expect expect_start_4 =
+    {
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {0x02, 0x01, 0x01, 0x04},
+    };
+    struct hid_expect expect_stop =
+    {
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {0x02, 0x01, 0x03, 0x00},
+    };
+    struct hid_expect expect_unload[] =
+    {
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 2,
+            .report_len = 4,
+            .report_buf = {0x02,0x01,0x03,0x00},
+        },
+        /* device reset, when unloaded from Unacquire */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1,0x01},
+        },
+    };
+    struct hid_expect expect_acquire[] =
+    {
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x01},
+        },
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 8,
+            .report_len = 2,
+            .report_buf = {8, 0x19},
+        },
+    };
+    struct hid_expect expect_reset[] =
+    {
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x01},
+        },
+    };
+    static const DWORD expect_axes_init[2] = {0};
+    const DIEFFECT expect_desc_init =
+    {
+        .dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5),
+        .dwTriggerButton = -1,
+        .rgdwAxes = (void *)expect_axes_init,
+    };
+    static const DWORD expect_axes[3] =
+    {
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR,
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR,
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR,
+    };
+    static const LONG expect_directions[3] =
+    {
+        +3000,
+        -6000,
+        0,
+    };
+    static const DIENVELOPE expect_envelope =
+    {
+        .dwSize = sizeof(DIENVELOPE),
+        .dwAttackLevel = 1000,
+        .dwAttackTime = 2000,
+        .dwFadeLevel = 3000,
+        .dwFadeTime = 4000,
+    };
+    static const DIPERIODIC expect_periodic =
+    {
+        .dwMagnitude = 1000,
+        .lOffset = 2000,
+        .dwPhase = 3000,
+        .dwPeriod = 4000,
+    };
+    const DIEFFECT expect_desc =
+    {
+        .dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5),
+        .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS,
+        .dwDuration = 1000,
+        .dwSamplePeriod = 2000,
+        .dwGain = 3000,
+        .dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
+        .dwTriggerRepeatInterval = 5000,
+        .cAxes = 3,
+        .rgdwAxes = (void *)expect_axes,
+        .rglDirection = (void *)expect_directions,
+        .lpEnvelope = (void *)&expect_envelope,
+        .cbTypeSpecificParams = sizeof(DIPERIODIC),
+        .lpvTypeSpecificParams = (void *)&expect_periodic,
+        .dwStartDelay = 6000,
+    };
+    struct check_created_effect_params check_params = {0};
+    IDirectInputEffect *effect;
+    DIPERIODIC periodic = {0};
+    DIENVELOPE envelope = {0};
+    LONG directions[4] = {0};
+    DIEFFECT desc = {0};
+    DWORD axes[4] = {0};
+    ULONG i, ref, flags;
+    HRESULT hr;
+    GUID guid;
+
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, NULL, NULL );
+    ok( hr == E_POINTER, "CreateEffect returned %#x\n", hr );
+    hr = IDirectInputDevice8_CreateEffect( device, NULL, NULL, &effect, NULL );
+    ok( hr == E_POINTER, "CreateEffect returned %#x\n", hr );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_NULL, NULL, &effect, NULL );
+    ok( hr == DIERR_DEVICENOTREG, "CreateEffect returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    if (hr != DI_OK) return;
+
+    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_no_created_effect_objects, effect, 0xdeadbeef );
+    ok( hr == DIERR_INVALIDPARAM, "EnumCreatedEffectObjects returned %#x\n", hr );
+    check_params.expect_effect = effect;
+    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_created_effect_objects, &check_params, 0 );
+    ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#x\n", hr );
+    ok( check_params.count == 1, "got count %u, expected 1\n", check_params.count );
+
+    hr = IDirectInputEffect_Initialize( effect, NULL, version, &GUID_Sine );
+    ok( hr == DIERR_INVALIDPARAM, "Initialize returned %#x\n", hr );
+    hr = IDirectInputEffect_Initialize( effect, instance, 0x800 - (version - 0x700), &GUID_Sine );
+    if (version == 0x800)
+    {
+        todo_wine
+        ok( hr == DIERR_BETADIRECTINPUTVERSION, "Initialize returned %#x\n", hr );
+    }
+    else
+    {
+        todo_wine
+        ok( hr == DIERR_OLDDIRECTINPUTVERSION, "Initialize returned %#x\n", hr );
+    }
+    hr = IDirectInputEffect_Initialize( effect, instance, 0, &GUID_Sine );
+    todo_wine
+    ok( hr == DIERR_NOTINITIALIZED, "Initialize returned %#x\n", hr );
+    hr = IDirectInputEffect_Initialize( effect, instance, version, NULL );
+    ok( hr == E_POINTER, "Initialize returned %#x\n", hr );
+
+    hr = IDirectInputEffect_Initialize( effect, instance, version, &GUID_NULL );
+    ok( hr == DIERR_DEVICENOTREG, "Initialize returned %#x\n", hr );
+    hr = IDirectInputEffect_Initialize( effect, instance, version, &GUID_Sine );
+    ok( hr == DI_OK, "Initialize returned %#x\n", hr );
+    hr = IDirectInputEffect_Initialize( effect, instance, version, &GUID_Square );
+    ok( hr == DI_OK, "Initialize returned %#x\n", hr );
+
+    hr = IDirectInputEffect_GetEffectGuid( effect, NULL );
+    ok( hr == E_POINTER, "GetEffectGuid returned %#x\n", hr );
+    hr = IDirectInputEffect_GetEffectGuid( effect, &guid );
+    ok( hr == DI_OK, "GetEffectGuid returned %#x\n", hr );
+    ok( IsEqualGUID( &guid, &GUID_Square ), "got guid %s, expected %s\n", debugstr_guid( &guid ),
+        debugstr_guid( &GUID_Square ) );
+
+    hr = IDirectInputEffect_GetParameters( effect, NULL, 0 );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_GetParameters( effect, NULL, DIEP_DURATION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, 0 );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    desc.dwSize = sizeof(DIEFFECT_DX5) + 2;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, 0 );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    desc.dwSize = sizeof(DIEFFECT_DX5);
+    hr = IDirectInputEffect_GetParameters( effect, &desc, 0 );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_STARTDELAY );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    desc.dwSize = sizeof(DIEFFECT_DX6);
+    hr = IDirectInputEffect_GetParameters( effect, &desc, 0 );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+
+    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DURATION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+
+    desc.dwDuration = 0xdeadbeef;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DURATION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%u", dwDuration );
+    memset( &desc, 0xcd, sizeof(desc) );
+    desc.dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5);
+    desc.dwFlags = 0;
+    desc.dwStartDelay = 0xdeadbeef;
+    flags = DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_TRIGGERREPEATINTERVAL |
+            (version >= 0x700 ? DIEP_STARTDELAY : 0);
+    hr = IDirectInputEffect_GetParameters( effect, &desc, flags );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%u", dwSamplePeriod );
+    check_member( desc, expect_desc_init, "%u", dwGain );
+    if (version >= 0x700) check_member( desc, expect_desc_init, "%u", dwStartDelay );
+    else ok( desc.dwStartDelay == 0xdeadbeef, "got dwStartDelay %#x\n", desc.dwStartDelay );
+    check_member( desc, expect_desc_init, "%u", dwTriggerRepeatInterval );
+
+    memset( &desc, 0xcd, sizeof(desc) );
+    desc.dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5);
+    desc.dwFlags = 0;
+    desc.lpEnvelope = NULL;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ENVELOPE );
+    ok( hr == E_POINTER, "GetParameters returned %#x\n", hr );
+    desc.lpEnvelope = &envelope;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ENVELOPE );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    envelope.dwSize = sizeof(DIENVELOPE);
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ENVELOPE );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+
+    desc.dwFlags = 0;
+    desc.cAxes = 0;
+    desc.rgdwAxes = NULL;
+    desc.rglDirection = NULL;
+    desc.lpEnvelope = NULL;
+    desc.cbTypeSpecificParams = 0;
+    desc.lpvTypeSpecificParams = NULL;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5 );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TRIGGERBUTTON );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    desc.dwFlags = DIEFF_OBJECTOFFSETS;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TRIGGERBUTTON );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%u", cAxes );
+    desc.dwFlags = DIEFF_OBJECTIDS;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TRIGGERBUTTON );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%u", cAxes );
+    desc.dwFlags |= DIEFF_CARTESIAN;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    ok( desc.dwFlags == DIEFF_OBJECTIDS, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_OBJECTIDS );
+    desc.dwFlags |= DIEFF_POLAR;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+    ok( desc.dwFlags == DIEFF_OBJECTIDS, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_OBJECTIDS );
+    desc.dwFlags |= DIEFF_SPHERICAL;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%u", cAxes );
+    ok( desc.dwFlags == DIEFF_OBJECTIDS, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_OBJECTIDS );
+
+    desc.dwFlags |= DIEFF_SPHERICAL;
+    desc.cAxes = 2;
+    desc.rgdwAxes = axes;
+    desc.rglDirection = directions;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES | DIEP_DIRECTION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%u", cAxes );
+    check_member( desc, expect_desc_init, "%u", rgdwAxes[0] );
+    check_member( desc, expect_desc_init, "%u", rgdwAxes[1] );
+    check_member( desc, expect_desc_init, "%p", rglDirection );
+    ok( desc.dwFlags == DIEFF_OBJECTIDS, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_OBJECTIDS );
+
+    desc.dwFlags |= DIEFF_SPHERICAL;
+    desc.lpEnvelope = &envelope;
+    desc.cbTypeSpecificParams = sizeof(periodic);
+    desc.lpvTypeSpecificParams = &periodic;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5 );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc_init, "%u", dwDuration );
+    check_member( desc, expect_desc_init, "%u", dwSamplePeriod );
+    check_member( desc, expect_desc_init, "%u", dwGain );
+    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
+    check_member( desc, expect_desc_init, "%u", dwTriggerRepeatInterval );
+    check_member( desc, expect_desc_init, "%u", cAxes );
+    check_member( desc, expect_desc_init, "%u", rgdwAxes[0] );
+    check_member( desc, expect_desc_init, "%u", rgdwAxes[1] );
+    check_member( desc, expect_desc_init, "%p", rglDirection );
+    check_member( desc, expect_desc_init, "%p", lpEnvelope );
+    check_member( desc, expect_desc_init, "%u", cbTypeSpecificParams );
+    if (version >= 0x700) check_member( desc, expect_desc_init, "%u", dwStartDelay );
+    else ok( desc.dwStartDelay == 0xcdcdcdcd, "got dwStartDelay %#x\n", desc.dwStartDelay );
+
+    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Download returned %#x\n", hr );
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
+
+    hr = IDirectInputEffect_SetParameters( effect, NULL, DIEP_NODOWNLOAD );
+    ok( hr == E_POINTER, "SetParameters returned %#x\n", hr );
+    memset( &desc, 0, sizeof(desc) );
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+    desc.dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5);
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+
+    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_DURATION );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_DURATION | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+
+    desc.dwTriggerButton = -1;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DURATION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc, "%u", dwDuration );
+    check_member( desc, expect_desc_init, "%u", dwSamplePeriod );
+    check_member( desc, expect_desc_init, "%u", dwGain );
+    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
+    check_member( desc, expect_desc_init, "%u", dwTriggerRepeatInterval );
+    check_member( desc, expect_desc_init, "%u", cAxes );
+    check_member( desc, expect_desc_init, "%p", rglDirection );
+    check_member( desc, expect_desc_init, "%p", lpEnvelope );
+    check_member( desc, expect_desc_init, "%u", cbTypeSpecificParams );
+    check_member( desc, expect_desc_init, "%u", dwStartDelay );
+
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
+
+    flags = DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_TRIGGERREPEATINTERVAL | DIEP_NODOWNLOAD;
+    if (version >= 0x700) flags |= DIEP_STARTDELAY;
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, flags );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    desc.dwDuration = 0;
+    flags = DIEP_DURATION | DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_TRIGGERREPEATINTERVAL;
+    if (version >= 0x700) flags |= DIEP_STARTDELAY;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, flags );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc, "%u", dwDuration );
+    check_member( desc, expect_desc, "%u", dwSamplePeriod );
+    check_member( desc, expect_desc, "%u", dwGain );
+    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
+    check_member( desc, expect_desc, "%u", dwTriggerRepeatInterval );
+    check_member( desc, expect_desc_init, "%u", cAxes );
+    check_member( desc, expect_desc_init, "%p", rglDirection );
+    check_member( desc, expect_desc_init, "%p", lpEnvelope );
+    check_member( desc, expect_desc_init, "%u", cbTypeSpecificParams );
+    if (version >= 0x700) check_member( desc, expect_desc, "%u", dwStartDelay );
+    else ok( desc.dwStartDelay == 0, "got dwStartDelay %#x\n", desc.dwStartDelay );
+
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
+
+    desc.lpEnvelope = NULL;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_ENVELOPE | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    desc.lpEnvelope = &envelope;
+    envelope.dwSize = 0;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_ENVELOPE | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_ENVELOPE | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+
+    desc.lpEnvelope = &envelope;
+    envelope.dwSize = sizeof(DIENVELOPE);
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ENVELOPE );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( envelope, expect_envelope, "%u", dwAttackLevel );
+    check_member( envelope, expect_envelope, "%u", dwAttackTime );
+    check_member( envelope, expect_envelope, "%u", dwFadeLevel );
+    check_member( envelope, expect_envelope, "%u", dwFadeTime );
+
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
+
+    desc.dwFlags = 0;
+    desc.cAxes = 0;
+    desc.rgdwAxes = NULL;
+    desc.rglDirection = NULL;
+    desc.lpEnvelope = NULL;
+    desc.cbTypeSpecificParams = 0;
+    desc.lpvTypeSpecificParams = NULL;
+    flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, flags | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TRIGGERBUTTON | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_AXES | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+
+    desc.dwFlags = DIEFF_OBJECTOFFSETS;
+    desc.cAxes = 1;
+    desc.rgdwAxes = axes;
+    desc.rgdwAxes[0] = DIJOFS_X;
+    desc.dwTriggerButton = DIJOFS_BUTTON( 1 );
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_AXES | DIEP_TRIGGERBUTTON | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_AXES | DIEP_TRIGGERBUTTON | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_ALREADYINITIALIZED, "SetParameters returned %#x\n", hr );
+
+    desc.cAxes = 0;
+    desc.dwFlags = DIEFF_OBJECTIDS;
+    desc.rgdwAxes = axes;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES | DIEP_TRIGGERBUTTON );
+    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc, "%u", cAxes );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES | DIEP_TRIGGERBUTTON );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc, "%#x", dwTriggerButton );
+    check_member( desc, expect_desc, "%u", cAxes );
+    check_member( desc, expect_desc, "%u", rgdwAxes[0] );
+    check_member( desc, expect_desc, "%u", rgdwAxes[1] );
+    check_member( desc, expect_desc, "%u", rgdwAxes[2] );
+
+    desc.dwFlags = DIEFF_OBJECTOFFSETS;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES | DIEP_TRIGGERBUTTON );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    ok( desc.dwTriggerButton == 0x30, "got %#x expected %#x\n", desc.dwTriggerButton, 0x30 );
+    ok( desc.rgdwAxes[0] == 8, "got %#x expected %#x\n", desc.rgdwAxes[0], 8 );
+    ok( desc.rgdwAxes[1] == 0, "got %#x expected %#x\n", desc.rgdwAxes[1], 0 );
+    ok( desc.rgdwAxes[2] == 4, "got %#x expected %#x\n", desc.rgdwAxes[2], 4 );
+
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
+
+    desc.dwFlags = DIEFF_CARTESIAN;
+    desc.cAxes = 0;
+    desc.rglDirection = directions;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+    desc.cAxes = 3;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    desc.dwFlags = DIEFF_POLAR;
+    desc.cAxes = 3;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+
+    desc.dwFlags = DIEFF_SPHERICAL;
+    desc.cAxes = 1;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
+    ok( desc.dwFlags == DIEFF_SPHERICAL, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_SPHERICAL );
+    check_member( desc, expect_desc, "%u", cAxes );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc, "%u", cAxes );
+    ok( desc.rglDirection[0] == 3000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 3000 );
+    ok( desc.rglDirection[1] == 30000, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 30000 );
+    ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], 0 );
+    desc.dwFlags = DIEFF_CARTESIAN;
+    desc.cAxes = 2;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
+    ok( desc.dwFlags == DIEFF_CARTESIAN, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_CARTESIAN );
+    check_member( desc, expect_desc, "%u", cAxes );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc, "%u", cAxes );
+    ok( desc.rglDirection[0] == 4330, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 4330 );
+    ok( desc.rglDirection[1] == 2500, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 2500 );
+    ok( desc.rglDirection[2] == -8660, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], -8660 );
+    desc.dwFlags = DIEFF_POLAR;
+    desc.cAxes = 3;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
+
+    desc.cbTypeSpecificParams = 0;
+    desc.lpvTypeSpecificParams = &periodic;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
+    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+    desc.cbTypeSpecificParams = sizeof(DIPERIODIC);
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( periodic, expect_periodic, "%u", dwMagnitude );
+    check_member( periodic, expect_periodic, "%d", lOffset );
+    check_member( periodic, expect_periodic, "%u", dwPhase );
+    check_member( periodic, expect_periodic, "%u", dwPeriod );
+
+    hr = IDirectInputEffect_Start( effect, 1, DIES_NODOWNLOAD );
+    ok( hr == DIERR_NOTDOWNLOADED, "Start returned %#x\n", hr );
+    hr = IDirectInputEffect_Stop( effect );
+    ok( hr == DIERR_NOTDOWNLOADED, "Stop returned %#x\n", hr );
+
+    set_hid_expect( file, expect_download, 3 * sizeof(struct hid_expect) );
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DI_OK, "Download returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DI_NOEFFECT, "Download returned %#x\n", hr );
+
+    hr = IDirectInputEffect_Start( effect, 1, 0xdeadbeef );
+    ok( hr == DIERR_INVALIDPARAM, "Start returned %#x\n", hr );
+
+    set_hid_expect( file, &expect_start_solo, sizeof(expect_start_solo) );
+    hr = IDirectInputEffect_Start( effect, 1, DIES_SOLO );
+    ok( hr == DI_OK, "Start returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_stop, sizeof(expect_stop) );
+    hr = IDirectInputEffect_Stop( effect );
+    ok( hr == DI_OK, "Stop returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_start, sizeof(expect_start) );
+    hr = IDirectInputEffect_Start( effect, 1, 0 );
+    ok( hr == DI_OK, "Start returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_start_4, sizeof(expect_start_4) );
+    hr = IDirectInputEffect_Start( effect, 4, 0 );
+    ok( hr == DI_OK, "Start returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_start_0, sizeof(expect_start_4) );
+    hr = IDirectInputEffect_Start( effect, 0, 0 );
+    ok( hr == DI_OK, "Start returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_unload, sizeof(struct hid_expect) );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_OK, "Unload returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_download, 4 * sizeof(struct hid_expect) );
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_START );
+    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_unload, sizeof(struct hid_expect) );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_OK, "Unload returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_download, 3 * sizeof(struct hid_expect) );
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DI_OK, "Download returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_unload, 2 * sizeof(struct hid_expect) );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    hr = IDirectInputEffect_Start( effect, 1, DIES_NODOWNLOAD );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Start returned %#x\n", hr );
+    hr = IDirectInputEffect_Stop( effect );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Stop returned %#x\n", hr );
+
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
+
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+
+    desc.dwFlags = DIEFF_POLAR | DIEFF_OBJECTIDS;
+    desc.cAxes = 2;
+    desc.rgdwAxes[0] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR;
+    desc.rgdwAxes[1] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR;
+    desc.rglDirection[0] = 3000;
+    desc.rglDirection[1] = 0;
+    desc.rglDirection[2] = 0;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_AXES | DIEP_DIRECTION | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    desc.rglDirection[0] = 0;
+
+    desc.dwFlags = DIEFF_SPHERICAL;
+    desc.cAxes = 1;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
+    ok( desc.dwFlags == DIEFF_SPHERICAL, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_SPHERICAL );
+    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
+    ok( desc.rglDirection[0] == 30000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 30000 );
+    ok( desc.rglDirection[1] == 0, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 0 );
+    ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], 0 );
+
+    desc.dwFlags = DIEFF_CARTESIAN;
+    desc.cAxes = 1;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
+    ok( desc.dwFlags == DIEFF_CARTESIAN, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_CARTESIAN );
+    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
+    ok( desc.rglDirection[0] == 5000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 5000 );
+    ok( desc.rglDirection[1] == -8660, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], -8660 );
+    ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], 0 );
+
+    desc.dwFlags = DIEFF_POLAR;
+    desc.cAxes = 1;
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
+    ok( desc.dwFlags == DIEFF_POLAR, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_POLAR );
+    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
+    ok( desc.rglDirection[0] == 3000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 3000 );
+    ok( desc.rglDirection[1] == 0, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 0 );
+    ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], 0 );
+
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+
+    for (i = 1; i < 4; i++)
+    {
+        struct hid_expect expect_directions[] =
+        {
+            /* set periodic */
+            {
+                .code = IOCTL_HID_WRITE_REPORT,
+                .report_id = 5,
+                .report_len = 2,
+                .report_buf = {0x05,0x19},
+            },
+            /* set envelope */
+            {
+                .code = IOCTL_HID_WRITE_REPORT,
+                .report_id = 6,
+                .report_len = 7,
+                .report_buf = {0x06,0x19,0x4c,0x02,0x00,0x04,0x00},
+            },
+            /* update effect */
+            {0},
+            /* effect control */
+            {
+                .code = IOCTL_HID_WRITE_REPORT,
+                .report_id = 2,
+                .report_len = 4,
+                .report_buf = {0x02,0x01,0x03,0x00},
+            },
+        };
+        struct hid_expect expect_spherical =
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = { 0x03, 0x01, 0x02, 0x08, 0x01, 0x00, version >= 0x700 ? 0x06 : 0x00, 0x00, 0x01,
+                            i >= 2 ? 0x55 : 0, i >= 3 ? 0x1c : 0 },
+        };
+        struct hid_expect expect_cartesian =
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03, 0x01, 0x02, 0x08, 0x01, 0x00, version >= 0x700 ? 0x06 : 0x00, 0x00,
+                           0x01, i >= 2 ? 0x63 : 0, i >= 3 ? 0x1d : 0},
+        };
+        struct hid_expect expect_polar =
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03, 0x01, 0x02, 0x08, 0x01, 0x00, version >= 0x700 ? 0x06 : 0x00, 0x00,
+                           0x01, i >= 2 ? 0x3f : 0, i >= 3 ? 0x00 : 0},
+        };
+
+        winetest_push_context( "%u axes", i );
+        hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
+        ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+
+        desc.dwFlags = DIEFF_OBJECTIDS;
+        desc.cAxes = i;
+        desc.rgdwAxes[0] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR;
+        desc.rgdwAxes[1] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR;
+        desc.rgdwAxes[2] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR;
+        desc.rglDirection[0] = 0;
+        desc.rglDirection[1] = 0;
+        desc.rglDirection[2] = 0;
+        hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_AXES | DIEP_NODOWNLOAD );
+        ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+
+        desc.dwFlags = DIEFF_CARTESIAN;
+        desc.cAxes = i == 3 ? 2 : 3;
+        desc.rglDirection[0] = 1000;
+        desc.rglDirection[1] = 2000;
+        desc.rglDirection[2] = 3000;
+        hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
+        ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
+        desc.cAxes = i;
+        hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
+        ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+
+        desc.dwFlags = DIEFF_SPHERICAL;
+        desc.cAxes = i;
+        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+        ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+        desc.cAxes = 3;
+        memset( desc.rglDirection, 0xcd, 3 * sizeof(LONG) );
+        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+        ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+        ok( desc.cAxes == i, "got cAxes %u expected 2\n", desc.cAxes );
+        if (i == 1)
+        {
+            ok( desc.rglDirection[0] == 0, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 0 );
+            ok( desc.rglDirection[1] == 0xcdcdcdcd, "got rglDirection[1] %d expected %d\n",
+                desc.rglDirection[1], 0xcdcdcdcd );
+            ok( desc.rglDirection[2] == 0xcdcdcdcd, "got rglDirection[2] %d expected %d\n",
+                desc.rglDirection[2], 0xcdcdcdcd );
+        }
+        else
+        {
+            ok( desc.rglDirection[0] == 6343, "got rglDirection[0] %d expected %d\n",
+                desc.rglDirection[0], 6343 );
+            if (i == 2)
+            {
+                ok( desc.rglDirection[1] == 0, "got rglDirection[1] %d expected %d\n",
+                    desc.rglDirection[1], 0 );
+                ok( desc.rglDirection[2] == 0xcdcdcdcd, "got rglDirection[2] %d expected %d\n",
+                    desc.rglDirection[2], 0xcdcdcdcd );
+            }
+            else
+            {
+                ok( desc.rglDirection[1] == 5330, "got rglDirection[1] %d expected %d\n",
+                    desc.rglDirection[1], 5330 );
+                ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n",
+                    desc.rglDirection[2], 0 );
+            }
+        }
+
+        desc.dwFlags = DIEFF_CARTESIAN;
+        desc.cAxes = i;
+        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+        ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+        desc.cAxes = 3;
+        memset( desc.rglDirection, 0xcd, 3 * sizeof(LONG) );
+        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+        ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+        ok( desc.cAxes == i, "got cAxes %u expected 2\n", desc.cAxes );
+        ok( desc.rglDirection[0] == 1000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 1000 );
+        if (i == 1)
+            ok( desc.rglDirection[1] == 0xcdcdcdcd, "got rglDirection[1] %d expected %d\n",
+                desc.rglDirection[1], 0xcdcdcdcd );
+        else
+            ok( desc.rglDirection[1] == 2000, "got rglDirection[1] %d expected %d\n",
+                desc.rglDirection[1], 2000 );
+        if (i <= 2)
+            ok( desc.rglDirection[2] == 0xcdcdcdcd, "got rglDirection[2] %d expected %d\n",
+                desc.rglDirection[2], 0xcdcdcdcd );
+        else
+            ok( desc.rglDirection[2] == 3000, "got rglDirection[2] %d expected %d\n",
+                desc.rglDirection[2], 3000 );
+
+        desc.dwFlags = DIEFF_POLAR;
+        desc.cAxes = 1;
+        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+        if (i != 2) ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+        else ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
+        desc.cAxes = 3;
+        memset( desc.rglDirection, 0xcd, 3 * sizeof(LONG) );
+        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
+        if (i != 2) ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
+        else
+        {
+            ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+            ok( desc.cAxes == i, "got cAxes %u expected 2\n", desc.cAxes );
+            ok( desc.rglDirection[0] == 15343, "got rglDirection[0] %d expected %d\n",
+                desc.rglDirection[0], 15343 );
+            ok( desc.rglDirection[1] == 0, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 0 );
+            ok( desc.rglDirection[2] == 0xcdcdcdcd, "got rglDirection[2] %d expected %d\n",
+                desc.rglDirection[2], 0xcdcdcdcd );
+        }
+
+        ref = IDirectInputEffect_Release( effect );
+        ok( ref == 0, "Release returned %d\n", ref );
+
+        desc = expect_desc;
+        desc.dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS;
+        desc.cAxes = i;
+        desc.rgdwAxes = axes;
+        desc.rglDirection = directions;
+        desc.rglDirection[0] = 3000;
+        desc.rglDirection[1] = 4000;
+        desc.rglDirection[2] = 5000;
+        flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
+        expect_directions[2] = expect_spherical;
+        set_hid_expect( file, expect_directions, sizeof(expect_directions) );
+        hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, &desc, &effect, NULL );
+        ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+        ref = IDirectInputEffect_Release( effect );
+        ok( ref == 0, "Release returned %d\n", ref );
+        set_hid_expect( file, NULL, 0 );
+
+        desc = expect_desc;
+        desc.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTIDS;
+        desc.cAxes = i;
+        desc.rgdwAxes = axes;
+        desc.rglDirection = directions;
+        desc.rglDirection[0] = 6000;
+        desc.rglDirection[1] = 7000;
+        desc.rglDirection[2] = 8000;
+        flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
+        expect_directions[2] = expect_cartesian;
+        set_hid_expect( file, expect_directions, sizeof(expect_directions) );
+        hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, &desc, &effect, NULL );
+        ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+        ref = IDirectInputEffect_Release( effect );
+        ok( ref == 0, "Release returned %d\n", ref );
+        set_hid_expect( file, NULL, 0 );
+
+        if (i == 2)
+        {
+            desc = expect_desc;
+            desc.dwFlags = DIEFF_POLAR | DIEFF_OBJECTIDS;
+            desc.cAxes = i;
+            desc.rgdwAxes = axes;
+            desc.rglDirection = directions;
+            desc.rglDirection[0] = 9000;
+            desc.rglDirection[1] = 10000;
+            desc.rglDirection[2] = 11000;
+            flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
+            expect_directions[2] = expect_polar;
+            set_hid_expect( file, expect_directions, sizeof(expect_directions) );
+            hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, &desc, &effect, NULL );
+            ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+            ref = IDirectInputEffect_Release( effect );
+            ok( ref == 0, "Release returned %d\n", ref );
+            set_hid_expect( file, NULL, 0 );
+        }
+
+        winetest_pop_context();
+    }
+
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+
+    set_hid_expect( file, expect_download_2, sizeof(expect_download_2) );
+    flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, flags );
+    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    desc = expect_desc;
+    desc.dwDuration = INFINITE;
+    desc.dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD | DIEP_DURATION | DIEP_TRIGGERBUTTON );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    set_hid_expect( file, expect_update, sizeof(expect_update) );
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, 0 );
+    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* these updates are sent asynchronously */
+    desc = expect_desc;
+    desc.dwDuration = INFINITE;
+    desc.dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD | DIEP_DURATION | DIEP_TRIGGERBUTTON );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    set_hid_expect( file, expect_update, sizeof(expect_update) );
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, 0 );
+    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* these updates are sent asynchronously */
+
+    desc = expect_desc;
+    desc.lpEnvelope = &envelope;
+    desc.lpEnvelope->dwAttackTime = 1000;
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD | DIEP_ENVELOPE );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    set_hid_expect( file, expect_set_envelope, sizeof(expect_set_envelope) );
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, 0 );
+    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* these updates are sent asynchronously */
+
+    set_hid_expect( file, &expect_stop, sizeof(expect_stop) );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, &expect_desc, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+}
+
+static void test_condition_effect( IDirectInputDevice8W *device, HANDLE file, DWORD version )
+{
+    struct hid_expect expect_create[] =
+    {
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 7,
+            .report_len = 8,
+            .report_buf = {0x07,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 7,
+            .report_len = 8,
+            .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0x00},
+        },
+    };
+    struct hid_expect expect_create_1[] =
+    {
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 7,
+            .report_len = 8,
+            .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x3f,0x00},
+        },
+    };
+    struct hid_expect expect_create_2[] =
+    {
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 7,
+            .report_len = 8,
+            .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0xf1},
+        },
+    };
+    struct hid_expect expect_create_3[] =
+    {
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 7,
+            .report_len = 8,
+            .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0x00},
+        },
+    };
+    struct hid_expect expect_destroy =
+    {
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {0x02, 0x01, 0x03, 0x00},
+    };
+    static const DWORD expect_axes[3] =
+    {
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR,
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR,
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR,
+    };
+    static const LONG expect_directions[3] = {
+        +3000,
+        0,
+        0,
+    };
+    static const DIENVELOPE expect_envelope =
+    {
+        .dwSize = sizeof(DIENVELOPE),
+        .dwAttackLevel = 1000,
+        .dwAttackTime = 2000,
+        .dwFadeLevel = 3000,
+        .dwFadeTime = 4000,
+    };
+    static const DICONDITION expect_condition[3] =
+    {
+        {
+            .lOffset = -500,
+            .lPositiveCoefficient = 2000,
+            .lNegativeCoefficient = -3000,
+            .dwPositiveSaturation = -4000,
+            .dwNegativeSaturation = -5000,
+            .lDeadBand = 6000,
+        },
+        {
+            .lOffset = 6000,
+            .lPositiveCoefficient = 5000,
+            .lNegativeCoefficient = -4000,
+            .dwPositiveSaturation = 3000,
+            .dwNegativeSaturation = 2000,
+            .lDeadBand = 1000,
+        },
+        {
+            .lOffset = -7000,
+            .lPositiveCoefficient = -8000,
+            .lNegativeCoefficient = 9000,
+            .dwPositiveSaturation = 10000,
+            .dwNegativeSaturation = 11000,
+            .lDeadBand = -12000,
+        },
+    };
+    const DIEFFECT expect_desc =
+    {
+        .dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5),
+        .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS,
+        .dwDuration = 1000,
+        .dwSamplePeriod = 2000,
+        .dwGain = 3000,
+        .dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
+        .dwTriggerRepeatInterval = 5000,
+        .cAxes = 2,
+        .rgdwAxes = (void *)expect_axes,
+        .rglDirection = (void *)expect_directions,
+        .lpEnvelope = (void *)&expect_envelope,
+        .cbTypeSpecificParams = 2 * sizeof(DICONDITION),
+        .lpvTypeSpecificParams = (void *)expect_condition,
+        .dwStartDelay = 6000,
+    };
+    struct check_created_effect_params check_params = {0};
+    DIENVELOPE envelope = {.dwSize = sizeof(DIENVELOPE)};
+    DICONDITION condition[2] = {0};
+    IDirectInputEffect *effect;
+    LONG directions[4] = {0};
+    DWORD axes[4] = {0};
+    DIEFFECT desc =
+    {
+        .dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5),
+        .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS,
+        .cAxes = 4,
+        .rgdwAxes = axes,
+        .rglDirection = directions,
+        .lpEnvelope = &envelope,
+        .cbTypeSpecificParams = 2 * sizeof(DICONDITION),
+        .lpvTypeSpecificParams = condition,
+    };
+    HRESULT hr;
+    ULONG ref;
+    GUID guid;
+
+    set_hid_expect( file, expect_create, sizeof(expect_create) );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &expect_desc, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    check_params.expect_effect = effect;
+    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_created_effect_objects, &check_params, 0 );
+    ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#x\n", hr );
+    ok( check_params.count == 1, "got count %u, expected 1\n", check_params.count );
+
+    hr = IDirectInputEffect_GetEffectGuid( effect, &guid );
+    ok( hr == DI_OK, "GetEffectGuid returned %#x\n", hr );
+    ok( IsEqualGUID( &guid, &GUID_Spring ), "got guid %s, expected %s\n", debugstr_guid( &guid ),
+        debugstr_guid( &GUID_Spring ) );
+
+    hr = IDirectInputEffect_GetParameters( effect, &desc, version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5 );
+    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
+    check_member( desc, expect_desc, "%u", dwDuration );
+    check_member( desc, expect_desc, "%u", dwSamplePeriod );
+    check_member( desc, expect_desc, "%u", dwGain );
+    check_member( desc, expect_desc, "%#x", dwTriggerButton );
+    check_member( desc, expect_desc, "%u", dwTriggerRepeatInterval );
+    check_member( desc, expect_desc, "%u", cAxes );
+    check_member( desc, expect_desc, "%#x", rgdwAxes[0] );
+    check_member( desc, expect_desc, "%#x", rgdwAxes[1] );
+    check_member( desc, expect_desc, "%d", rglDirection[0] );
+    check_member( desc, expect_desc, "%d", rglDirection[1] );
+    check_member( desc, expect_desc, "%u", cbTypeSpecificParams );
+    if (version >= 0x700) check_member( desc, expect_desc, "%u", dwStartDelay );
+    else ok( desc.dwStartDelay == 0, "got dwStartDelay %#x\n", desc.dwStartDelay );
+    check_member( envelope, expect_envelope, "%u", dwAttackLevel );
+    check_member( envelope, expect_envelope, "%u", dwAttackTime );
+    check_member( envelope, expect_envelope, "%u", dwFadeLevel );
+    check_member( envelope, expect_envelope, "%u", dwFadeTime );
+    check_member( condition[0], expect_condition[0], "%d", lOffset );
+    check_member( condition[0], expect_condition[0], "%d", lPositiveCoefficient );
+    check_member( condition[0], expect_condition[0], "%d", lNegativeCoefficient );
+    check_member( condition[0], expect_condition[0], "%u", dwPositiveSaturation );
+    check_member( condition[0], expect_condition[0], "%u", dwNegativeSaturation );
+    check_member( condition[0], expect_condition[0], "%d", lDeadBand );
+    check_member( condition[1], expect_condition[1], "%d", lOffset );
+    check_member( condition[1], expect_condition[1], "%d", lPositiveCoefficient );
+    check_member( condition[1], expect_condition[1], "%d", lNegativeCoefficient );
+    check_member( condition[1], expect_condition[1], "%u", dwPositiveSaturation );
+    check_member( condition[1], expect_condition[1], "%u", dwNegativeSaturation );
+    check_member( condition[1], expect_condition[1], "%d", lDeadBand );
+
+    set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, NULL, 0 );
+
+    desc = expect_desc;
+    desc.cAxes = 1;
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
+    ok( hr == DIERR_INVALIDPARAM, "CreateEffect returned %#x\n", hr );
+    desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION);
+    desc.lpvTypeSpecificParams = (void *)&expect_condition[1];
+    set_hid_expect( file, expect_create_1, sizeof(expect_create_1) );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, NULL, 0 );
+
+    desc = expect_desc;
+    desc.cAxes = 3;
+    desc.rglDirection = directions;
+    desc.rglDirection[0] = +3000;
+    desc.rglDirection[1] = -2000;
+    desc.rglDirection[2] = +1000;
+    desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION);
+    desc.lpvTypeSpecificParams = (void *)&expect_condition[1];
+    set_hid_expect( file, expect_create_2, sizeof(expect_create_2) );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, NULL, 0 );
+
+    desc = expect_desc;
+    desc.cAxes = 2;
+    desc.rgdwAxes = axes;
+    desc.rgdwAxes[0] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR;
+    desc.rgdwAxes[1] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR;
+    desc.rglDirection = directions;
+    desc.rglDirection[0] = +3000;
+    desc.rglDirection[1] = -2000;
+    desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION);
+    desc.lpvTypeSpecificParams = (void *)&expect_condition[1];
+    set_hid_expect( file, expect_create_3, sizeof(expect_create_3) );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, NULL, 0 );
+
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, NULL, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    desc = expect_desc;
+    desc.cAxes = 0;
+    desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION);
+    desc.lpvTypeSpecificParams = (void *)&expect_condition[0];
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    desc.cbTypeSpecificParams = 0 * sizeof(DICONDITION);
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS );
+    ok( hr == DIERR_MOREDATA, "SetParameters returned %#x\n", hr );
+    ok( desc.cbTypeSpecificParams == 1 * sizeof(DICONDITION), "got %u\n", desc.cbTypeSpecificParams );
+    desc.cbTypeSpecificParams = 0 * sizeof(DICONDITION);
+    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+    desc.cbTypeSpecificParams = 0 * sizeof(DICONDITION);
+    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS );
+    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
+    ok( desc.cbTypeSpecificParams == 0 * sizeof(DICONDITION), "got %u\n", desc.cbTypeSpecificParams );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+}
+
+static BOOL test_force_feedback_joystick( DWORD version )
+{
+#include "psh_hid_macros.h"
+    const unsigned char report_descriptor[] = {
+        USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
+        USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
+        COLLECTION(1, Application),
+            USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 1),
+
+                USAGE(1, HID_USAGE_GENERIC_X),
+                USAGE(1, HID_USAGE_GENERIC_Y),
+                USAGE(1, HID_USAGE_GENERIC_Z),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 3),
+                INPUT(1, Data|Var|Abs),
+
+                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
+                USAGE_MINIMUM(1, 1),
+                USAGE_MAXIMUM(1, 2),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 2),
+                INPUT(1, Data|Var|Abs),
+                REPORT_COUNT(1, 6),
+                INPUT(1, Cnst|Var|Abs),
+            END_COLLECTION,
+
+            USAGE_PAGE(1, HID_USAGE_PAGE_PID),
+            USAGE(1, PID_USAGE_STATE_REPORT),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 2),
+
+                USAGE(1, PID_USAGE_DEVICE_PAUSED),
+                USAGE(1, PID_USAGE_ACTUATORS_ENABLED),
+                USAGE(1, PID_USAGE_SAFETY_SWITCH),
+                USAGE(1, PID_USAGE_ACTUATOR_OVERRIDE_SWITCH),
+                USAGE(1, PID_USAGE_ACTUATOR_POWER),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 5),
+                INPUT(1, Data|Var|Abs),
+                REPORT_COUNT(1, 3),
+                INPUT(1, Cnst|Var|Abs),
+
+                USAGE(1, PID_USAGE_EFFECT_PLAYING),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 1),
+                INPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                LOGICAL_MINIMUM(1, 0x00),
+                REPORT_SIZE(1, 7),
+                REPORT_COUNT(1, 1),
+                INPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE_PAGE(1, HID_USAGE_PAGE_PID),
+            USAGE(1, PID_USAGE_DEVICE_CONTROL_REPORT),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 1),
+
+                USAGE(1, PID_USAGE_DEVICE_CONTROL),
+                COLLECTION(1, Logical),
+                    USAGE(1, PID_USAGE_DC_DEVICE_RESET),
+                    LOGICAL_MINIMUM(1, 1),
+                    LOGICAL_MAXIMUM(1, 2),
+                    PHYSICAL_MINIMUM(1, 1),
+                    PHYSICAL_MAXIMUM(1, 2),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 1),
+                    OUTPUT(1, Data|Ary|Abs),
+                END_COLLECTION,
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_EFFECT_OPERATION_REPORT),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 2),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_EFFECT_OPERATION),
+                COLLECTION(1, NamedArray),
+                    USAGE(1, PID_USAGE_OP_EFFECT_START),
+                    USAGE(1, PID_USAGE_OP_EFFECT_START_SOLO),
+                    USAGE(1, PID_USAGE_OP_EFFECT_STOP),
+                    LOGICAL_MINIMUM(1, 1),
+                    LOGICAL_MAXIMUM(1, 3),
+                    PHYSICAL_MINIMUM(1, 1),
+                    PHYSICAL_MAXIMUM(1, 3),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 1),
+                    OUTPUT(1, Data|Ary|Abs),
+                END_COLLECTION,
+
+                USAGE(1, PID_USAGE_LOOP_COUNT),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_SET_EFFECT_REPORT),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 3),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_EFFECT_TYPE),
+                COLLECTION(1, NamedArray),
+                    USAGE(1, PID_USAGE_ET_SQUARE),
+                    USAGE(1, PID_USAGE_ET_SINE),
+                    USAGE(1, PID_USAGE_ET_SPRING),
+                    LOGICAL_MINIMUM(1, 1),
+                    LOGICAL_MAXIMUM(1, 3),
+                    PHYSICAL_MINIMUM(1, 1),
+                    PHYSICAL_MAXIMUM(1, 3),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 1),
+                    OUTPUT(1, Data|Ary|Abs),
+                END_COLLECTION,
+
+                USAGE(1, PID_USAGE_AXES_ENABLE),
+                COLLECTION(1, Logical),
+                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_X),
+                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Y),
+                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Z),
+                    LOGICAL_MINIMUM(1, 0),
+                    LOGICAL_MAXIMUM(1, 1),
+                    PHYSICAL_MINIMUM(1, 0),
+                    PHYSICAL_MAXIMUM(1, 1),
+                    REPORT_SIZE(1, 1),
+                    REPORT_COUNT(1, 3),
+                    OUTPUT(1, Data|Var|Abs),
+                END_COLLECTION,
+                USAGE(1, PID_USAGE_DIRECTION_ENABLE),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+                REPORT_COUNT(1, 4),
+                OUTPUT(1, Cnst|Var|Abs),
+
+                USAGE(1, PID_USAGE_DURATION),
+                USAGE(1, PID_USAGE_START_DELAY),
+                UNIT(2, 0x1003),      /* Eng Lin:Time */
+                UNIT_EXPONENT(1, -3), /* 10^-3 */
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x7fff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x7fff),
+                REPORT_SIZE(1, 16),
+                REPORT_COUNT(1, 2),
+                OUTPUT(1, Data|Var|Abs),
+                UNIT(1, 0),
+                UNIT_EXPONENT(1, 0),
+
+                USAGE(1, PID_USAGE_TRIGGER_BUTTON),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 0x08),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(1, 0x08),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_DIRECTION),
+                COLLECTION(1, Logical),
+                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1),
+                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2),
+                    UNIT(1, 0x14),        /* Eng Rot:Angular Pos */
+                    UNIT_EXPONENT(1, -2), /* 10^-2 */
+                    LOGICAL_MINIMUM(1, 0),
+                    LOGICAL_MAXIMUM(2, 0x00ff),
+                    PHYSICAL_MINIMUM(1, 0),
+                    PHYSICAL_MAXIMUM(4, 0x00008ca0),
+                    UNIT(1, 0),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 2),
+                    OUTPUT(1, Data|Var|Abs),
+                    UNIT_EXPONENT(1, 0),
+                    UNIT(1, 0),
+                END_COLLECTION,
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_SET_PERIODIC_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 5),
+
+                USAGE(1, PID_USAGE_MAGNITUDE),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x00ff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_SET_ENVELOPE_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 6),
+
+                USAGE(1, PID_USAGE_ATTACK_LEVEL),
+                USAGE(1, PID_USAGE_FADE_LEVEL),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x00ff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 2),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_ATTACK_TIME),
+                USAGE(1, PID_USAGE_FADE_TIME),
+                UNIT(2, 0x1003),      /* Eng Lin:Time */
+                UNIT_EXPONENT(1, -3), /* 10^-3 */
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x7fff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x7fff),
+                REPORT_SIZE(1, 16),
+                REPORT_COUNT(1, 2),
+                OUTPUT(1, Data|Var|Abs),
+                PHYSICAL_MAXIMUM(1, 0),
+                UNIT_EXPONENT(1, 0),
+                UNIT(1, 0),
+            END_COLLECTION,
+
+
+            USAGE(1, PID_USAGE_SET_CONDITION_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 7),
+
+                USAGE(1, PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET),
+                COLLECTION(1, Logical),
+                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1),
+                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2),
+                    LOGICAL_MINIMUM(1, 0),
+                    LOGICAL_MAXIMUM(1, 1),
+                    PHYSICAL_MINIMUM(1, 0),
+                    PHYSICAL_MAXIMUM(1, 1),
+                    REPORT_SIZE(1, 2),
+                    REPORT_COUNT(1, 2),
+                    OUTPUT(1, Data|Var|Abs),
+                END_COLLECTION,
+                REPORT_SIZE(1, 4),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Cnst|Var|Abs),
+
+                USAGE(1, PID_USAGE_CP_OFFSET),
+                LOGICAL_MINIMUM(1, 0x80),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(2, 0xd8f0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_POSITIVE_COEFFICIENT),
+                USAGE(1, PID_USAGE_NEGATIVE_COEFFICIENT),
+                LOGICAL_MINIMUM(1, 0x80),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(2, 0xd8f0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 2),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_POSITIVE_SATURATION),
+                USAGE(1, PID_USAGE_NEGATIVE_SATURATION),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x00ff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 2),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_DEAD_BAND),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x00ff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+
+            USAGE(1, PID_USAGE_DEVICE_GAIN_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 8),
+
+                USAGE(1, PID_USAGE_DEVICE_GAIN),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x00ff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+        END_COLLECTION,
+    };
+#undef REPORT_ID_OR_USAGE_PAGE
+#include "pop_hid_macros.h"
+
+    static const HIDP_CAPS hid_caps =
+    {
+        .InputReportByteLength = 5,
+    };
+    const DIDEVCAPS expect_caps =
+    {
+        .dwSize = sizeof(DIDEVCAPS),
+        .dwFlags = DIDC_FORCEFEEDBACK | DIDC_ATTACHED | DIDC_EMULATED | DIDC_STARTDELAY |
+                   DIDC_FFFADE | DIDC_FFATTACK | DIDC_DEADBAND | DIDC_SATURATION,
+        .dwDevType = version >= 0x800 ? DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK
+                                      : DIDEVTYPE_HID | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_JOYSTICK,
+        .dwAxes = 3,
+        .dwButtons = 2,
+        .dwFFSamplePeriod = 1000000,
+        .dwFFMinTimeResolution = 1000000,
+        .dwHardwareRevision = 1,
+        .dwFFDriverVersion = 1,
+    };
+    struct hid_expect expect_acquire[] =
+    {
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x01},
+        },
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 8,
+            .report_len = 2,
+            .report_buf = {8, 0x19},
+        },
+    };
+    struct hid_expect expect_reset[] =
+    {
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x01},
+        },
+    };
+    struct hid_expect expect_set_device_gain_1 =
+    {
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 8,
+        .report_len = 2,
+        .report_buf = {8, 0x19},
+    };
+    struct hid_expect expect_set_device_gain_2 =
+    {
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 8,
+        .report_len = 2,
+        .report_buf = {8, 0x33},
+    };
+
+    const DIDEVICEINSTANCEW expect_devinst =
+    {
+        .dwSize = sizeof(DIDEVICEINSTANCEW),
+        .guidInstance = expect_guid_product,
+        .guidProduct = expect_guid_product,
+        .dwDevType = version >= 0x800 ? DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK
+                                      : DIDEVTYPE_HID | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_JOYSTICK,
+        .tszInstanceName = L"Wine test root driver",
+        .tszProductName = L"Wine test root driver",
+        .guidFFDriver = IID_IDirectInputPIDDriver,
+        .wUsagePage = HID_USAGE_PAGE_GENERIC,
+        .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+    };
+    const DIDEVICEOBJECTINSTANCEW expect_objects_5[] =
+    {
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_XAxis,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(0)|DIDFT_FFACTUATOR,
+            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
+            .tszName = L"X Axis",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_X,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_YAxis,
+            .dwOfs = 0x4,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(1)|DIDFT_FFACTUATOR,
+            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
+            .tszName = L"Y Axis",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_Y,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_ZAxis,
+            .dwOfs = 0x8,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(2)|DIDFT_FFACTUATOR,
+            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
+            .tszName = L"Z Axis",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_Z,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Button,
+            .dwOfs = 0x30,
+            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(0)|DIDFT_FFEFFECTTRIGGER,
+            .dwFlags = DIDOI_FFEFFECTTRIGGER,
+            .tszName = L"Button 0",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_BUTTON,
+            .wUsage = 0x1,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Button,
+            .dwOfs = 0x31,
+            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(1)|DIDFT_FFEFFECTTRIGGER,
+            .dwFlags = DIDOI_FFEFFECTTRIGGER,
+            .tszName = L"Button 1",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_BUTTON,
+            .wUsage = 0x2,
+            .wReportId = 1,
+        },
+    };
+    const DIDEVICEOBJECTINSTANCEW expect_objects[] =
+    {
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_ZAxis,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(2)|DIDFT_FFACTUATOR,
+            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
+            .tszName = L"Z Axis",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_Z,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_YAxis,
+            .dwOfs = 0x4,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(1)|DIDFT_FFACTUATOR,
+            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
+            .tszName = L"Y Axis",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_Y,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_XAxis,
+            .dwOfs = 0x8,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(0)|DIDFT_FFACTUATOR,
+            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
+            .tszName = L"X Axis",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_X,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Button,
+            .dwOfs = version >= 0x800 ? 0x68 : 0x10,
+            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(0)|DIDFT_FFEFFECTTRIGGER,
+            .dwFlags = DIDOI_FFEFFECTTRIGGER,
+            .tszName = L"Button 0",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_BUTTON,
+            .wUsage = 0x1,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Button,
+            .dwOfs = version >= 0x800 ? 0x69 : 0x11,
+            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(1)|DIDFT_FFEFFECTTRIGGER,
+            .dwFlags = DIDOI_FFEFFECTTRIGGER,
+            .tszName = L"Button 1",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_BUTTON,
+            .wUsage = 0x2,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x70 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(12)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"DC Device Reset",
+            .wCollectionNumber = 4,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DC_DEVICE_RESET,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x10 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(13)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Effect Block Index",
+            .wCollectionNumber = 5,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_EFFECT_BLOCK_INDEX,
+            .wReportId = 2,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x71 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(14)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Op Effect Start",
+            .wCollectionNumber = 6,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_OP_EFFECT_START,
+            .wReportId = 2,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x72 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(15)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Op Effect Start Solo",
+            .wCollectionNumber = 6,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_OP_EFFECT_START_SOLO,
+            .wReportId = 2,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x73 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(16)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Op Effect Stop",
+            .wCollectionNumber = 6,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_OP_EFFECT_STOP,
+            .wReportId = 2,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x14 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(17)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Loop Count",
+            .wCollectionNumber = 5,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_LOOP_COUNT,
+            .wReportId = 2,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x18 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(18)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Effect Block Index",
+            .wCollectionNumber = 7,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_EFFECT_BLOCK_INDEX,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x74 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(19)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"ET Square",
+            .wCollectionNumber = 8,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_ET_SQUARE,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x75 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(20)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"ET Sine",
+            .wCollectionNumber = 8,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_ET_SINE,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x76 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(21)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"ET Spring",
+            .wCollectionNumber = 8,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_ET_SPRING,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x77 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(22)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Z Axis",
+            .wCollectionNumber = 9,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_Z,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x78 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(23)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Y Axis",
+            .wCollectionNumber = 9,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_Y,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x79 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(24)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"X Axis",
+            .wCollectionNumber = 9,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_X,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x7a : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(25)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Direction Enable",
+            .wCollectionNumber = 7,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DIRECTION_ENABLE,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x1c : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(26)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Start Delay",
+            .wCollectionNumber = 7,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_START_DELAY,
+            .wReportId = 3,
+            .dwDimension = 0x1003,
+            .wExponent = -3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x20 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(27)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Duration",
+            .wCollectionNumber = 7,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DURATION,
+            .wReportId = 3,
+            .dwDimension = 0x1003,
+            .wExponent = -3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x24 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(28)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Trigger Button",
+            .wCollectionNumber = 7,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_TRIGGER_BUTTON,
+            .wReportId = 3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x28 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(29)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Unknown 29",
+            .wCollectionNumber = 10,
+            .wUsagePage = HID_USAGE_PAGE_ORDINAL,
+            .wUsage = 2,
+            .wReportId = 3,
+            .wExponent = -2,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x2c : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(30)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Unknown 30",
+            .wCollectionNumber = 10,
+            .wUsagePage = HID_USAGE_PAGE_ORDINAL,
+            .wUsage = 1,
+            .wReportId = 3,
+            .wExponent = -2,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x30 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(31)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Magnitude",
+            .wCollectionNumber = 11,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_MAGNITUDE,
+            .wReportId = 5,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x34 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(32)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Fade Level",
+            .wCollectionNumber = 12,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_FADE_LEVEL,
+            .wReportId = 6,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x38 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(33)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Attack Level",
+            .wCollectionNumber = 12,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_ATTACK_LEVEL,
+            .wReportId = 6,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x3c : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(34)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Fade Time",
+            .wCollectionNumber = 12,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_FADE_TIME,
+            .wReportId = 6,
+            .dwDimension = 0x1003,
+            .wExponent = -3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x40 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(35)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Attack Time",
+            .wCollectionNumber = 12,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_ATTACK_TIME,
+            .wReportId = 6,
+            .dwDimension = 0x1003,
+            .wExponent = -3,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x44 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(36)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Unknown 36",
+            .wCollectionNumber = 14,
+            .wUsagePage = HID_USAGE_PAGE_ORDINAL,
+            .wUsage = 2,
+            .wReportId = 7,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x48 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(37)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Unknown 37",
+            .wCollectionNumber = 14,
+            .wUsagePage = HID_USAGE_PAGE_ORDINAL,
+            .wUsage = 1,
+            .wReportId = 7,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x4c : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(38)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"CP Offset",
+            .wCollectionNumber = 13,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_CP_OFFSET,
+            .wReportId = 7,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x50 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(39)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Negative Coefficient",
+            .wCollectionNumber = 13,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_NEGATIVE_COEFFICIENT,
+            .wReportId = 7,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x54 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(40)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Positive Coefficient",
+            .wCollectionNumber = 13,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_POSITIVE_COEFFICIENT,
+            .wReportId = 7,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x58 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(41)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Negative Saturation",
+            .wCollectionNumber = 13,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_NEGATIVE_SATURATION,
+            .wReportId = 7,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x5c : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(42)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Positive Saturation",
+            .wCollectionNumber = 13,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_POSITIVE_SATURATION,
+            .wReportId = 7,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x60 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(43)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Dead Band",
+            .wCollectionNumber = 13,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DEAD_BAND,
+            .wReportId = 7,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = version >= 0x800 ? 0x64 : 0,
+            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(44)|DIDFT_OUTPUT,
+            .dwFlags = 0x80008000,
+            .tszName = L"Device Gain",
+            .wCollectionNumber = 15,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DEVICE_GAIN,
+            .wReportId = 8,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(0),
+            .tszName = L"Collection 0 - Joystick",
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(1),
+            .tszName = L"Collection 1 - Joystick",
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(2),
+            .tszName = L"Collection 2 - PID State Report",
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_STATE_REPORT,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(3),
+            .tszName = L"Collection 3 - PID Device Control Report",
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DEVICE_CONTROL_REPORT,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(4),
+            .tszName = L"Collection 4 - PID Device Control",
+            .wCollectionNumber = 3,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DEVICE_CONTROL,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(5),
+            .tszName = L"Collection 5 - Effect Operation Report",
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_EFFECT_OPERATION_REPORT,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(6),
+            .tszName = L"Collection 6 - Effect Operation",
+            .wCollectionNumber = 5,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_EFFECT_OPERATION,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(7),
+            .tszName = L"Collection 7 - Set Effect Report",
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_SET_EFFECT_REPORT,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(8),
+            .tszName = L"Collection 8 - Effect Type",
+            .wCollectionNumber = 7,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_EFFECT_TYPE,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(9),
+            .tszName = L"Collection 9 - Axes Enable",
+            .wCollectionNumber = 7,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_AXES_ENABLE,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(10),
+            .tszName = L"Collection 10 - Direction",
+            .wCollectionNumber = 7,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DIRECTION,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(11),
+            .tszName = L"Collection 11 - Set Periodic Report",
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_SET_PERIODIC_REPORT,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(12),
+            .tszName = L"Collection 12 - Set Envelope Report",
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_SET_ENVELOPE_REPORT,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(13),
+            .tszName = L"Collection 13 - Set Condition Report",
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_SET_CONDITION_REPORT,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(14),
+            .tszName = L"Collection 14 - Type Specific Block Offset",
+            .wCollectionNumber = 13,
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(15),
+            .tszName = L"Collection 15 - Device Gain Report",
+            .wUsagePage = HID_USAGE_PAGE_PID,
+            .wUsage = PID_USAGE_DEVICE_GAIN_REPORT,
+        },
+    };
+    const DIEFFECTINFOW expect_effects[] =
+    {
+        {
+            .dwSize = sizeof(DIEFFECTINFOW),
+            .guid = GUID_Square,
+            .dwEffType = DIEFT_PERIODIC | DIEFT_STARTDELAY | DIEFT_FFFADE | DIEFT_FFATTACK,
+            .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
+                              DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE,
+            .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
+                              DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE,
+            .tszName = L"GUID_Square",
+        },
+        {
+            .dwSize = sizeof(DIEFFECTINFOW),
+            .guid = GUID_Sine,
+            .dwEffType = DIEFT_PERIODIC | DIEFT_STARTDELAY | DIEFT_FFFADE | DIEFT_FFATTACK,
+            .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
+                              DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE,
+            .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
+                              DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE,
+            .tszName = L"GUID_Sine",
+        },
+        {
+            .dwSize = sizeof(DIEFFECTINFOW),
+            .guid = GUID_Spring,
+            .dwEffType = DIEFT_CONDITION | DIEFT_STARTDELAY | DIEFT_DEADBAND | DIEFT_SATURATION,
+            .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
+                              DIEP_DURATION,
+            .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
+                              DIEP_DURATION,
+            .tszName = L"GUID_Spring",
+        }
+    };
+
+    struct check_objects_todos todo_objects_5[ARRAY_SIZE(expect_objects_5)] =
+    {
+        {.guid = TRUE, .type = TRUE, .usage = TRUE, .name = TRUE},
+        {0},
+        {.guid = TRUE, .type = TRUE, .usage = TRUE, .name = TRUE},
+    };
+    struct check_objects_params check_objects_params =
+    {
+        .version = version,
+        .expect_count = version < 0x700 ? ARRAY_SIZE(expect_objects_5) : ARRAY_SIZE(expect_objects),
+        .expect_objs = version < 0x700 ? expect_objects_5 : expect_objects,
+        .todo_objs = version < 0x700 ? todo_objects_5 : NULL,
+        .todo_extra = version < 0x700 ? TRUE : FALSE,
+    };
+    struct check_effects_params check_effects_params =
+    {
+        .expect_count = ARRAY_SIZE(expect_effects),
+        .expect_effects = expect_effects,
+    };
+    DIPROPDWORD prop_dword =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPDWORD),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    DIPROPGUIDANDPATH prop_guid_path =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPGUIDANDPATH),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
+    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
+    IDirectInputDevice8W *device = NULL;
+    DIDEVICEOBJECTDATA objdata = {0};
+    DIEFFECTINFOW effectinfo = {0};
+    DIEFFESCAPE escape = {0};
+    DIDEVCAPS caps = {0};
+    char buffer[1024];
+    ULONG res, ref;
+    HANDLE file;
+    HRESULT hr;
+    HWND hwnd;
+
+    winetest_push_context( "%#x", version );
+
+    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
+    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
+    SetCurrentDirectoryW( tempdir );
+
+    cleanup_registry_keys();
+    if (!dinput_driver_start( report_descriptor, sizeof(report_descriptor), &hid_caps, NULL, 0 )) goto done;
+    if (FAILED(hr = dinput_test_create_device( version, &devinst, &device ))) goto done;
+
+    check_dinput_devices( version, &devinst );
+
+    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
+    ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
+    check_member( devinst, expect_devinst, "%d", dwSize );
+    todo_wine
+    check_member_guid( devinst, expect_devinst, guidInstance );
+    check_member_guid( devinst, expect_devinst, guidProduct );
+    check_member( devinst, expect_devinst, "%#x", dwDevType );
+    todo_wine
+    check_member_wstr( devinst, expect_devinst, tszInstanceName );
+    todo_wine
+    check_member_wstr( devinst, expect_devinst, tszProductName );
+    check_member_guid( devinst, expect_devinst, guidFFDriver );
+    check_member( devinst, expect_devinst, "%04x", wUsagePage );
+    check_member( devinst, expect_devinst, "%04x", wUsage );
+
+    caps.dwSize = sizeof(DIDEVCAPS);
+    hr = IDirectInputDevice8_GetCapabilities( device, &caps );
+    ok( hr == DI_OK, "GetCapabilities returned %#x\n", hr );
+    check_member( caps, expect_caps, "%d", dwSize );
+    check_member( caps, expect_caps, "%#x", dwFlags );
+    check_member( caps, expect_caps, "%#x", dwDevType );
+    check_member( caps, expect_caps, "%d", dwAxes );
+    check_member( caps, expect_caps, "%d", dwButtons );
+    check_member( caps, expect_caps, "%d", dwPOVs );
+    check_member( caps, expect_caps, "%d", dwFFSamplePeriod );
+    check_member( caps, expect_caps, "%d", dwFFMinTimeResolution );
+    check_member( caps, expect_caps, "%d", dwFirmwareRevision );
+    check_member( caps, expect_caps, "%d", dwHardwareRevision );
+    check_member( caps, expect_caps, "%d", dwFFDriverVersion );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_FFGAIN returned %#x\n", hr );
+    ok( prop_dword.dwData == 10000, "got %u expected %u\n", prop_dword.dwData, 10000 );
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_EnumObjects( device, check_objects, &check_objects_params, DIDFT_ALL );
+    ok( hr == DI_OK, "EnumObjects returned %#x\n", hr );
+    ok( check_objects_params.index >= check_objects_params.expect_count, "missing %u objects\n",
+        check_objects_params.expect_count - check_objects_params.index );
+
+    res = 0;
+    hr = IDirectInputDevice8_EnumEffects( device, check_effect_count, &res, 0xfe );
+    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
+    ok( res == 0, "got %u expected %u\n", res, 0 );
+    res = 0;
+    hr = IDirectInputDevice8_EnumEffects( device, check_effect_count, &res, DIEFT_PERIODIC );
+    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
+    ok( res == 2, "got %u expected %u\n", res, 2 );
+    hr = IDirectInputDevice8_EnumEffects( device, check_effects, &check_effects_params, DIEFT_ALL );
+    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
+    ok( check_effects_params.index >= check_effects_params.expect_count, "missing %u effects\n",
+        check_effects_params.expect_count - check_effects_params.index );
+
+    effectinfo.dwSize = sizeof(DIEFFECTINFOW);
+    hr = IDirectInputDevice8_GetEffectInfo( device, &effectinfo, &GUID_Sine );
+    ok( hr == DI_OK, "GetEffectInfo returned %#x\n", hr );
+    check_member_guid( effectinfo, expect_effects[1], guid );
+    check_member( effectinfo, expect_effects[1], "%#x", dwEffType );
+    check_member( effectinfo, expect_effects[1], "%#x", dwStaticParams );
+    check_member( effectinfo, expect_effects[1], "%#x", dwDynamicParams );
+    check_member_wstr( effectinfo, expect_effects[1], tszName );
+
+    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
+
+    file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
+    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
+
+    hwnd = CreateWindowW( L"static", L"dinput", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200,
+                          NULL, NULL, NULL, NULL );
+
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE );
+    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
+
+    prop_dword.diph.dwHow = DIPH_BYUSAGE;
+    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_dword.dwData = DIPROPAUTOCENTER_ON;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
+    prop_dword.diph.dwHow = DIPH_DEVICE;
+    prop_dword.diph.dwObj = 0;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
+    prop_dword.dwData = 1000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DIERR_READONLY, "SetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetForceFeedbackState returned %#x\n", hr );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_RESET );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "SendForceFeedbackCommand returned %#x\n", hr );
+
+    escape.dwSize = sizeof(DIEFFESCAPE);
+    escape.dwCommand = 0;
+    escape.lpvInBuffer = buffer;
+    escape.cbInBuffer = 10;
+    escape.lpvOutBuffer = buffer + 10;
+    escape.cbOutBuffer = 10;
+    hr = IDirectInputDevice8_Escape( device, &escape );
+    todo_wine
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Escape returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE );
+    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
+
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+
+    set_hid_expect( file, &expect_set_device_gain_2, sizeof(expect_set_device_gain_2) );
+    prop_dword.dwData = 2000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+
+    set_hid_expect( file, &expect_set_device_gain_1, sizeof(expect_set_device_gain_1) );
+    prop_dword.dwData = 1000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+
+    hr = IDirectInputDevice8_Escape( device, &escape );
+    todo_wine
+    ok( hr == DIERR_UNSUPPORTED, "Escape returned: %#x\n", hr );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    todo_wine
+    ok( hr == 0x80040301, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    todo_wine
+    ok( hr == 0x80040301, "GetForceFeedbackState returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, 0xdeadbeef );
+    ok( hr == DIERR_INVALIDPARAM, "SendForceFeedbackCommand returned %#x\n", hr );
+
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_RESET );
+    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
+    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
+
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_STOPALL );
+    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_PAUSE );
+    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_CONTINUE );
+    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_SETACTUATORSON );
+    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_SETACTUATORSOFF );
+    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
+
+    objdata.dwOfs = 0x1e;
+    objdata.dwData = 0x80;
+    res = 1;
+    hr = IDirectInputDevice8_SendDeviceData( device, sizeof(DIDEVICEOBJECTDATA), &objdata, &res, 0 );
+    if (version < 0x800) ok( hr == DI_OK, "SendDeviceData returned %#x\n", hr );
+    else todo_wine ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
+
+    test_periodic_effect( device, file, version );
+    test_condition_effect( device, file, version );
+
+    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    ref = IDirectInputDevice8_Release( device );
+    ok( ref == 0, "Release returned %d\n", ref );
+
+    DestroyWindow( hwnd );
+    CloseHandle( file );
+
+done:
+    pnp_driver_stop();
+    cleanup_registry_keys();
+    SetCurrentDirectoryW( cwd );
+    winetest_pop_context();
+
+    return device != NULL;
+}
+
+static void test_device_managed_effect(void)
+{
+#include "psh_hid_macros.h"
+    const unsigned char report_descriptor[] = {
+        USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
+        USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
+        COLLECTION(1, Application),
+            USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 1),
+
+                USAGE(1, HID_USAGE_GENERIC_X),
+                USAGE(1, HID_USAGE_GENERIC_Y),
+                USAGE(1, HID_USAGE_GENERIC_Z),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 3),
+                INPUT(1, Data|Var|Abs),
+
+                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
+                USAGE_MINIMUM(1, 1),
+                USAGE_MAXIMUM(1, 2),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 2),
+                INPUT(1, Data|Var|Abs),
+                REPORT_COUNT(1, 6),
+                INPUT(1, Cnst|Var|Abs),
+            END_COLLECTION,
+
+            USAGE_PAGE(1, HID_USAGE_PAGE_PID),
+            USAGE(1, PID_USAGE_STATE_REPORT),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 2),
+
+                USAGE(1, PID_USAGE_DEVICE_PAUSED),
+                USAGE(1, PID_USAGE_ACTUATORS_ENABLED),
+                USAGE(1, PID_USAGE_SAFETY_SWITCH),
+                USAGE(1, PID_USAGE_ACTUATOR_OVERRIDE_SWITCH),
+                USAGE(1, PID_USAGE_ACTUATOR_POWER),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 5),
+                INPUT(1, Data|Var|Abs),
+                REPORT_COUNT(1, 3),
+                INPUT(1, Cnst|Var|Abs),
+
+                USAGE(1, PID_USAGE_EFFECT_PLAYING),
+                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),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                INPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE_PAGE(1, HID_USAGE_PAGE_PID),
+            USAGE(1, PID_USAGE_DEVICE_CONTROL_REPORT),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 1),
+
+                USAGE(1, PID_USAGE_DEVICE_CONTROL),
+                COLLECTION(1, Logical),
+                    USAGE(1, PID_USAGE_DC_DEVICE_RESET),
+                    USAGE(1, PID_USAGE_DC_DEVICE_PAUSE),
+                    USAGE(1, PID_USAGE_DC_DEVICE_CONTINUE),
+                    USAGE(1, PID_USAGE_DC_ENABLE_ACTUATORS),
+                    USAGE(1, PID_USAGE_DC_DISABLE_ACTUATORS),
+                    USAGE(1, PID_USAGE_DC_STOP_ALL_EFFECTS),
+                    LOGICAL_MINIMUM(1, 1),
+                    LOGICAL_MAXIMUM(1, 6),
+                    PHYSICAL_MINIMUM(1, 1),
+                    PHYSICAL_MAXIMUM(1, 6),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 1),
+                    OUTPUT(1, Data|Ary|Abs),
+                END_COLLECTION,
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_EFFECT_OPERATION_REPORT),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 2),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_EFFECT_OPERATION),
+                COLLECTION(1, NamedArray),
+                    USAGE(1, PID_USAGE_OP_EFFECT_START),
+                    USAGE(1, PID_USAGE_OP_EFFECT_START_SOLO),
+                    USAGE(1, PID_USAGE_OP_EFFECT_STOP),
+                    LOGICAL_MINIMUM(1, 1),
+                    LOGICAL_MAXIMUM(1, 3),
+                    PHYSICAL_MINIMUM(1, 1),
+                    PHYSICAL_MAXIMUM(1, 3),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 1),
+                    OUTPUT(1, Data|Ary|Abs),
+                END_COLLECTION,
+
+                USAGE(1, PID_USAGE_LOOP_COUNT),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_SET_EFFECT_REPORT),
+            COLLECTION(1, Report),
+                REPORT_ID(1, 3),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_EFFECT_TYPE),
+                COLLECTION(1, NamedArray),
+                    USAGE(1, PID_USAGE_ET_SQUARE),
+                    USAGE(1, PID_USAGE_ET_SINE),
+                    USAGE(1, PID_USAGE_ET_SPRING),
+                    LOGICAL_MINIMUM(1, 1),
+                    LOGICAL_MAXIMUM(1, 3),
+                    PHYSICAL_MINIMUM(1, 1),
+                    PHYSICAL_MAXIMUM(1, 3),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 1),
+                    OUTPUT(1, Data|Ary|Abs),
+                END_COLLECTION,
+
+                USAGE(1, PID_USAGE_AXES_ENABLE),
+                COLLECTION(1, Logical),
+                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_X),
+                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Y),
+                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Z),
+                    LOGICAL_MINIMUM(1, 0),
+                    LOGICAL_MAXIMUM(1, 1),
+                    PHYSICAL_MINIMUM(1, 0),
+                    PHYSICAL_MAXIMUM(1, 1),
+                    REPORT_SIZE(1, 1),
+                    REPORT_COUNT(1, 3),
+                    OUTPUT(1, Data|Var|Abs),
+                END_COLLECTION,
+                USAGE(1, PID_USAGE_DIRECTION_ENABLE),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+                REPORT_COUNT(1, 4),
+                OUTPUT(1, Cnst|Var|Abs),
+
+                USAGE(1, PID_USAGE_DURATION),
+                USAGE(1, PID_USAGE_START_DELAY),
+                UNIT(2, 0x1003),      /* Eng Lin:Time */
+                UNIT_EXPONENT(1, -3), /* 10^-3 */
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x7fff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x7fff),
+                REPORT_SIZE(1, 16),
+                REPORT_COUNT(1, 2),
+                OUTPUT(1, Data|Var|Abs),
+                UNIT(1, 0),
+                UNIT_EXPONENT(1, 0),
+
+                USAGE(1, PID_USAGE_TRIGGER_BUTTON),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 0x08),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(1, 0x08),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_DIRECTION),
+                COLLECTION(1, Logical),
+                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1),
+                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2),
+                    UNIT(1, 0x14),        /* Eng Rot:Angular Pos */
+                    UNIT_EXPONENT(1, -2), /* 10^-2 */
+                    LOGICAL_MINIMUM(1, 0),
+                    LOGICAL_MAXIMUM(2, 0x00ff),
+                    PHYSICAL_MINIMUM(1, 0),
+                    PHYSICAL_MAXIMUM(4, 0x00008ca0),
+                    UNIT(1, 0),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 2),
+                    OUTPUT(1, Data|Var|Abs),
+                    UNIT_EXPONENT(1, 0),
+                    UNIT(1, 0),
+                END_COLLECTION,
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_SET_CONDITION_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 4),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_PARAMETER_BLOCK_OFFSET),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 4),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET),
+                COLLECTION(1, Logical),
+                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1),
+                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2),
+                    LOGICAL_MINIMUM(1, 0),
+                    LOGICAL_MAXIMUM(1, 1),
+                    PHYSICAL_MINIMUM(1, 0),
+                    PHYSICAL_MAXIMUM(1, 1),
+                    REPORT_SIZE(1, 2),
+                    REPORT_COUNT(1, 2),
+                    OUTPUT(1, Data|Var|Abs),
+                END_COLLECTION,
+
+                USAGE(1, PID_USAGE_CP_OFFSET),
+                LOGICAL_MINIMUM(1, 0x80),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(2, 0xd8f0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_POSITIVE_COEFFICIENT),
+                USAGE(1, PID_USAGE_NEGATIVE_COEFFICIENT),
+                LOGICAL_MINIMUM(1, 0x80),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(2, 0xd8f0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 2),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_POSITIVE_SATURATION),
+                USAGE(1, PID_USAGE_NEGATIVE_SATURATION),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x00ff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 2),
+                OUTPUT(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_DEAD_BAND),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x00ff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_BLOCK_FREE_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 5),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_DEVICE_GAIN_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 6),
+
+                USAGE(1, PID_USAGE_DEVICE_GAIN),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(2, 0x00ff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(2, 0x2710),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                OUTPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_POOL_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 1),
+
+                USAGE(1, PID_USAGE_RAM_POOL_SIZE),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(4, 0xffff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(4, 0xffff),
+                REPORT_SIZE(1, 16),
+                REPORT_COUNT(1, 1),
+                FEATURE(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_SIMULTANEOUS_EFFECTS_MAX),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                FEATURE(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_DEVICE_MANAGED_POOL),
+                USAGE(1, PID_USAGE_SHARED_PARAMETER_BLOCKS),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 8),
+                FEATURE(1, Data|Var|Abs),
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_CREATE_NEW_EFFECT_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 2),
+
+                USAGE(1, PID_USAGE_EFFECT_TYPE),
+                COLLECTION(1, NamedArray),
+                    USAGE(1, PID_USAGE_ET_SQUARE),
+                    USAGE(1, PID_USAGE_ET_SINE),
+                    USAGE(1, PID_USAGE_ET_SPRING),
+                    LOGICAL_MINIMUM(1, 1),
+                    LOGICAL_MAXIMUM(1, 3),
+                    PHYSICAL_MINIMUM(1, 1),
+                    PHYSICAL_MAXIMUM(1, 3),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 1),
+                    FEATURE(1, Data|Ary|Abs),
+                END_COLLECTION,
+            END_COLLECTION,
+
+            USAGE(1, PID_USAGE_BLOCK_LOAD_REPORT),
+            COLLECTION(1, Logical),
+                REPORT_ID(1, 3),
+
+                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 0x7f),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(1, 0x7f),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                FEATURE(1, Data|Var|Abs),
+
+                USAGE(1, PID_USAGE_BLOCK_LOAD_STATUS),
+                COLLECTION(1, NamedArray),
+                    USAGE(1, PID_USAGE_BLOCK_LOAD_SUCCESS),
+                    USAGE(1, PID_USAGE_BLOCK_LOAD_FULL),
+                    USAGE(1, PID_USAGE_BLOCK_LOAD_ERROR),
+                    LOGICAL_MINIMUM(1, 1),
+                    LOGICAL_MAXIMUM(1, 3),
+                    PHYSICAL_MINIMUM(1, 1),
+                    PHYSICAL_MAXIMUM(1, 3),
+                    REPORT_SIZE(1, 8),
+                    REPORT_COUNT(1, 1),
+                    FEATURE(1, Data|Ary|Abs),
+                END_COLLECTION,
+
+                USAGE(1, PID_USAGE_RAM_POOL_AVAILABLE),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(4, 0xffff),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(4, 0xffff),
+                REPORT_SIZE(1, 16),
+                REPORT_COUNT(1, 1),
+                FEATURE(1, Data|Var|Abs),
+            END_COLLECTION,
+        END_COLLECTION,
+    };
+#include "pop_hid_macros.h"
+
+    static const HIDP_CAPS hid_caps =
+    {
+        .InputReportByteLength = 5,
+    };
+    struct hid_expect expect_acquire[] =
+    {
+        /* device control */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x01},
+        },
+        /* device gain */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 6,
+            .report_len = 2,
+            .report_buf = {6, 0xff},
+        },
+    };
+    struct hid_expect expect_reset[] =
+    {
+        /* device control */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x01},
+        },
+    };
+    struct hid_expect expect_enable_actuators[] =
+    {
+        /* device control */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x04},
+        },
+    };
+    struct hid_expect expect_disable_actuators[] =
+    {
+        /* device control */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x05},
+        },
+    };
+    struct hid_expect expect_stop_all[] =
+    {
+        /* device control */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x06},
+        },
+    };
+    struct hid_expect expect_device_pause[] =
+    {
+        /* device control */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x02},
+        },
+    };
+    struct hid_expect expect_device_continue[] =
+    {
+        /* device control */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 1,
+            .report_len = 2,
+            .report_buf = {1, 0x03},
+        },
+    };
+    struct hid_expect expect_create[] =
+    {
+        /* create new effect */
+        {
+            .code = IOCTL_HID_SET_FEATURE,
+            .report_id = 2,
+            .report_len = 2,
+            .report_buf = {2,0x03},
+        },
+        /* block load */
+        {
+            .code = IOCTL_HID_GET_FEATURE,
+            .report_id = 3,
+            .report_len = 5,
+            .report_buf = {3,0x01,0x01,0x00,0x00},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 4,
+            .report_len = 9,
+            .report_buf = {4,0x01,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 4,
+            .report_len = 9,
+            .report_buf = {4,0x01,0x01,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {3,0x01,0x03,0x08,0x01,0x00,0x06,0x00,0x01,0x55,0x00},
+        },
+    };
+    struct hid_expect expect_create_2[] =
+    {
+        /* create new effect */
+        {
+            .code = IOCTL_HID_SET_FEATURE,
+            .report_id = 2,
+            .report_len = 2,
+            .report_buf = {2,0x03},
+        },
+        /* block load */
+        {
+            .code = IOCTL_HID_GET_FEATURE,
+            .report_id = 3,
+            .report_len = 5,
+            .report_buf = {3,0x02,0x01,0x00,0x00},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 4,
+            .report_len = 9,
+            .report_buf = {4,0x02,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 4,
+            .report_len = 9,
+            .report_buf = {4,0x02,0x01,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {3,0x02,0x03,0x08,0x01,0x00,0x06,0x00,0x01,0x55,0x00},
+        },
+    };
+    struct hid_expect expect_create_delay[] =
+    {
+        /* create new effect */
+        {
+            .code = IOCTL_HID_SET_FEATURE,
+            .report_id = 2,
+            .report_len = 2,
+            .report_buf = {2,0x03},
+        },
+        /* block load */
+        {
+            .code = IOCTL_HID_GET_FEATURE,
+            .report_id = 3,
+            .report_len = 5,
+            .report_buf = {3,0x01,0x01,0x00,0x00},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 4,
+            .report_len = 9,
+            .report_buf = {4,0x01,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 4,
+            .report_len = 9,
+            .report_buf = {4,0x01,0x01,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {3,0x01,0x03,0x08,0x01,0x00,0xff,0x7f,0x01,0x55,0x00},
+        },
+    };
+    struct hid_expect expect_create_duration[] =
+    {
+        /* create new effect */
+        {
+            .code = IOCTL_HID_SET_FEATURE,
+            .report_id = 2,
+            .report_len = 2,
+            .report_buf = {2,0x03},
+        },
+        /* block load */
+        {
+            .code = IOCTL_HID_GET_FEATURE,
+            .report_id = 3,
+            .report_len = 5,
+            .report_buf = {3,0x01,0x01,0x00,0x00},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 4,
+            .report_len = 9,
+            .report_buf = {4,0x01,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
+        },
+        /* set condition */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 4,
+            .report_len = 9,
+            .report_buf = {4,0x01,0x01,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
+        },
+        /* update effect */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 3,
+            .report_len = 11,
+            .report_buf = {3,0x01,0x03,0x08,0x00,0x00,0x00,0x00,0x01,0x55,0x00},
+        },
+    };
+    struct hid_expect expect_start =
+    {
+        /* effect control */
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {2, 0x01, 0x01, 0x01},
+    };
+    struct hid_expect expect_start_2 =
+    {
+        /* effect control */
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {2, 0x02, 0x02, 0x01},
+    };
+    struct hid_expect expect_stop =
+    {
+        /* effect control */
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {2, 0x01, 0x03, 0x00},
+    };
+    struct hid_expect expect_stop_2 =
+    {
+        /* effect control */
+        .code = IOCTL_HID_WRITE_REPORT,
+        .report_id = 2,
+        .report_len = 4,
+        .report_buf = {2, 0x02, 0x03, 0x00},
+    };
+    struct hid_expect expect_destroy[] =
+    {
+        /* effect operation */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 2,
+            .report_len = 4,
+            .report_buf = {2,0x01,0x03,0x00},
+        },
+        /* block free */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 5,
+            .report_len = 2,
+            .report_buf = {5,0x01},
+        },
+    };
+    struct hid_expect expect_destroy_2[] =
+    {
+        /* effect operation */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 2,
+            .report_len = 4,
+            .report_buf = {2,0x02,0x03,0x00},
+        },
+        /* block free */
+        {
+            .code = IOCTL_HID_WRITE_REPORT,
+            .report_id = 5,
+            .report_len = 2,
+            .report_buf = {5,0x02},
+        },
+    };
+    struct hid_expect device_state_input[] =
+    {
+        /* effect state */
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_id = 2,
+            .report_len = 4,
+            .report_buf = {2,0xff,0x00,0xff},
+        },
+        /* device state */
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_id = 1,
+            .report_len = 5,
+            .report_buf = {1,0x12,0x34,0x56,0xff},
+        },
+    };
+    struct hid_expect device_state_input_0[] =
+    {
+        /* effect state */
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_id = 2,
+            .report_len = 4,
+            .report_buf = {2,0xff,0x00,0xff},
+        },
+        /* device state */
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_id = 1,
+            .report_len = 5,
+            .report_buf = {1,0x56,0x12,0x34,0xff},
+        },
+    };
+    struct hid_expect device_state_input_1[] =
+    {
+        /* effect state */
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_id = 2,
+            .report_len = 4,
+            .report_buf = {2,0x00,0x01,0x01},
+        },
+        /* device state */
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_id = 1,
+            .report_len = 5,
+            .report_buf = {1,0x65,0x43,0x21,0x00},
+        },
+    };
+    struct hid_expect device_state_input_2[] =
+    {
+        /* effect state */
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_id = 2,
+            .report_len = 4,
+            .report_buf = {2,0x03,0x00,0x01},
+        },
+        /* device state */
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_id = 1,
+            .report_len = 5,
+            .report_buf = {1,0x12,0x34,0x56,0xff},
+        },
+    };
+    struct hid_expect expect_pool[] =
+    {
+        /* device pool */
+        {
+            .code = IOCTL_HID_GET_FEATURE,
+            .report_id = 1,
+            .report_len = 5,
+            .report_buf = {1,0x10,0x00,0x04,0x03},
+            .todo = TRUE,
+        },
+        /* device pool */
+        {
+            .code = IOCTL_HID_GET_FEATURE,
+            .report_id = 1,
+            .report_len = 5,
+            .report_buf = {1,0x10,0x00,0x04,0x03},
+            .todo = TRUE,
+        },
+    };
+    static const DWORD expect_axes[3] =
+    {
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR,
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR,
+        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR,
+    };
+    static const LONG expect_directions[3] = {
+        +3000,
+        0,
+        0,
+    };
+    static const DIENVELOPE expect_envelope =
+    {
+        .dwSize = sizeof(DIENVELOPE),
+        .dwAttackLevel = 1000,
+        .dwAttackTime = 2000,
+        .dwFadeLevel = 3000,
+        .dwFadeTime = 4000,
+    };
+    static const DICONDITION expect_condition[3] =
+    {
+        {
+            .lOffset = -500,
+            .lPositiveCoefficient = 2000,
+            .lNegativeCoefficient = -3000,
+            .dwPositiveSaturation = -4000,
+            .dwNegativeSaturation = -5000,
+            .lDeadBand = 6000,
+        },
+        {
+            .lOffset = 6000,
+            .lPositiveCoefficient = 5000,
+            .lNegativeCoefficient = -4000,
+            .dwPositiveSaturation = 3000,
+            .dwNegativeSaturation = 2000,
+            .lDeadBand = 1000,
+        },
+        {
+            .lOffset = -7000,
+            .lPositiveCoefficient = -8000,
+            .lNegativeCoefficient = 9000,
+            .dwPositiveSaturation = 10000,
+            .dwNegativeSaturation = 11000,
+            .lDeadBand = -12000,
+        },
+    };
+    const DIEFFECT expect_desc =
+    {
+        .dwSize = sizeof(DIEFFECT_DX6),
+        .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS,
+        .dwDuration = 1000,
+        .dwSamplePeriod = 2000,
+        .dwGain = 3000,
+        .dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
+        .dwTriggerRepeatInterval = 5000,
+        .cAxes = 2,
+        .rgdwAxes = (void *)expect_axes,
+        .rglDirection = (void *)expect_directions,
+        .lpEnvelope = (void *)&expect_envelope,
+        .cbTypeSpecificParams = 2 * sizeof(DICONDITION),
+        .lpvTypeSpecificParams = (void *)expect_condition,
+        .dwStartDelay = 6000,
+    };
+    DIPROPGUIDANDPATH prop_guid_path =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPGUIDANDPATH),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    DIPROPDWORD prop_dword =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPDWORD),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
+    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
+    IDirectInputDevice8W *device;
+    IDirectInputEffect *effect, *effect2;
+    HANDLE file, event;
+    ULONG res, ref;
+    DIEFFECT desc;
+    DWORD flags;
+    HRESULT hr;
+    HWND hwnd;
+
+    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
+    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
+    SetCurrentDirectoryW( tempdir );
+
+    cleanup_registry_keys();
+    if (!dinput_driver_start( report_descriptor, sizeof(report_descriptor), &hid_caps,
+                              expect_pool, sizeof(expect_pool) )) goto done;
+    if (FAILED(hr = dinput_test_create_device( DIRECTINPUT_VERSION, &devinst, &device ))) goto done;
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
+    file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
+    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
+
+    hwnd = CreateWindowW( L"static", L"dinput", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200,
+                          NULL, NULL, NULL, NULL );
+
+    event = CreateEventW( NULL, FALSE, FALSE, NULL );
+    ok( event != NULL, "CreateEventW failed, last error %u\n", GetLastError() );
+    hr = IDirectInputDevice8_SetEventNotification( device, event );
+    ok( hr == DI_OK, "SetEventNotification returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE );
+    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetForceFeedbackState returned %#x\n", hr );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_RESET );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "SendForceFeedbackCommand returned %#x\n", hr );
+
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    wait_hid_expect( file, 100 );
+
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    ok( prop_dword.dwData == 0, "got DIPROP_FFLOAD %#x\n", prop_dword.dwData );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED | DIGFFS_EMPTY;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    ok( prop_dword.dwData == 0, "got DIPROP_FFLOAD %#x\n", prop_dword.dwData );
+    set_hid_expect( file, NULL, 0 );
+
+    send_hid_input( file, device_state_input, sizeof(struct hid_expect) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_TIMEOUT, "WaitForSingleObject returned %#x\n", res );
+    send_hid_input( file, device_state_input, sizeof(device_state_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
+
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_PAUSED | DIGFFS_EMPTY | DIGFFS_ACTUATORSON | DIGFFS_POWERON |
+            DIGFFS_SAFETYSWITCHON | DIGFFS_USERFFSWITCHON;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, NULL, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+
+    hr = IDirectInputEffect_GetEffectStatus( effect, NULL );
+    ok( hr == E_POINTER, "GetEffectStatus returned %#x\n", hr );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DIERR_NOTDOWNLOADED, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+
+    flags = DIEP_ALLPARAMS;
+    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, flags | DIEP_NODOWNLOAD );
+    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
+
+    set_hid_expect( file, expect_reset, sizeof(struct hid_expect) );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetEffectStatus returned %#x\n", hr );
+
+    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    wait_hid_expect( file, 100 );
+
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DIERR_NOTDOWNLOADED, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED | DIGFFS_EMPTY;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_create, sizeof(expect_create) );
+    hr = IDirectInputEffect_Download( effect );
+    ok( hr == DI_OK, "Download returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    ok( prop_dword.dwData == 0, "got DIPROP_FFLOAD %#x\n", prop_dword.dwData );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_start, sizeof(expect_start) );
+    hr = IDirectInputEffect_Start( effect, 1, DIES_NODOWNLOAD );
+    ok( hr == DI_OK, "Start returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_create_2, sizeof(expect_create_2) );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &expect_desc, &effect2, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    set_hid_expect( file, &expect_start_2, sizeof(expect_start_2) );
+    hr = IDirectInputEffect_Start( effect2, 1, DIES_SOLO );
+    ok( hr == DI_OK, "Start returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect2, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, &expect_stop_2, sizeof(expect_stop_2) );
+    hr = IDirectInputEffect_Stop( effect2 );
+    ok( hr == DI_OK, "Stop returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect2, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+    set_hid_expect( file, expect_destroy_2, sizeof(expect_destroy_2) );
+    ref = IDirectInputEffect_Release( effect2 );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, NULL, 0 );
+
+    /* sending commands has no direct effect on status */
+    set_hid_expect( file, expect_stop_all, sizeof(expect_stop_all) );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_STOPALL );
+    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_device_pause, sizeof(expect_device_pause) );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_PAUSE );
+    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_device_continue, sizeof(expect_device_continue) );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_CONTINUE );
+    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_disable_actuators, sizeof(expect_disable_actuators) );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_SETACTUATORSOFF );
+    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_enable_actuators, sizeof(expect_enable_actuators) );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_SETACTUATORSON );
+    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_stop, sizeof(expect_stop) );
+    hr = IDirectInputEffect_Stop( effect );
+    ok( hr == DI_OK, "Stop returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_STOPPED;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    send_hid_input( file, device_state_input_0, sizeof(device_state_input_0) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_PAUSED | DIGFFS_ACTUATORSON | DIGFFS_POWERON | DIGFFS_SAFETYSWITCHON | DIGFFS_USERFFSWITCHON;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    send_hid_input( file, device_state_input_1, sizeof(device_state_input_1) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_ACTUATORSOFF | DIGFFS_POWEROFF | DIGFFS_SAFETYSWITCHOFF | DIGFFS_USERFFSWITCHOFF;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    send_hid_input( file, device_state_input_2, sizeof(device_state_input_2) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_PAUSED | DIGFFS_ACTUATORSON | DIGFFS_POWEROFF | DIGFFS_SAFETYSWITCHOFF | DIGFFS_USERFFSWITCHOFF;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, &expect_stop, sizeof(expect_stop) );
+    hr = IDirectInputEffect_Stop( effect );
+    ok( hr == DI_OK, "Stop returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_PAUSED | DIGFFS_ACTUATORSON | DIGFFS_POWEROFF | DIGFFS_SAFETYSWITCHOFF | DIGFFS_USERFFSWITCHOFF;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_destroy, sizeof(expect_destroy) );
+    hr = IDirectInputEffect_Unload( effect );
+    ok( hr == DI_OK, "Unload returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DIERR_NOTDOWNLOADED, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
+    flags = DIGFFS_EMPTY | DIGFFS_PAUSED | DIGFFS_ACTUATORSON | DIGFFS_POWEROFF |
+            DIGFFS_SAFETYSWITCHOFF | DIGFFS_USERFFSWITCHOFF;
+    ok( res == flags, "got state %#x\n", res );
+    set_hid_expect( file, NULL, 0 );
+
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+
+    /* start delay has no direct effect on effect status */
+    desc = expect_desc;
+    desc.dwStartDelay = 32767000;
+    set_hid_expect( file, expect_create_delay, sizeof(expect_create_delay) );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+    set_hid_expect( file, &expect_start, sizeof(expect_start) );
+    hr = IDirectInputEffect_Start( effect, 1, 0 );
+    ok( hr == DI_OK, "Start returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, expect_destroy, sizeof(expect_destroy) );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, NULL, 0 );
+
+    /* duration has no direct effect on effect status */
+    desc = expect_desc;
+    desc.dwDuration = 100;
+    desc.dwStartDelay = 0;
+    set_hid_expect( file, expect_create_duration, sizeof(expect_create_duration) );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
+    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == 0, "got status %#x\n", res );
+    set_hid_expect( file, &expect_start, sizeof(expect_start) );
+    hr = IDirectInputEffect_Start( effect, 1, 0 );
+    ok( hr == DI_OK, "Start returned %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+    Sleep( 100 );
+    res = 0xdeadbeef;
+    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
+    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
+    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
+    set_hid_expect( file, expect_destroy, sizeof(expect_destroy) );
+    ref = IDirectInputEffect_Release( effect );
+    ok( ref == 0, "Release returned %d\n", ref );
+    set_hid_expect( file, NULL, 0 );
+
+    set_hid_expect( file, expect_reset, sizeof(struct hid_expect) );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    set_hid_expect( file, NULL, 0 );
+
+    ref = IDirectInputDevice8_Release( device );
+    ok( ref == 0, "Release returned %d\n", ref );
+
+    DestroyWindow( hwnd );
+    CloseHandle( event );
+    CloseHandle( file );
+
+done:
+    pnp_driver_stop();
+    cleanup_registry_keys();
+    SetCurrentDirectoryW( cwd );
+    winetest_pop_context();
+}
+
+START_TEST( force_feedback )
+{
+    if (!dinput_test_init()) return;
+
+    CoInitialize( NULL );
+    if (test_force_feedback_joystick( 0x800 ))
+    {
+        test_force_feedback_joystick( 0x500 );
+        test_force_feedback_joystick( 0x700 );
+        test_device_managed_effect();
+    }
+    CoUninitialize();
+
+    dinput_test_exit();
+}
diff --git a/dlls/dinput/tests/hid.c b/dlls/dinput/tests/hid.c
index 82657c2e235..f8e8a275b09 100644
--- a/dlls/dinput/tests/hid.c
+++ b/dlls/dinput/tests/hid.c
@@ -43,10 +43,7 @@
 #include "objbase.h"
 
 #define COBJMACROS
-#include "wingdi.h"
 #include "dinput.h"
-#include "dinputd.h"
-#include "mmsystem.h"
 
 #include "initguid.h"
 #include "ddk/wdm.h"
@@ -57,22 +54,20 @@
 #include "hidusage.h"
 #include "devguid.h"
 
-#include "wine/test.h"
 #include "wine/mssign.h"
-#include "wine/hid.h"
 
-#include "driver_hid.h"
+#include "dinput_test.h"
 
-static HINSTANCE instance;
-static BOOL localized; /* object names get translated */
+HINSTANCE instance;
+BOOL localized; /* object names get translated */
 
-#define EXPECT_VIDPID MAKELONG( 0x1209, 0x0001 )
-static const WCHAR expect_vidpid_str[] = L"VID_1209&PID_0001";
-static const GUID expect_guid_product = {EXPECT_VIDPID,0x0000,0x0000,{0x00,0x00,'P','I','D','V','I','D'}};
-static const WCHAR expect_path[] = L"\\\\?\\hid#winetest#1&2fafeb0&";
-static const WCHAR expect_path_end[] = L"&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
+const WCHAR expect_vidpid_str[] = L"VID_1209&PID_0001";
+const GUID expect_guid_product = {EXPECT_VIDPID, 0x0000, 0x0000, {0x00, 0x00, 'P', 'I', 'D', 'V', 'I', 'D'}};
+const WCHAR expect_path[] = L"\\\\?\\hid#winetest#1&2fafeb0&";
+const WCHAR expect_path_end[] = L"&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
 
 static struct winetest_shared_data *test_data;
+static HANDLE test_data_mapping;
 static HANDLE okfile;
 
 static HRESULT (WINAPI *pSignerSign)( SIGNER_SUBJECT_INFO *subject, SIGNER_CERT *cert,
@@ -420,7 +415,7 @@ static void unload_driver( SC_HANDLE service )
     CloseServiceHandle( service );
 }
 
-static void pnp_driver_stop(void)
+void pnp_driver_stop(void)
 {
     SP_DEVINFO_DATA device = {sizeof(SP_DEVINFO_DATA)};
     WCHAR path[MAX_PATH], dest[MAX_PATH], *filepart;
@@ -493,7 +488,7 @@ static void pnp_driver_stop(void)
     ok( ret || GetLastError() == ERROR_FILE_NOT_FOUND, "Failed to delete file, error %u\n", GetLastError() );
 }
 
-static BOOL pnp_driver_start( const WCHAR *resource )
+BOOL pnp_driver_start( const WCHAR *resource )
 {
     static const WCHAR hardware_id[] = L"test_hardware_id\0";
     SP_DEVINFO_DATA device = {sizeof(SP_DEVINFO_DATA)};
@@ -595,21 +590,6 @@ static BOOL pnp_driver_start( const WCHAR *resource )
     return ret || GetLastError() == ERROR_SERVICE_ALREADY_RUNNING;
 }
 
-#define check_member_( file, line, val, exp, fmt, member ) \
-    ok_(file, line)( (val).member == (exp).member, "got " #member " " fmt "\n", (val).member )
-#define check_member( val, exp, fmt, member ) \
-    check_member_( __FILE__, __LINE__, val, exp, fmt, member )
-
-#define check_member_guid_( file, line, val, exp, member ) \
-    ok_(file, line)( IsEqualGUID( &(val).member, &(exp).member ), "got " #member " %s\n", debugstr_guid(&(val).member) )
-#define check_member_guid( val, exp, member ) \
-    check_member_guid_( __FILE__, __LINE__, val, exp, member )
-
-#define check_member_wstr_( file, line, val, exp, member ) \
-    ok_(file, line)( !wcscmp( (val).member, (exp).member ), "got " #member " %s\n", debugstr_w((val).member) )
-#define check_member_wstr( val, exp, member ) \
-    check_member_wstr_( __FILE__, __LINE__, val, exp, member )
-
 #define check_hidp_caps( a, b ) check_hidp_caps_( __LINE__, a, b )
 static inline void check_hidp_caps_( int line, HIDP_CAPS *caps, const HIDP_CAPS *exp )
 {
@@ -744,21 +724,21 @@ static inline void check_hidp_value_caps_( int line, HIDP_VALUE_CAPS *caps, cons
     }
 }
 
-#define sync_ioctl( a, b, c, d, e, f, g ) sync_ioctl_( __LINE__, a, b, c, d, e, f, g )
-static BOOL sync_ioctl_( int line, HANDLE file, DWORD code, void *in_buf, DWORD in_len, void *out_buf, DWORD *ret_len, DWORD timeout )
+BOOL sync_ioctl_( const char *file, int line, HANDLE device, DWORD code, void *in_buf, DWORD in_len,
+                  void *out_buf, DWORD *ret_len, DWORD timeout )
 {
     DWORD res, out_len = ret_len ? *ret_len : 0;
     OVERLAPPED ovl = {0};
     BOOL ret;
 
     ovl.hEvent = CreateEventW( NULL, TRUE, FALSE, NULL );
-    ret = DeviceIoControl( file, code, in_buf, in_len, out_buf, out_len, &out_len, &ovl );
+    ret = DeviceIoControl( device, code, in_buf, in_len, out_buf, out_len, &out_len, &ovl );
     if (!ret && GetLastError() == ERROR_IO_PENDING)
     {
         res = WaitForSingleObject( ovl.hEvent, timeout );
-        ok_(__FILE__, line)( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
-        ret = GetOverlappedResult( file, &ovl, &out_len, FALSE );
-        ok_(__FILE__, line)( ret, "GetOverlappedResult returned %u\n", GetLastError() );
+        ok_(file, line)( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
+        ret = GetOverlappedResult( device, &ovl, &out_len, FALSE );
+        ok_(file, line)( ret, "GetOverlappedResult returned %u\n", GetLastError() );
     }
     CloseHandle( ovl.hEvent );
 
@@ -776,39 +756,36 @@ static BOOL sync_ioctl_( int line, HANDLE file, DWORD code, void *in_buf, DWORD
         snprintf( a, b, "%s:%d", source_file, line ); \
     } while (0)
 
-#define set_hid_expect( a, b, c ) set_hid_expect_( __LINE__, a, b, c )
-static void set_hid_expect_( int line, HANDLE file, struct hid_expect *expect, DWORD expect_size )
+void set_hid_expect_( const char *file, int line, HANDLE device, struct hid_expect *expect, DWORD expect_size )
 {
     char context[64];
     BOOL ret;
 
     fill_context( line, context, ARRAY_SIZE(context) );
-    ret = sync_ioctl_( line, file, IOCTL_WINETEST_HID_SET_CONTEXT, context, ARRAY_SIZE(context), NULL, 0, INFINITE );
-    ok_(__FILE__, line)( ret, "IOCTL_WINETEST_HID_SET_CONTEXT failed, last error %u\n", GetLastError() );
-    ret = sync_ioctl_( line, file, IOCTL_WINETEST_HID_SET_EXPECT, expect, expect_size, NULL, 0, INFINITE );
-    ok_(__FILE__, line)( ret, "IOCTL_WINETEST_HID_SET_EXPECT failed, last error %u\n", GetLastError() );
+    ret = sync_ioctl_( file, line, device, IOCTL_WINETEST_HID_SET_CONTEXT, context, ARRAY_SIZE(context), NULL, 0, INFINITE );
+    ok_(file, line)( ret, "IOCTL_WINETEST_HID_SET_CONTEXT failed, last error %u\n", GetLastError() );
+    ret = sync_ioctl_( file, line, device, IOCTL_WINETEST_HID_SET_EXPECT, expect, expect_size, NULL, 0, INFINITE );
+    ok_(file, line)( ret, "IOCTL_WINETEST_HID_SET_EXPECT failed, last error %u\n", GetLastError() );
 }
 
-#define wait_hid_expect( a, b ) wait_hid_expect_( __LINE__, a, b )
-static void wait_hid_expect_( int line, HANDLE file, DWORD timeout )
+void wait_hid_expect_( const char *file, int line, HANDLE device, DWORD timeout )
 {
-    BOOL ret = sync_ioctl_( line, file, IOCTL_WINETEST_HID_WAIT_EXPECT, NULL, 0, NULL, 0, timeout );
-    ok_(__FILE__, line)( ret, "IOCTL_WINETEST_HID_WAIT_EXPECT failed, last error %u\n", GetLastError() );
+    BOOL ret = sync_ioctl_( file, line, device, IOCTL_WINETEST_HID_WAIT_EXPECT, NULL, 0, NULL, 0, timeout );
+    ok_(file, line)( ret, "IOCTL_WINETEST_HID_WAIT_EXPECT failed, last error %u\n", GetLastError() );
 
-    set_hid_expect_( line, file, NULL, 0 );
+    set_hid_expect_( file, line, device, NULL, 0 );
 }
 
-#define send_hid_input( a, b, c ) send_hid_input_( __LINE__, a, b, c )
-static void send_hid_input_( int line, HANDLE file, struct hid_expect *expect, DWORD expect_size )
+void send_hid_input_( const char *file, int line, HANDLE device, struct hid_expect *expect, DWORD expect_size )
 {
     char context[64];
     BOOL ret;
 
     fill_context( line, context, ARRAY_SIZE(context) );
-    ret = sync_ioctl_( line, file, IOCTL_WINETEST_HID_SET_CONTEXT, context, ARRAY_SIZE(context), NULL, 0, INFINITE );
-    ok_(__FILE__, line)( ret, "IOCTL_WINETEST_HID_SET_CONTEXT failed, last error %u\n", GetLastError() );
-    ret = sync_ioctl( file, IOCTL_WINETEST_HID_SEND_INPUT, expect, expect_size, NULL, 0, INFINITE );
-    ok( ret, "IOCTL_WINETEST_HID_SEND_INPUT failed, last error %u\n", GetLastError() );
+    ret = sync_ioctl_( file, line, device, IOCTL_WINETEST_HID_SET_CONTEXT, context, ARRAY_SIZE(context), NULL, 0, INFINITE );
+    ok_(file, line)( ret, "IOCTL_WINETEST_HID_SET_CONTEXT failed, last error %u\n", GetLastError() );
+    ret = sync_ioctl_( file, line, device, IOCTL_WINETEST_HID_SEND_INPUT, expect, expect_size, NULL, 0, INFINITE );
+    ok_(file, line)( ret, "IOCTL_WINETEST_HID_SEND_INPUT failed, last error %u\n", GetLastError() );
 }
 
 static void test_hidp_get_input( HANDLE file, int report_id, ULONG report_len, PHIDP_PREPARSED_DATA preparsed )
@@ -3266,7 +3243,7 @@ done:
     SetCurrentDirectoryW( cwd );
 }
 
-static void cleanup_registry_keys(void)
+void cleanup_registry_keys(void)
 {
     static const WCHAR joystick_oem_path[] = L"System\\CurrentControlSet\\Control\\MediaProperties\\"
                                               "PrivateProperties\\Joystick\\OEM";
@@ -3299,9 +3276,8 @@ static void cleanup_registry_keys(void)
     RegCloseKey( root_key );
 }
 
-#define dinput_driver_start( a, b, c, d, e ) dinput_driver_start_( __LINE__, a, b, c, d, e )
-static BOOL dinput_driver_start_( int line, const BYTE *desc_buf, ULONG desc_len, const HIDP_CAPS *caps,
-                                  struct hid_expect *expect, ULONG expect_size )
+BOOL dinput_driver_start_( const char *file, int line, const BYTE *desc_buf, ULONG desc_len,
+                           const HIDP_CAPS *caps, struct hid_expect *expect, ULONG expect_size )
 {
     static const HID_DEVICE_ATTRIBUTES attributes =
     {
@@ -3318,192 +3294,85 @@ static BOOL dinput_driver_start_( int line, const BYTE *desc_buf, ULONG desc_len
 
     status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\winetest",
                               0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL );
-    ok_(__FILE__, line)( !status, "RegCreateKeyExW returned %#x\n", status );
+    ok_(file, line)( !status, "RegCreateKeyExW returned %#x\n", status );
     status = RegSetValueExW( hkey, L"ReportID", 0, REG_DWORD, (void *)&report_id, sizeof(report_id) );
-    ok_(__FILE__, line)( !status, "RegSetValueExW returned %#x\n", status );
+    ok_(file, line)( !status, "RegSetValueExW returned %#x\n", status );
     status = RegSetValueExW( hkey, L"PolledMode", 0, REG_DWORD, (void *)&polled, sizeof(polled) );
-    ok_(__FILE__, line)( !status, "RegSetValueExW returned %#x\n", status );
+    ok_(file, line)( !status, "RegSetValueExW returned %#x\n", status );
     status = RegSetValueExW( hkey, L"Descriptor", 0, REG_BINARY, (void *)desc_buf, desc_len );
-    ok_(__FILE__, line)( !status, "RegSetValueExW returned %#x\n", status );
+    ok_(file, line)( !status, "RegSetValueExW returned %#x\n", status );
     status = RegSetValueExW( hkey, L"Attributes", 0, REG_BINARY, (void *)&attributes, sizeof(attributes) );
-    ok_(__FILE__, line)( !status, "RegSetValueExW returned %#x\n", status );
+    ok_(file, line)( !status, "RegSetValueExW returned %#x\n", status );
     status = RegSetValueExW( hkey, L"Caps", 0, REG_BINARY, (void *)caps, sizeof(*caps) );
-    ok_(__FILE__, line)( !status, "RegSetValueExW returned %#x\n", status );
+    ok_(file, line)( !status, "RegSetValueExW returned %#x\n", status );
     status = RegSetValueExW( hkey, L"Expect", 0, REG_BINARY, (void *)expect, expect_size );
-    ok_(__FILE__, line)( !status, "RegSetValueExW returned %#x\n", status );
+    ok_(file, line)( !status, "RegSetValueExW returned %#x\n", status );
     status = RegSetValueExW( hkey, L"Input", 0, REG_BINARY, NULL, 0 );
-    ok_(__FILE__, line)( !status, "RegSetValueExW returned %#x\n", status );
+    ok_(file, line)( !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_(__FILE__, line)( !status, "RegSetValueExW returned %#x\n", status );
+    ok_(file, line)( !status, "RegSetValueExW returned %#x\n", status );
 
     return pnp_driver_start( L"driver_hid.dll" );
 }
 
-static BOOL CALLBACK find_test_device( const DIDEVICEINSTANCEW *devinst, void *context )
+BOOL dinput_test_init_( const char *file, int line )
+{
+    BOOL is_wow64;
+
+    subtest_(file, line)( "hid" );
+    instance = GetModuleHandleW( NULL );
+    localized = GetUserDefaultLCID() != MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
+    pSignerSign = (void *)GetProcAddress( LoadLibraryW( L"mssign32" ), "SignerSign" );
+
+    if (IsWow64Process( GetCurrentProcess(), &is_wow64 ) && is_wow64)
+    {
+        skip( "Running in WoW64.\n" );
+        return FALSE;
+    }
+
+    test_data_mapping = CreateFileMappingW( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
+                                            sizeof(*test_data), L"Global\\winetest_dinput_section" );
+    if (!test_data_mapping && GetLastError() == ERROR_ACCESS_DENIED)
+    {
+        win_skip( "Failed to create test data mapping.\n" );
+        return FALSE;
+    }
+    ok( !!test_data_mapping, "got error %u\n", GetLastError() );
+    test_data = MapViewOfFile( test_data_mapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 1024 );
+    test_data->running_under_wine = !strcmp( winetest_platform, "wine" );
+    test_data->winetest_report_success = winetest_report_success;
+    test_data->winetest_debug = winetest_debug;
+
+    okfile = CreateFileW( L"C:\\windows\\winetest_dinput_okfile", GENERIC_READ | GENERIC_WRITE,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL );
+    ok( okfile != INVALID_HANDLE_VALUE, "failed to create file, error %u\n", GetLastError() );
+
+    subtest( "driver_hid" );
+    return TRUE;
+}
+
+void dinput_test_exit(void)
+{
+    UnmapViewOfFile( test_data );
+    CloseHandle( test_data_mapping );
+    CloseHandle( okfile );
+    DeleteFileW( L"C:\\windows\\winetest_dinput_okfile" );
+}
+
+BOOL CALLBACK find_test_device( const DIDEVICEINSTANCEW *devinst, void *context )
 {
     if (IsEqualGUID( &devinst->guidProduct, &expect_guid_product ))
         *(DIDEVICEINSTANCEW *)context = *devinst;
     return DIENUM_CONTINUE;
 }
 
-struct check_objects_todos
+HRESULT dinput_test_create_device( DWORD version, DIDEVICEINSTANCEW *devinst, IDirectInputDevice8W **device )
 {
-    BOOL type;
-    BOOL guid;
-    BOOL usage;
-    BOOL name;
-};
-
-struct check_objects_params
-{
-    DWORD version;
-    UINT index;
-    UINT expect_count;
-    const DIDEVICEOBJECTINSTANCEW *expect_objs;
-    const struct check_objects_todos *todo_objs;
-    BOOL todo_extra;
-};
-
-static BOOL CALLBACK check_objects( const DIDEVICEOBJECTINSTANCEW *obj, void *args )
-{
-    static const DIDEVICEOBJECTINSTANCEW unexpected_obj = {0};
-    static const struct check_objects_todos todo_none = {0};
-    struct check_objects_params *params = args;
-    const DIDEVICEOBJECTINSTANCEW *exp = params->expect_objs + params->index;
-    const struct check_objects_todos *todo;
-
-    if (!params->todo_objs) todo = &todo_none;
-    else todo = params->todo_objs + params->index;
-
-    todo_wine_if( params->todo_extra && params->index >= params->expect_count )
-    ok( params->index < params->expect_count, "unexpected extra object\n" );
-    if (params->index >= params->expect_count) return DIENUM_STOP;
-
-    winetest_push_context( "obj[%d]", params->index );
-
-    ok( params->index < params->expect_count, "unexpected extra object\n" );
-    if (params->index >= params->expect_count) exp = &unexpected_obj;
-
-    check_member( *obj, *exp, "%u", dwSize );
-    todo_wine_if( todo->guid )
-    check_member_guid( *obj, *exp, guidType );
-    todo_wine_if( params->version < 0x700 && (obj->dwType & DIDFT_BUTTON) )
-    check_member( *obj, *exp, "%#x", dwOfs );
-    todo_wine_if( todo->type )
-    check_member( *obj, *exp, "%#x", dwType );
-    check_member( *obj, *exp, "%#x", dwFlags );
-    if (!localized) todo_wine_if( todo->name )check_member_wstr( *obj, *exp, tszName );
-    check_member( *obj, *exp, "%u", dwFFMaxForce );
-    check_member( *obj, *exp, "%u", dwFFForceResolution );
-    check_member( *obj, *exp, "%u", wCollectionNumber );
-    check_member( *obj, *exp, "%u", wDesignatorIndex );
-    check_member( *obj, *exp, "%#04x", wUsagePage );
-    todo_wine_if( todo->usage )
-    check_member( *obj, *exp, "%#04x", wUsage );
-    check_member( *obj, *exp, "%#04x", dwDimension );
-    check_member( *obj, *exp, "%#04x", wExponent );
-    check_member( *obj, *exp, "%u", wReportId );
-
-    winetest_pop_context();
-
-    params->index++;
-    return DIENUM_CONTINUE;
-}
-
-static BOOL CALLBACK check_object_count( const DIDEVICEOBJECTINSTANCEW *obj, void *args )
-{
-    DWORD *count = args;
-    *count = *count + 1;
-    return DIENUM_CONTINUE;
-}
-
-struct check_effects_params
-{
-    UINT index;
-    UINT expect_count;
-    const DIEFFECTINFOW *expect_effects;
-};
-
-static BOOL CALLBACK check_effects( const DIEFFECTINFOW *effect, void *args )
-{
-    static const DIEFFECTINFOW unexpected_effect = {0};
-    struct check_effects_params *params = args;
-    const DIEFFECTINFOW *exp = params->expect_effects + params->index;
-
-    winetest_push_context( "effect[%d]", params->index );
-
-    ok( params->index < params->expect_count, "unexpected extra object\n" );
-    if (params->index >= params->expect_count) exp = &unexpected_effect;
-
-    check_member( *effect, *exp, "%u", dwSize );
-    check_member_guid( *effect, *exp, guid );
-    check_member( *effect, *exp, "%#x", dwEffType );
-    check_member( *effect, *exp, "%#x", dwStaticParams );
-    check_member( *effect, *exp, "%#x", dwDynamicParams );
-    check_member_wstr( *effect, *exp, tszName );
-
-    winetest_pop_context();
-    params->index++;
-
-    return DIENUM_CONTINUE;
-}
-
-static BOOL CALLBACK check_effect_count( const DIEFFECTINFOW *effect, void *args )
-{
-    DWORD *count = args;
-    *count = *count + 1;
-    return DIENUM_CONTINUE;
-}
-
-static BOOL CALLBACK check_no_created_effect_objects( IDirectInputEffect *effect, void *context )
-{
-    ok( 0, "unexpected effect %p\n", effect );
-    return DIENUM_CONTINUE;
-}
-
-struct check_created_effect_params
-{
-    IDirectInputEffect *expect_effect;
-    DWORD count;
-};
-
-static BOOL CALLBACK check_created_effect_objects( IDirectInputEffect *effect, void *context )
-{
-    struct check_created_effect_params *params = context;
-    ULONG ref;
-
-    ok( effect == params->expect_effect, "got effect %p, expected %p\n", effect, params->expect_effect );
-    params->count++;
-
-    IDirectInputEffect_AddRef( effect );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 1, "got ref %u, expected 1\n", ref );
-    return DIENUM_CONTINUE;
-}
-
-static BOOL CALLBACK enum_device_count( const DIDEVICEINSTANCEW *devinst, void *context )
-{
-    DWORD *count = context;
-    *count = *count + 1;
-    return DIENUM_CONTINUE;
-}
-
-static HRESULT create_dinput_device( DWORD version, DIDEVICEINSTANCEW *devinst, IDirectInputDevice8W **device )
-{
-    DIPROPDWORD prop_dword =
-    {
-        .diph =
-        {
-            .dwSize = sizeof(DIPROPDWORD),
-            .dwHeaderSize = sizeof(DIPROPHEADER),
-            .dwHow = DIPH_DEVICE,
-        },
-    };
     IDirectInput8W *di8;
     IDirectInputW *di;
-    ULONG ref, count;
     HRESULT hr;
+    ULONG ref;
 
     if (version >= 0x800)
     {
@@ -3524,76 +3393,6 @@ static HRESULT create_dinput_device( DWORD version, DIDEVICEINSTANCEW *devinst,
             return DIERR_DEVICENOTREG;
         }
 
-        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, NULL, NULL, DIEDFL_ALLDEVICES );
-        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, enum_device_count, &count, 0xdeadbeef );
-        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-        hr = IDirectInput8_EnumDevices( di8, 0xdeadbeef, enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-
-        count = 0;
-        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        ok( count == 3, "got count %u, expected 0\n", count );
-        count = 0;
-        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_DEVICE, enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        ok( count == 0, "got count %u, expected 0\n", count );
-        count = 0;
-        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_POINTER, enum_device_count, &count,
-                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        todo_wine
-        ok( count == 3, "got count %u, expected 3\n", count );
-        count = 0;
-        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_KEYBOARD, enum_device_count, &count,
-                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        todo_wine
-        ok( count == 3, "got count %u, expected 3\n", count );
-        count = 0;
-        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_GAMECTRL, enum_device_count, &count,
-                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        ok( count == 1, "got count %u, expected 1\n", count );
-
-        count = 0;
-        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        ok( count == 1, "got count %u, expected 1\n", count );
-
-        count = 0;
-        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_FORCEFEEDBACK );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        if (IsEqualGUID( &devinst->guidFFDriver, &GUID_NULL )) ok( count == 0, "got count %u, expected 0\n", count );
-        else ok( count == 1, "got count %u, expected 1\n", count );
-
-        count = 0;
-        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff) + 1, enum_device_count, &count, DIEDFL_ALLDEVICES );
-        if ((devinst->dwDevType & 0xff) != DI8DEVTYPE_SUPPLEMENTAL) ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        else ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-        ok( count == 0, "got count %u, expected 0\n", count );
-
-        hr = IDirectInput8_CreateDevice( di8, &devinst->guidInstance, NULL, NULL );
-        ok( hr == E_POINTER, "CreateDevice returned %#x\n", hr );
-        hr = IDirectInput8_CreateDevice( di8, NULL, device, NULL );
-        ok( hr == E_POINTER, "CreateDevice returned %#x\n", hr );
-        hr = IDirectInput8_CreateDevice( di8, &GUID_NULL, device, NULL );
-        ok( hr == DIERR_DEVICENOTREG, "CreateDevice returned %#x\n", hr );
-        hr = IDirectInput8_CreateDevice( di8, &devinst->guidInstance, device, NULL );
-        ok( hr == DI_OK, "CreateDevice returned %#x\n", hr );
-
-        prop_dword.dwData = 0xdeadbeef;
-        hr = IDirectInputDevice8_GetProperty( *device, DIPROP_VIDPID, &prop_dword.diph );
-        ok( hr == DI_OK, "GetProperty DIPROP_VIDPID returned %#x\n", hr );
-        /* Wine may get the wrong device here, because the test driver creates another instance of
-           hidclass.sys, and gets duplicate rawinput handles, which we use in the guidInstance */
-        todo_wine_if( prop_dword.dwData != EXPECT_VIDPID )
-        ok( prop_dword.dwData == EXPECT_VIDPID, "got %#x expected %#x\n", prop_dword.dwData, EXPECT_VIDPID );
-
-        ref = IDirectInputDevice8_Release( *device );
-        ok( ref == 0, "Release returned %d\n", ref );
-
         hr = IDirectInput8_CreateDevice( di8, &expect_guid_product, device, NULL );
         ok( hr == DI_OK, "CreateDevice returned %#x\n", hr );
 
@@ -3621,55 +3420,6 @@ static HRESULT create_dinput_device( DWORD version, DIDEVICEINSTANCEW *devinst,
             return DIERR_DEVICENOTREG;
         }
 
-        hr = IDirectInput_EnumDevices( di, 0, NULL, NULL, DIEDFL_ALLDEVICES );
-        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, 0xdeadbeef );
-        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-        hr = IDirectInput_EnumDevices( di, 0xdeadbeef, enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, DIEDFL_INCLUDEHIDDEN );
-        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-
-        count = 0;
-        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        ok( count == 3, "got count %u, expected 0\n", count );
-        count = 0;
-        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_DEVICE, enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        ok( count == 0, "got count %u, expected 0\n", count );
-        count = 0;
-        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_MOUSE, enum_device_count, &count,
-                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        todo_wine
-        ok( count == 3, "got count %u, expected 3\n", count );
-        count = 0;
-        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_KEYBOARD, enum_device_count, &count,
-                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        todo_wine
-        ok( count == 3, "got count %u, expected 3\n", count );
-        count = 0;
-        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_JOYSTICK, enum_device_count, &count,
-                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        ok( count == 1, "got count %u, expected 1\n", count );
-
-        count = 0;
-        hr = IDirectInput_EnumDevices( di, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        ok( count == 1, "got count %u, expected 1\n", count );
-
-        count = 0;
-        hr = IDirectInput_EnumDevices( di, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_FORCEFEEDBACK );
-        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
-        if (IsEqualGUID( &devinst->guidFFDriver, &GUID_NULL )) ok( count == 0, "got count %u, expected 0\n", count );
-        else ok( count == 1, "got count %u, expected 1\n", count );
-
-        hr = IDirectInput_EnumDevices( di, 0x14, enum_device_count, &count, DIEDFL_ALLDEVICES );
-        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
-
         hr = IDirectInput_CreateDevice( di, &expect_guid_product, (IDirectInputDeviceW **)device, NULL );
         ok( hr == DI_OK, "CreateDevice returned %#x\n", hr );
 
@@ -3681,6350 +3431,15 @@ static HRESULT create_dinput_device( DWORD version, DIDEVICEINSTANCEW *devinst,
     return DI_OK;
 }
 
-static void test_simple_joystick(void)
-{
-#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, Report),
-                REPORT_ID(1, 1),
-
-                USAGE(1, HID_USAGE_GENERIC_WHEEL),
-                USAGE(4, (0xff01u<<16)|(0x1234)),
-                USAGE(1, HID_USAGE_GENERIC_X),
-                USAGE(1, HID_USAGE_GENERIC_Y),
-                USAGE(4, (HID_USAGE_PAGE_SIMULATION<<16)|HID_USAGE_SIMULATION_RUDDER),
-                USAGE(4, (HID_USAGE_PAGE_DIGITIZER<<16)|HID_USAGE_DIGITIZER_TIP_PRESSURE),
-                USAGE(4, (HID_USAGE_PAGE_CONSUMER<<16)|HID_USAGE_CONSUMER_VOLUME),
-                LOGICAL_MINIMUM(1, 0xe7),
-                LOGICAL_MAXIMUM(1, 0x38),
-                PHYSICAL_MINIMUM(1, 0xe7),
-                PHYSICAL_MAXIMUM(1, 0x38),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 7),
-                INPUT(1, Data|Var|Abs),
-
-                USAGE(1, HID_USAGE_GENERIC_HATSWITCH),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 8),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 8),
-                REPORT_SIZE(1, 4),
-                REPORT_COUNT(1, 1),
-                INPUT(1, Data|Var|Abs|Null),
-
-                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
-                USAGE_MINIMUM(1, 1),
-                USAGE_MAXIMUM(1, 2),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 1),
-                REPORT_COUNT(1, 4),
-                INPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-        END_COLLECTION,
-    };
-#undef REPORT_ID_OR_USAGE_PAGE
-#include "pop_hid_macros.h"
-
-    static const HIDP_CAPS hid_caps =
-    {
-        .InputReportByteLength = 9,
-    };
-    static const DIDEVCAPS expect_caps =
-    {
-        .dwSize = sizeof(DIDEVCAPS),
-        .dwFlags = DIDC_ATTACHED | DIDC_EMULATED,
-        .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK,
-        .dwAxes = 6,
-        .dwPOVs = 1,
-        .dwButtons = 2,
-    };
-    struct hid_expect injected_input[] =
-    {
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_buf = {1,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0},
-        },
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_buf = {1,0x10,0x10,0x38,0x38,0x10,0x10,0x10,0xf8},
-        },
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_buf = {1,0x10,0x10,0x01,0x01,0x10,0x10,0x10,0x00},
-        },
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_buf = {1,0x10,0x10,0x01,0x01,0x10,0x10,0x10,0x00},
-        },
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_buf = {1,0x10,0x10,0x80,0x80,0x10,0x10,0x10,0xff},
-        },
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_buf = {1,0x10,0x10,0x10,0xee,0x10,0x10,0x10,0x54},
-        },
-    };
-    static const struct DIJOYSTATE2 expect_state[] =
-    {
-        {.lX = 32767, .lY = 32767, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = 32767, .lY = 32767, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = 65535, .lY = 65535, .lZ = 32767, .rgdwPOV = {31500, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
-        {.lX = 20779, .lY = 20779, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = 20779, .lY = 20779, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = 0, .lY = 0, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
-        {.lX = 32767, .lY = 5594, .lZ = 32767, .rgdwPOV = {13500, -1, -1, -1}, .rgbButtons = {0x80}},
-    };
-    static const struct DIJOYSTATE2 expect_state_abs[] =
-    {
-        {.lX = -9000, .lY = 26000, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = -9000, .lY = 26000, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = -4000, .lY = 51000, .lZ = 26000, .rgdwPOV = {31500, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
-        {.lX = -10667, .lY = 12905, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = -10667, .lY = 12905, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = -14000, .lY = 1000, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
-        {.lX = -9000, .lY = 1000, .lZ = 26000, .rgdwPOV = {13500, -1, -1, -1}, .rgbButtons = {0x80}},
-    };
-    static const struct DIJOYSTATE2 expect_state_rel[] =
-    {
-        {.lX = 0, .lY = 0, .rgdwPOV = {13500, -1, -1, -1}, .rgbButtons = {0x80, 0}},
-        {.lX = 9016, .lY = -984, .lZ = -25984, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = 40, .lY = 40, .rgdwPOV = {31500, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
-        {.lX = -55, .lY = -55, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = 0, .lY = 0, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
-        {.lX = -129, .lY = -129, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
-        {.lX = 144, .lY = 110, .rgdwPOV = {13500, -1, -1, -1}, .rgbButtons = {0x80}},
-    };
-    static const DIDEVICEOBJECTDATA expect_objdata[] =
-    {
-        {.dwOfs = 0x4, .dwData = 0xffff, .dwSequence = 0xa},
-        {.dwOfs = 0x4, .dwData = 0xffff, .dwSequence = 0xa},
-        {.dwOfs = 0, .dwData = 0xffff, .dwSequence = 0xa},
-        {.dwOfs = 0x20, .dwData = 31500, .dwSequence = 0xa},
-        {.dwOfs = 0x30, .dwData = 0x80, .dwSequence = 0xa},
-        {.dwOfs = 0x4, .dwData = 0x512b, .dwSequence = 0xd},
-        {.dwOfs = 0, .dwData = 0x512b, .dwSequence = 0xd},
-        {.dwOfs = 0x20, .dwData = -1, .dwSequence = 0xd},
-        {.dwOfs = 0x30, .dwData = 0, .dwSequence = 0xd},
-        {.dwOfs = 0x31, .dwData = 0, .dwSequence = 0xd},
-        {.dwOfs = 0x4, .dwData = 0, .dwSequence = 0xf},
-        {.dwOfs = 0, .dwData = 0, .dwSequence = 0xf},
-        {.dwOfs = 0x30, .dwData = 0x80, .dwSequence = 0xf},
-        {.dwOfs = 0x31, .dwData = 0x80, .dwSequence = 0xf},
-    };
-
-    const DIDEVICEINSTANCEW expect_devinst =
-    {
-        .dwSize = sizeof(DIDEVICEINSTANCEW),
-        .guidInstance = expect_guid_product,
-        .guidProduct = expect_guid_product,
-        .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK,
-        .tszInstanceName = L"Wine test root driver",
-        .tszProductName = L"Wine test root driver",
-        .guidFFDriver = GUID_NULL,
-        .wUsagePage = HID_USAGE_PAGE_GENERIC,
-        .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-    };
-    const DIDEVICEOBJECTINSTANCEW expect_objects[] =
-    {
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(6),
-            .tszName = L"Volume",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_CONSUMER,
-            .wUsage = HID_USAGE_CONSUMER_VOLUME,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = 0x4,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(7),
-            .tszName = L"Tip Pressure",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_DIGITIZER,
-            .wUsage = HID_USAGE_DIGITIZER_TIP_PRESSURE,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_RzAxis,
-            .dwOfs = 0x8,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(5),
-            .dwFlags = DIDOI_ASPECTPOSITION,
-            .tszName = L"Rudder",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_SIMULATION,
-            .wUsage = HID_USAGE_SIMULATION_RUDDER,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_YAxis,
-            .dwOfs = 0xc,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(1),
-            .dwFlags = DIDOI_ASPECTPOSITION,
-            .tszName = L"Y Axis",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_Y,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_XAxis,
-            .dwOfs = 0x10,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(0),
-            .dwFlags = DIDOI_ASPECTPOSITION,
-            .tszName = L"X Axis",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_X,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_ZAxis,
-            .dwOfs = 0x18,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(2),
-            .dwFlags = DIDOI_ASPECTPOSITION,
-            .tszName = L"Wheel",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_WHEEL,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_POV,
-            .dwOfs = 0x1c,
-            .dwType = DIDFT_POV|DIDFT_MAKEINSTANCE(0),
-            .tszName = L"Hat Switch",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_HATSWITCH,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Button,
-            .dwOfs = 0x20,
-            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(0),
-            .tszName = L"Button 0",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_BUTTON,
-            .wUsage = 0x1,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Button,
-            .dwOfs = 0x21,
-            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(1),
-            .tszName = L"Button 1",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_BUTTON,
-            .wUsage = 0x2,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(0),
-            .tszName = L"Collection 0 - Joystick",
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(1),
-            .tszName = L"Collection 1 - Joystick",
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-        },
-    };
-    const DIEFFECTINFOW expect_effects[] = {};
-
-    struct check_objects_params check_objects_params =
-    {
-        .version = DIRECTINPUT_VERSION,
-        .expect_count = ARRAY_SIZE(expect_objects),
-        .expect_objs = expect_objects,
-    };
-    struct check_effects_params check_effects_params =
-    {
-        .expect_count = ARRAY_SIZE(expect_effects),
-        .expect_effects = expect_effects,
-    };
-    DIPROPGUIDANDPATH prop_guid_path =
-    {
-        .diph =
-            {
-                .dwSize = sizeof(DIPROPGUIDANDPATH),
-                .dwHeaderSize = sizeof(DIPROPHEADER),
-                .dwHow = DIPH_DEVICE,
-            },
-    };
-    DIPROPSTRING prop_string =
-    {
-        .diph =
-            {
-                .dwSize = sizeof(DIPROPSTRING),
-                .dwHeaderSize = sizeof(DIPROPHEADER),
-                .dwHow = DIPH_DEVICE,
-            },
-    };
-    DIPROPDWORD prop_dword =
-    {
-        .diph =
-            {
-                .dwSize = sizeof(DIPROPDWORD),
-                .dwHeaderSize = sizeof(DIPROPHEADER),
-                .dwHow = DIPH_DEVICE,
-            },
-    };
-    DIPROPRANGE prop_range =
-    {
-        .diph =
-            {
-                .dwSize = sizeof(DIPROPRANGE),
-                .dwHeaderSize = sizeof(DIPROPHEADER),
-                .dwHow = DIPH_DEVICE,
-            },
-    };
-    DIPROPPOINTER prop_pointer =
-    {
-        .diph =
-            {
-                .dwSize = sizeof(DIPROPPOINTER),
-                .dwHeaderSize = sizeof(DIPROPHEADER),
-            },
-    };
-    DIOBJECTDATAFORMAT objdataformat[32] = {{0}};
-    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
-    DIDEVICEOBJECTDATA objdata[32] = {{0}};
-    DIDEVICEOBJECTINSTANCEW objinst = {0};
-    DIDEVICEINSTANCEW devinst = {0};
-    DIEFFECTINFOW effectinfo = {0};
-    DIDATAFORMAT dataformat = {0};
-    IDirectInputDevice8W *device;
-    IDirectInputEffect *effect;
-    DIEFFESCAPE escape = {0};
-    DIDEVCAPS caps = {0};
-    HANDLE event, file;
-    char buffer[1024];
-    DIJOYSTATE2 state;
-    ULONG i, res, ref;
-    HRESULT hr;
-    WCHAR *tmp;
-    GUID guid;
-    HWND hwnd;
-
-    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
-    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
-    SetCurrentDirectoryW( tempdir );
-
-    cleanup_registry_keys();
-    if (!dinput_driver_start( report_desc, sizeof(report_desc), &hid_caps, NULL, 0 )) goto done;
-    if (FAILED(hr = create_dinput_device( DIRECTINPUT_VERSION, &devinst, &device ))) goto done;
-
-    hr = IDirectInputDevice8_Initialize( device, instance, 0x0700, &GUID_NULL );
-    todo_wine
-    ok( hr == DIERR_BETADIRECTINPUTVERSION, "Initialize returned %#x\n", hr );
-    hr = IDirectInputDevice8_Initialize( device, instance, DIRECTINPUT_VERSION, NULL );
-    todo_wine
-    ok( hr == E_POINTER, "Initialize returned %#x\n", hr );
-    hr = IDirectInputDevice8_Initialize( device, NULL, DIRECTINPUT_VERSION, &GUID_NULL );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "Initialize returned %#x\n", hr );
-    hr = IDirectInputDevice8_Initialize( device, instance, DIRECTINPUT_VERSION, &GUID_NULL );
-    todo_wine
-    ok( hr == REGDB_E_CLASSNOTREG, "Initialize returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_Initialize( device, instance, DIRECTINPUT_VERSION, &devinst.guidInstance );
-    ok( hr == DI_OK, "Initialize returned %#x\n", hr );
-    guid = devinst.guidInstance;
-    memset( &devinst, 0, sizeof(devinst) );
-    devinst.dwSize = sizeof(DIDEVICEINSTANCEW);
-    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
-    ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
-    ok( IsEqualGUID( &guid, &devinst.guidInstance ), "got %s expected %s\n", debugstr_guid( &guid ),
-        debugstr_guid( &devinst.guidInstance ) );
-    hr = IDirectInputDevice8_Initialize( device, instance, DIRECTINPUT_VERSION, &devinst.guidProduct );
-    ok( hr == DI_OK, "Initialize returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_GetDeviceInfo( device, NULL );
-    ok( hr == E_POINTER, "GetDeviceInfo returned %#x\n", hr );
-    devinst.dwSize = sizeof(DIDEVICEINSTANCEW) + 1;
-    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
-    ok( hr == DIERR_INVALIDPARAM, "GetDeviceInfo returned %#x\n", hr );
-
-    devinst.dwSize = sizeof(DIDEVICEINSTANCE_DX3W);
-    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
-    ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
-    todo_wine
-    check_member_guid( devinst, expect_devinst, guidInstance );
-    check_member_guid( devinst, expect_devinst, guidProduct );
-    check_member( devinst, expect_devinst, "%#x", dwDevType );
-    todo_wine
-    check_member_wstr( devinst, expect_devinst, tszInstanceName );
-    todo_wine
-    check_member_wstr( devinst, expect_devinst, tszProductName );
-
-    memset( &devinst, 0, sizeof(devinst) );
-    devinst.dwSize = sizeof(DIDEVICEINSTANCEW);
-    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
-    ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
-    check_member( devinst, expect_devinst, "%d", dwSize );
-    todo_wine
-    check_member_guid( devinst, expect_devinst, guidInstance );
-    check_member_guid( devinst, expect_devinst, guidProduct );
-    check_member( devinst, expect_devinst, "%#x", dwDevType );
-    todo_wine
-    check_member_wstr( devinst, expect_devinst, tszInstanceName );
-    todo_wine
-    check_member_wstr( devinst, expect_devinst, tszProductName );
-    check_member_guid( devinst, expect_devinst, guidFFDriver );
-    check_member( devinst, expect_devinst, "%04x", wUsagePage );
-    check_member( devinst, expect_devinst, "%04x", wUsage );
-
-    hr = IDirectInputDevice8_GetCapabilities( device, NULL );
-    ok( hr == E_POINTER, "GetCapabilities returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetCapabilities( device, &caps );
-    ok( hr == DIERR_INVALIDPARAM, "GetCapabilities returned %#x\n", hr );
-    caps.dwSize = sizeof(DIDEVCAPS);
-    hr = IDirectInputDevice8_GetCapabilities( device, &caps );
-    ok( hr == DI_OK, "GetCapabilities returned %#x\n", hr );
-    check_member( caps, expect_caps, "%d", dwSize );
-    check_member( caps, expect_caps, "%#x", dwFlags );
-    check_member( caps, expect_caps, "%#x", dwDevType );
-    check_member( caps, expect_caps, "%d", dwAxes );
-    check_member( caps, expect_caps, "%d", dwButtons );
-    check_member( caps, expect_caps, "%d", dwPOVs );
-    check_member( caps, expect_caps, "%d", dwFFSamplePeriod );
-    check_member( caps, expect_caps, "%d", dwFFMinTimeResolution );
-    check_member( caps, expect_caps, "%d", dwFirmwareRevision );
-    check_member( caps, expect_caps, "%d", dwHardwareRevision );
-    check_member( caps, expect_caps, "%d", dwFFDriverVersion );
-
-    hr = IDirectInputDevice8_GetProperty( device, NULL, NULL );
-    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, &GUID_NULL, NULL );
-    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, NULL );
-    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, &prop_string.diph );
-    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
-    prop_dword.diph.dwHeaderSize = sizeof(DIPROPHEADER) - 1;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
-    prop_dword.diph.dwHeaderSize = sizeof(DIPROPHEADER);
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_VIDPID returned %#x\n", hr );
-    ok( prop_dword.dwData == EXPECT_VIDPID, "got %#x expected %#x\n", prop_dword.dwData, EXPECT_VIDPID );
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
-    todo_wine
-    ok( IsEqualGUID( &prop_guid_path.guidClass, &GUID_DEVCLASS_HIDCLASS ), "got guid %s\n",
-        debugstr_guid( &prop_guid_path.guidClass ) );
-    todo_wine
-    ok( !wcsncmp( prop_guid_path.wszPath, expect_path, wcslen( expect_path ) ), "got path %s\n",
-        debugstr_w(prop_guid_path.wszPath) );
-    if (!(tmp = wcsrchr( prop_guid_path.wszPath, '&' )))
-        todo_wine ok( 0, "got path %s\n", debugstr_w(prop_guid_path.wszPath) );
-    else
-    {
-        ok( !wcscmp( wcsrchr( prop_guid_path.wszPath, '&' ), expect_path_end ), "got path %s\n",
-            debugstr_w(prop_guid_path.wszPath) );
-    }
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_INSTANCENAME, &prop_string.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_INSTANCENAME returned %#x\n", hr );
-    todo_wine
-    ok( !wcscmp( prop_string.wsz, expect_devinst.tszInstanceName ), "got instance %s\n",
-        debugstr_w(prop_string.wsz) );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_PRODUCTNAME, &prop_string.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_PRODUCTNAME returned %#x\n", hr );
-    todo_wine
-    ok( !wcscmp( prop_string.wsz, expect_devinst.tszProductName ), "got product %s\n",
-        debugstr_w(prop_string.wsz) );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_TYPENAME, &prop_string.diph );
-    todo_wine
-    ok( hr == DI_OK, "GetProperty DIPROP_TYPENAME returned %#x\n", hr );
-    todo_wine
-    ok( !wcscmp( prop_string.wsz, expect_vidpid_str ), "got type %s\n", debugstr_w(prop_string.wsz) );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_USERNAME, &prop_string.diph );
-    ok( hr == S_FALSE, "GetProperty DIPROP_USERNAME returned %#x\n", hr );
-    todo_wine
-    ok( !wcscmp( prop_string.wsz, L"" ), "got user %s\n", debugstr_w(prop_string.wsz) );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_JOYSTICKID, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_JOYSTICKID returned %#x\n", hr );
-    todo_wine
-    ok( prop_dword.dwData == 0, "got %#x expected %#x\n", prop_dword.dwData, 0 );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
-    todo_wine
-    ok( hr == DI_OK, "GetProperty DIPROP_AXISMODE returned %#x\n", hr );
-    todo_wine
-    ok( prop_dword.dwData == DIPROPAXISMODE_ABS, "got %u expected %u\n", prop_dword.dwData, DIPROPAXISMODE_ABS );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
-    ok( prop_dword.dwData == 0, "got %#x expected %#x\n", prop_dword.dwData, 0 );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_FFGAIN returned %#x\n", hr );
-    ok( prop_dword.dwData == 10000, "got %u expected %u\n", prop_dword.dwData, 10000 );
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATION, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "GetProperty DIPROP_CALIBRATION returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GRANULARITY, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_GRANULARITY returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_RANGE returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_KEYNAME, &prop_string.diph );
-    ok( hr == DIERR_INVALIDPARAM, "GetProperty DIPROP_KEYNAME returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_LOGICALRANGE, &prop_range.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_LOGICALRANGE returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_PHYSICALRANGE, &prop_range.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_PHYSICALRANGE returned %#x\n", hr );
-
-    prop_dword.diph.dwHow = DIPH_BYUSAGE;
-    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    ok( prop_dword.dwData == 0, "got %u expected %u\n", prop_dword.dwData, 0 );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GRANULARITY, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_GRANULARITY returned %#x\n", hr );
-    ok( prop_dword.dwData == 1, "got %u expected %u\n", prop_dword.dwData, 1 );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
-    ok( prop_dword.dwData == 10000, "got %u expected %u\n", prop_dword.dwData, 10000 );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
-    ok( prop_dword.dwData == DIPROPCALIBRATIONMODE_COOKED, "got %u expected %u\n", prop_dword.dwData, DIPROPCALIBRATIONMODE_COOKED );
-
-    prop_string.diph.dwHow = DIPH_BYUSAGE;
-    prop_string.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_KEYNAME, &prop_string.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_KEYNAME returned %#x\n", hr );
-    ok( !wcscmp( prop_string.wsz, expect_objects[4].tszName ), "got DIPROP_KEYNAME %s\n",
-        debugstr_w( prop_string.wsz ) );
-    prop_string.diph.dwObj = MAKELONG( 0x1, HID_USAGE_PAGE_BUTTON );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_KEYNAME, &prop_string.diph );
-    todo_wine
-    ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_KEYNAME returned %#x\n", hr );
-    prop_string.diph.dwHow = DIPH_BYUSAGE;
-    prop_string.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_KEYNAME, &prop_string.diph );
-    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_KEYNAME returned %#x\n", hr );
-
-    prop_range.diph.dwHow = DIPH_BYUSAGE;
-    prop_range.diph.dwObj = MAKELONG( 0, 0 );
-    prop_range.lMin = 0xdeadbeef;
-    prop_range.lMax = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_RANGE returned %#x\n", hr );
-    prop_range.diph.dwObj = MAKELONG( 0, HID_USAGE_PAGE_GENERIC );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_RANGE returned %#x\n", hr );
-    prop_range.diph.dwObj = MAKELONG( HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_RANGE returned %#x\n", hr );
-    prop_range.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_range.lMin = 0xdeadbeef;
-    prop_range.lMax = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_RANGE returned %#x\n", hr );
-    ok( prop_range.lMin == 0, "got %d expected %d\n", prop_range.lMin, 0 );
-    ok( prop_range.lMax == 65535, "got %d expected %d\n", prop_range.lMax, 65535 );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_LOGICALRANGE, &prop_range.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_LOGICALRANGE returned %#x\n", hr );
-    ok( prop_range.lMin == -25, "got %d expected %d\n", prop_range.lMin, -25 );
-    ok( prop_range.lMax == 56, "got %d expected %d\n", prop_range.lMax, 56 );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_PHYSICALRANGE, &prop_range.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_PHYSICALRANGE returned %#x\n", hr );
-    ok( prop_range.lMin == -25, "got %d expected %d\n", prop_range.lMin, -25 );
-    ok( prop_range.lMax == 56, "got %d expected %d\n", prop_range.lMax, 56 );
-
-    prop_pointer.diph.dwHow = DIPH_BYUSAGE;
-    prop_pointer.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_APPDATA, &prop_pointer.diph );
-    todo_wine
-    ok( hr == DIERR_NOTINITIALIZED, "GetProperty DIPROP_APPDATA returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_EnumObjects( device, NULL, NULL, DIDFT_ALL );
-    ok( hr == DIERR_INVALIDPARAM, "EnumObjects returned %#x\n", hr );
-    hr = IDirectInputDevice8_EnumObjects( device, check_object_count, &res, 0x20 );
-    ok( hr == DIERR_INVALIDPARAM, "EnumObjects returned %#x\n", hr );
-    res = 0;
-    hr = IDirectInputDevice8_EnumObjects( device, check_object_count, &res, DIDFT_AXIS | DIDFT_PSHBUTTON );
-    ok( hr == DI_OK, "EnumObjects returned %#x\n", hr );
-    ok( res == 8, "got %u expected %u\n", res, 8 );
-    hr = IDirectInputDevice8_EnumObjects( device, check_objects, &check_objects_params, DIDFT_ALL );
-    ok( hr == DI_OK, "EnumObjects returned %#x\n", hr );
-    ok( check_objects_params.index >= check_objects_params.expect_count, "missing %u objects\n",
-        check_objects_params.expect_count - check_objects_params.index );
-
-    hr = IDirectInputDevice8_GetObjectInfo( device, NULL, 0, DIPH_DEVICE );
-    ok( hr == E_POINTER, "GetObjectInfo returned: %#x\n", hr );
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, 0, DIPH_DEVICE );
-    ok( hr == DIERR_INVALIDPARAM, "GetObjectInfo returned: %#x\n", hr );
-    objinst.dwSize = sizeof(DIDEVICEOBJECTINSTANCEW);
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, 0, DIPH_DEVICE );
-    ok( hr == DIERR_INVALIDPARAM, "GetObjectInfo returned: %#x\n", hr );
-
-    res = MAKELONG( HID_USAGE_GENERIC_Z, HID_USAGE_PAGE_GENERIC );
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, res, DIPH_BYUSAGE );
-    ok( hr == DIERR_NOTFOUND, "GetObjectInfo returned: %#x\n", hr );
-    res = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, res, DIPH_BYUSAGE );
-    ok( hr == DI_OK, "GetObjectInfo returned: %#x\n", hr );
-
-    check_member( objinst, expect_objects[4], "%u", dwSize );
-    check_member_guid( objinst, expect_objects[4], guidType );
-    check_member( objinst, expect_objects[4], "%#x", dwOfs );
-    check_member( objinst, expect_objects[4], "%#x", dwType );
-    check_member( objinst, expect_objects[4], "%#x", dwFlags );
-    if (!localized) check_member_wstr( objinst, expect_objects[4], tszName );
-    check_member( objinst, expect_objects[4], "%u", dwFFMaxForce );
-    check_member( objinst, expect_objects[4], "%u", dwFFForceResolution );
-    check_member( objinst, expect_objects[4], "%u", wCollectionNumber );
-    check_member( objinst, expect_objects[4], "%u", wDesignatorIndex );
-    check_member( objinst, expect_objects[4], "%#04x", wUsagePage );
-    check_member( objinst, expect_objects[4], "%#04x", wUsage );
-    check_member( objinst, expect_objects[4], "%#04x", dwDimension );
-    check_member( objinst, expect_objects[4], "%#04x", wExponent );
-    check_member( objinst, expect_objects[4], "%u", wReportId );
-
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, 0x14, DIPH_BYOFFSET );
-    ok( hr == DIERR_NOTFOUND, "GetObjectInfo returned: %#x\n", hr );
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, 0, DIPH_BYOFFSET );
-    ok( hr == DIERR_NOTFOUND, "GetObjectInfo returned: %#x\n", hr );
-    res = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 3 );
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, res, DIPH_BYID );
-    ok( hr == DIERR_NOTFOUND, "GetObjectInfo returned: %#x\n", hr );
-    res = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 1 );
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, res, DIPH_BYID );
-    ok( hr == DI_OK, "GetObjectInfo returned: %#x\n", hr );
-
-    check_member( objinst, expect_objects[8], "%u", dwSize );
-    check_member_guid( objinst, expect_objects[8], guidType );
-    check_member( objinst, expect_objects[8], "%#x", dwOfs );
-    check_member( objinst, expect_objects[8], "%#x", dwType );
-    check_member( objinst, expect_objects[8], "%#x", dwFlags );
-    if (!localized) check_member_wstr( objinst, expect_objects[8], tszName );
-    check_member( objinst, expect_objects[8], "%u", dwFFMaxForce );
-    check_member( objinst, expect_objects[8], "%u", dwFFForceResolution );
-    check_member( objinst, expect_objects[8], "%u", wCollectionNumber );
-    check_member( objinst, expect_objects[8], "%u", wDesignatorIndex );
-    check_member( objinst, expect_objects[8], "%#04x", wUsagePage );
-    check_member( objinst, expect_objects[8], "%#04x", wUsage );
-    check_member( objinst, expect_objects[8], "%#04x", dwDimension );
-    check_member( objinst, expect_objects[8], "%#04x", wExponent );
-    check_member( objinst, expect_objects[8], "%u", wReportId );
-
-    hr = IDirectInputDevice8_EnumEffects( device, NULL, NULL, DIEFT_ALL );
-    ok( hr == DIERR_INVALIDPARAM, "EnumEffects returned %#x\n", hr );
-    res = 0;
-    hr = IDirectInputDevice8_EnumEffects( device, check_effect_count, &res, 0xfe );
-    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
-    ok( res == 0, "got %u expected %u\n", res, 0 );
-    res = 0;
-    hr = IDirectInputDevice8_EnumEffects( device, check_effect_count, &res, DIEFT_PERIODIC );
-    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
-    ok( res == 0, "got %u expected %u\n", res, 0 );
-    hr = IDirectInputDevice8_EnumEffects( device, check_effects, &check_effects_params, DIEFT_ALL );
-    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
-    ok( check_effects_params.index >= check_effects_params.expect_count, "missing %u effects\n",
-        check_effects_params.expect_count - check_effects_params.index );
-
-    hr = IDirectInputDevice8_GetEffectInfo( device, NULL, &GUID_Sine );
-    ok( hr == E_POINTER, "GetEffectInfo returned %#x\n", hr );
-    effectinfo.dwSize = sizeof(DIEFFECTINFOW) + 1;
-    hr = IDirectInputDevice8_GetEffectInfo( device, &effectinfo, &GUID_Sine );
-    ok( hr == DIERR_INVALIDPARAM, "GetEffectInfo returned %#x\n", hr );
-    effectinfo.dwSize = sizeof(DIEFFECTINFOW);
-    hr = IDirectInputDevice8_GetEffectInfo( device, &effectinfo, &GUID_NULL );
-    ok( hr == DIERR_DEVICENOTREG, "GetEffectInfo returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetEffectInfo( device, &effectinfo, &GUID_Sine );
-    ok( hr == DIERR_DEVICENOTREG, "GetEffectInfo returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_SetDataFormat( device, NULL );
-    ok( hr == E_POINTER, "SetDataFormat returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    dataformat.dwSize = sizeof(DIDATAFORMAT);
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    dataformat.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, DIJOFS_Y, DIPH_BYOFFSET );
-    ok( hr == DI_OK, "GetObjectInfo returned: %#x\n", hr );
-
-    check_member( objinst, expect_objects[3], "%u", dwSize );
-    check_member_guid( objinst, expect_objects[3], guidType );
-    check_member( objinst, expect_objects[3], "%#x", dwOfs );
-    check_member( objinst, expect_objects[3], "%#x", dwType );
-    check_member( objinst, expect_objects[3], "%#x", dwFlags );
-    if (!localized) check_member_wstr( objinst, expect_objects[3], tszName );
-    check_member( objinst, expect_objects[3], "%u", dwFFMaxForce );
-    check_member( objinst, expect_objects[3], "%u", dwFFForceResolution );
-    check_member( objinst, expect_objects[3], "%u", wCollectionNumber );
-    check_member( objinst, expect_objects[3], "%u", wDesignatorIndex );
-    check_member( objinst, expect_objects[3], "%#04x", wUsagePage );
-    check_member( objinst, expect_objects[3], "%#04x", wUsage );
-    check_member( objinst, expect_objects[3], "%#04x", dwDimension );
-    check_member( objinst, expect_objects[3], "%#04x", wExponent );
-    check_member( objinst, expect_objects[3], "%u", wReportId );
-
-    hr = IDirectInputDevice8_SetEventNotification( device, (HANDLE)0xdeadbeef );
-    todo_wine
-    ok( hr == E_HANDLE, "SetEventNotification returned: %#x\n", hr );
-    event = CreateEventW( NULL, FALSE, FALSE, NULL );
-    ok( event != NULL, "CreateEventW failed, last error %u\n", GetLastError() );
-    hr = IDirectInputDevice8_SetEventNotification( device, event );
-    ok( hr == DI_OK, "SetEventNotification returned: %#x\n", hr );
-
-    file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
-                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
-                        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
-    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
-
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, 0 );
-    ok( hr == DIERR_INVALIDPARAM, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_BACKGROUND );
-    ok( hr == DIERR_INVALIDPARAM, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE );
-    ok( hr == E_HANDLE, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_BACKGROUND | DISCL_EXCLUSIVE );
-    ok( hr == E_HANDLE, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_FOREGROUND | DISCL_EXCLUSIVE );
-    ok( hr == E_HANDLE, "SetCooperativeLevel returned: %#x\n", hr );
-
-    hwnd = CreateWindowW( L"static", L"dinput", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200,
-                          NULL, NULL, NULL, NULL );
-    SetForegroundWindow( hwnd );
-
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE );
-    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE );
-    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE );
-    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_NOEFFECT, "Unacquire returned: %#x\n", hr );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE );
-    ok( hr == DIERR_ACQUIRED, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    DestroyWindow( hwnd );
-
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE );
-    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-    ok( hr == DIERR_NOTACQUIRED, "GetDeviceState returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_Poll( device );
-    ok( hr == DIERR_NOTACQUIRED, "Poll returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_Poll( device );
-    ok( hr == DI_NOEFFECT, "Poll returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2) + 1, &state );
-    ok( hr == DIERR_INVALIDPARAM, "GetDeviceState returned: %#x\n", hr );
-
-    for (i = 0; i < ARRAY_SIZE(injected_input); ++i)
-    {
-        winetest_push_context( "state[%d]", i );
-        hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-        ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-        check_member( state, expect_state[i], "%d", lX );
-        check_member( state, expect_state[i], "%d", lY );
-        check_member( state, expect_state[i], "%d", lZ );
-        check_member( state, expect_state[i], "%d", lRx );
-        check_member( state, expect_state[i], "%#x", rgdwPOV[0] );
-        check_member( state, expect_state[i], "%#x", rgdwPOV[1] );
-        check_member( state, expect_state[i], "%#x", rgbButtons[0] );
-        check_member( state, expect_state[i], "%#x", rgbButtons[1] );
-        check_member( state, expect_state[i], "%#x", rgbButtons[2] );
-
-        send_hid_input( file, &injected_input[i], sizeof(*injected_input) );
-
-        res = WaitForSingleObject( event, 100 );
-        if (i == 0 || i == 3) ok( res == WAIT_TIMEOUT, "WaitForSingleObject succeeded\n" );
-        else ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-        ResetEvent( event );
-        winetest_pop_context();
-    }
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    winetest_push_context( "state[%d]", i );
-    check_member( state, expect_state[i], "%d", lX );
-    check_member( state, expect_state[i], "%d", lY );
-    check_member( state, expect_state[i], "%d", lZ );
-    check_member( state, expect_state[i], "%d", lRx );
-    check_member( state, expect_state[i], "%#x", rgdwPOV[0] );
-    check_member( state, expect_state[i], "%#x", rgdwPOV[1] );
-    check_member( state, expect_state[i], "%#x", rgbButtons[0] );
-    check_member( state, expect_state[i], "%#x", rgbButtons[1] );
-    check_member( state, expect_state[i], "%#x", rgbButtons[2] );
-    winetest_pop_context();
-
-    res = 1;
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA) - 1, objdata, &res, DIGDD_PEEK );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "GetDeviceData returned %#x\n", hr );
-    res = 1;
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, DIGDD_PEEK );
-    ok( hr == DIERR_NOTBUFFERED, "GetDeviceData returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    prop_dword.diph.dwHow = DIPH_BYUSAGE;
-    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_dword.dwData = 1;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
-    prop_dword.diph.dwHow = DIPH_DEVICE;
-    prop_dword.diph.dwObj = 0;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    res = 1;
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, DIGDD_PEEK );
-    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
-    ok( res == 0, "got %u expected %u\n", res, 0 );
-
-    send_hid_input( file, &injected_input[0], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-
-    res = 1;
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, DIGDD_PEEK );
-    ok( hr == DI_BUFFEROVERFLOW, "GetDeviceData returned %#x\n", hr );
-    ok( res == 0, "got %u expected %u\n", res, 0 );
-    res = 1;
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
-    todo_wine
-    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
-    ok( res == 0, "got %u expected %u\n", res, 0 );
-
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    prop_dword.diph.dwHow = DIPH_DEVICE;
-    prop_dword.diph.dwObj = 0;
-    prop_dword.dwData = 10;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    send_hid_input( file, &injected_input[1], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-
-    res = 1;
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, DIGDD_PEEK );
-    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
-    ok( res == 1, "got %u expected %u\n", res, 1 );
-    check_member( objdata[0], expect_objdata[0], "%#x", dwOfs );
-    check_member( objdata[0], expect_objdata[0], "%#x", dwData );
-    ok( objdata[0].uAppData == -1, "got %p, expected %p\n", (void *)objdata[0].uAppData, (void *)-1 );
-    res = 4;
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
-    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
-    ok( res == 4, "got %u expected %u\n", res, 4 );
-    for (i = 0; i < 4; ++i)
-    {
-        winetest_push_context( "objdata[%d]", i );
-        check_member( objdata[i], expect_objdata[1 + i], "%#x", dwOfs );
-        check_member( objdata[i], expect_objdata[1 + i], "%#x", dwData );
-        ok( objdata[i].uAppData == -1, "got %p, expected %p\n", (void *)objdata[i].uAppData, (void *)-1 );
-        winetest_pop_context();
-    }
-
-    send_hid_input( file, &injected_input[2], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-    send_hid_input( file, &injected_input[4], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-
-    res = 1;
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
-    ok( hr == DI_BUFFEROVERFLOW, "GetDeviceData returned %#x\n", hr );
-    ok( res == 1, "got %u expected %u\n", res, 1 );
-    todo_wine
-    check_member( objdata[0], expect_objdata[5], "%#x", dwOfs );
-    todo_wine
-    check_member( objdata[0], expect_objdata[5], "%#x", dwData );
-    ok( objdata[0].uAppData == -1, "got %p, expected %p\n", (void *)objdata[0].uAppData, (void *)-1 );
-    res = ARRAY_SIZE(objdata);
-    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
-    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
-    ok( res == 8, "got %u expected %u\n", res, 8 );
-    for (i = 0; i < 8; ++i)
-    {
-        winetest_push_context( "objdata[%d]", i );
-        todo_wine
-        check_member( objdata[i], expect_objdata[6 + i], "%#x", dwOfs );
-        todo_wine_if( i == 1 || i == 2 || i == 6 )
-        check_member( objdata[i], expect_objdata[6 + i], "%#x", dwData );
-        ok( objdata[i].uAppData == -1, "got %p, expected %p\n", (void *)objdata[i].uAppData, (void *)-1 );
-        winetest_pop_context();
-    }
-
-    send_hid_input( file, &injected_input[3], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    check_member( state, expect_state[3], "%d", lX );
-    check_member( state, expect_state[3], "%d", lY );
-    check_member( state, expect_state[3], "%d", lZ );
-    check_member( state, expect_state[3], "%d", lRx );
-    check_member( state, expect_state[3], "%d", rgdwPOV[0] );
-    check_member( state, expect_state[3], "%d", rgdwPOV[1] );
-    check_member( state, expect_state[3], "%#x", rgbButtons[0] );
-    check_member( state, expect_state[3], "%#x", rgbButtons[1] );
-    check_member( state, expect_state[3], "%#x", rgbButtons[2] );
-
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    dataformat.dwSize = sizeof(DIDATAFORMAT);
-    dataformat.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
-    dataformat.dwFlags = DIDF_ABSAXIS;
-    dataformat.dwDataSize = sizeof(buffer);
-    dataformat.dwNumObjs = 0;
-    dataformat.rgodf = objdataformat;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-
-    dataformat.dwNumObjs = 1;
-    dataformat.dwDataSize = 8;
-    objdataformat[0].pguid = NULL;
-    objdataformat[0].dwOfs = 2;
-    objdataformat[0].dwType = DIDFT_AXIS|DIDFT_ANYINSTANCE;
-    objdataformat[0].dwFlags = 0;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    objdataformat[0].dwOfs = 4;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-    dataformat.dwDataSize = 10;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    dataformat.dwDataSize = 8;
-    objdataformat[0].dwOfs = 2;
-    objdataformat[0].dwType = DIDFT_OPTIONAL|0xff|DIDFT_ANYINSTANCE;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-
-    dataformat.dwNumObjs = 2;
-    dataformat.dwDataSize = 5;
-    objdataformat[0].pguid = NULL;
-    objdataformat[0].dwOfs = 4;
-    objdataformat[0].dwType = DIDFT_BUTTON|DIDFT_ANYINSTANCE;
-    objdataformat[0].dwFlags = 0;
-    objdataformat[1].pguid = NULL;
-    objdataformat[1].dwOfs = 0;
-    objdataformat[1].dwType = DIDFT_AXIS|DIDFT_MAKEINSTANCE( 0 );
-    objdataformat[1].dwFlags = 0;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    dataformat.dwDataSize = 8;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-
-    dataformat.dwNumObjs = 4;
-    dataformat.dwDataSize = 12;
-    objdataformat[0].pguid = NULL;
-    objdataformat[0].dwOfs = 0;
-    objdataformat[0].dwType = DIDFT_AXIS|DIDFT_MAKEINSTANCE( 0 );
-    objdataformat[0].dwFlags = 0;
-    objdataformat[1].pguid = NULL;
-    objdataformat[1].dwOfs = 0;
-    objdataformat[1].dwType = DIDFT_AXIS|DIDFT_MAKEINSTANCE( 0 );
-    objdataformat[1].dwFlags = 0;
-    objdataformat[2].pguid = &GUID_ZAxis;
-    objdataformat[2].dwOfs = 8;
-    objdataformat[2].dwType = 0xff|DIDFT_ANYINSTANCE;
-    objdataformat[2].dwFlags = 0;
-    objdataformat[3].pguid = &GUID_POV;
-    objdataformat[3].dwOfs = 0;
-    objdataformat[3].dwType = 0xff|DIDFT_ANYINSTANCE;
-    objdataformat[3].dwFlags = 0;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    objdataformat[1].dwType = DIDFT_AXIS|DIDFT_MAKEINSTANCE( 12 );
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    objdataformat[1].dwType = DIDFT_AXIS|DIDFT_MAKEINSTANCE( 0xff );
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    objdataformat[1].dwType = DIDFT_AXIS|DIDFT_MAKEINSTANCE( 1 );
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-    objdataformat[1].pguid = &GUID_RzAxis;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    objdataformat[1].pguid = &GUID_Unknown;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
-    objdataformat[1].pguid = &GUID_YAxis;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-    objdataformat[1].pguid = NULL;
-    objdataformat[1].dwType = DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_MAKEINSTANCE( 0 );
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-
-    dataformat.dwNumObjs = 0;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    send_hid_input( file, &injected_input[4], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    todo_wine
-    ok( res == WAIT_TIMEOUT, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-
-    hr = IDirectInputDevice8_GetDeviceState( device, dataformat.dwDataSize, buffer );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    dataformat.dwNumObjs = 4;
-    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    send_hid_input( file, &injected_input[4], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    todo_wine
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-    send_hid_input( file, &injected_input[3], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-
-    hr = IDirectInputDevice8_GetDeviceState( device, dataformat.dwDataSize, buffer );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    ok( ((ULONG *)buffer)[0] == 0x512b, "got %#x, expected %#x\n", ((ULONG *)buffer)[0], 0x512b );
-    ok( ((ULONG *)buffer)[1] == 0, "got %#x, expected %#x\n", ((ULONG *)buffer)[1], 0 );
-    ok( ((ULONG *)buffer)[2] == 0x7fff, "got %#x, expected %#x\n", ((ULONG *)buffer)[2], 0x7fff );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    send_hid_input( file, &injected_input[4], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-    send_hid_input( file, &injected_input[3], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-    ResetEvent( event );
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    check_member( state, expect_state[3], "%d", lX );
-    check_member( state, expect_state[3], "%d", lY );
-    check_member( state, expect_state[3], "%d", lZ );
-    check_member( state, expect_state[3], "%d", lRx );
-    check_member( state, expect_state[3], "%d", rgdwPOV[0] );
-    check_member( state, expect_state[3], "%d", rgdwPOV[1] );
-    check_member( state, expect_state[3], "%#x", rgbButtons[0] );
-    check_member( state, expect_state[3], "%#x", rgbButtons[1] );
-    check_member( state, expect_state[3], "%#x", rgbButtons[2] );
-
-    prop_range.diph.dwHow = DIPH_DEVICE;
-    prop_range.diph.dwObj = 0;
-    prop_range.lMin = 1000;
-    prop_range.lMax = 51000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_RANGE returned %#x\n", hr );
-    prop_range.diph.dwHow = DIPH_BYUSAGE;
-    prop_range.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_range.lMin = -4000;
-    prop_range.lMax = -14000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_RANGE returned %#x\n", hr );
-    prop_range.lMin = -14000;
-    prop_range.lMax = -4000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_RANGE returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_LOGICALRANGE, &prop_range.diph );
-    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_LOGICALRANGE returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_PHYSICALRANGE, &prop_range.diph );
-    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_PHYSICALRANGE returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_LOGICALRANGE, &prop_range.diph );
-    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_LOGICALRANGE returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_PHYSICALRANGE, &prop_range.diph );
-    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_PHYSICALRANGE returned %#x\n", hr );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    prop_range.diph.dwHow = DIPH_DEVICE;
-    prop_range.diph.dwObj = 0;
-    prop_range.lMin = 0xdeadbeef;
-    prop_range.lMax = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_RANGE returned %#x\n", hr );
-    prop_range.diph.dwHow = DIPH_BYUSAGE;
-    prop_range.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_range.lMin = 0xdeadbeef;
-    prop_range.lMax = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_RANGE returned %#x\n", hr );
-    ok( prop_range.lMin == -14000, "got %d expected %d\n", prop_range.lMin, -14000 );
-    ok( prop_range.lMax == -4000, "got %d expected %d\n", prop_range.lMax, -4000 );
-    prop_range.diph.dwHow = DIPH_BYUSAGE;
-    prop_range.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_Y, HID_USAGE_PAGE_GENERIC );
-    prop_range.lMin = 0xdeadbeef;
-    prop_range.lMax = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_RANGE returned %#x\n", hr );
-    ok( prop_range.lMin == 1000, "got %d expected %d\n", prop_range.lMin, 1000 );
-    ok( prop_range.lMax == 51000, "got %d expected %d\n", prop_range.lMax, 51000 );
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    check_member( state, expect_state_abs[1], "%d", lX );
-    check_member( state, expect_state_abs[1], "%d", lY );
-    check_member( state, expect_state_abs[1], "%d", lZ );
-    check_member( state, expect_state_abs[1], "%d", lRx );
-    check_member( state, expect_state_abs[1], "%d", rgdwPOV[0] );
-    check_member( state, expect_state_abs[1], "%d", rgdwPOV[1] );
-    check_member( state, expect_state_abs[1], "%#x", rgbButtons[0] );
-    check_member( state, expect_state_abs[1], "%#x", rgbButtons[1] );
-    check_member( state, expect_state_abs[1], "%#x", rgbButtons[2] );
-
-    hr = IDirectInputDevice8_SetProperty( device, NULL, NULL );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, &GUID_NULL, NULL );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_VIDPID, NULL );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_VIDPID, &prop_string.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty returned %#x\n", hr );
-
-    prop_dword.diph.dwHow = DIPH_DEVICE;
-    prop_dword.diph.dwObj = 0;
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_VIDPID, &prop_dword.diph );
-    ok( hr == DIERR_READONLY, "SetProperty DIPROP_VIDPID returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
-    ok( hr == DIERR_READONLY, "SetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
-
-    prop_string.diph.dwHow = DIPH_DEVICE;
-    prop_string.diph.dwObj = 0;
-    wcscpy( prop_string.wsz, L"instance name" );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_INSTANCENAME, &prop_string.diph );
-    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_INSTANCENAME returned %#x\n", hr );
-
-    wcscpy( prop_string.wsz, L"product name" );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_PRODUCTNAME, &prop_string.diph );
-    todo_wine
-    ok( hr == DI_OK, "SetProperty DIPROP_PRODUCTNAME returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_PRODUCTNAME, &prop_string.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_PRODUCTNAME returned %#x\n", hr );
-    todo_wine
-    ok( !wcscmp( prop_string.wsz, expect_devinst.tszProductName ), "got product %s\n",
-        debugstr_w(prop_string.wsz) );
-
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_TYPENAME, &prop_string.diph );
-    ok( hr == DIERR_READONLY, "SetProperty DIPROP_TYPENAME returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_USERNAME, &prop_string.diph );
-    ok( hr == DIERR_READONLY, "SetProperty DIPROP_USERNAME returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DIERR_READONLY, "SetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_GRANULARITY, &prop_dword.diph );
-    ok( hr == DIERR_READONLY, "SetProperty DIPROP_GRANULARITY returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_JOYSTICKID, &prop_dword.diph );
-    todo_wine
-    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_JOYSTICKID returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
-    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_AXISMODE returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
-    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
-    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
-    prop_pointer.diph.dwHow = DIPH_BYUSAGE;
-    prop_pointer.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_APPDATA, &prop_pointer.diph );
-    todo_wine
-    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_APPDATA returned %#x\n", hr );
-
-    prop_dword.diph.dwHow = DIPH_DEVICE;
-    prop_dword.diph.dwObj = 0;
-    prop_dword.dwData = 10001;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_SATURATION returned %#x\n", hr );
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
-    prop_dword.dwData = 1000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    prop_dword.dwData = 6000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_SATURATION returned %#x\n", hr );
-    prop_dword.dwData = DIPROPCALIBRATIONMODE_COOKED;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
-
-    prop_dword.diph.dwHow = DIPH_BYUSAGE;
-    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_dword.dwData = 2000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    ok( prop_dword.dwData == 2000, "got %u expected %u\n", prop_dword.dwData, 2000 );
-    prop_dword.dwData = 7000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_SATURATION returned %#x\n", hr );
-
-    prop_dword.diph.dwHow = DIPH_BYUSAGE;
-    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    ok( prop_dword.dwData == 2000, "got %u expected %u\n", prop_dword.dwData, 2000 );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
-    ok( prop_dword.dwData == 7000, "got %u expected %u\n", prop_dword.dwData, 7000 );
-
-    prop_dword.diph.dwHow = DIPH_BYUSAGE;
-    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_Y, HID_USAGE_PAGE_GENERIC );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    ok( prop_dword.dwData == 1000, "got %u expected %u\n", prop_dword.dwData, 1000 );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
-    ok( prop_dword.dwData == 6000, "got %u expected %u\n", prop_dword.dwData, 6000 );
-
-    for (i = 0; i < ARRAY_SIZE(injected_input); ++i)
-    {
-        winetest_push_context( "state[%d]", i );
-        hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-        ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-        if (broken( state.lX == -10750 )) win_skip( "Ignoring 32-bit rounding\n" );
-        else
-        {
-            check_member( state, expect_state_abs[i], "%d", lX );
-            check_member( state, expect_state_abs[i], "%d", lY );
-        }
-        check_member( state, expect_state_abs[i], "%d", lZ );
-        check_member( state, expect_state_abs[i], "%d", lRx );
-        check_member( state, expect_state_abs[i], "%d", rgdwPOV[0] );
-        check_member( state, expect_state_abs[i], "%d", rgdwPOV[1] );
-        check_member( state, expect_state_abs[i], "%#x", rgbButtons[0] );
-        check_member( state, expect_state_abs[i], "%#x", rgbButtons[1] );
-        check_member( state, expect_state_abs[i], "%#x", rgbButtons[2] );
-
-        send_hid_input( file, &injected_input[i], sizeof(*injected_input) );
-
-        res = WaitForSingleObject( event, 100 );
-        if (i == 0 || i == 3) ok( res == WAIT_TIMEOUT, "WaitForSingleObject succeeded\n" );
-        else ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-        ResetEvent( event );
-        winetest_pop_context();
-    }
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    winetest_push_context( "state[%d]", i );
-    check_member( state, expect_state_abs[i], "%d", lX );
-    check_member( state, expect_state_abs[i], "%d", lY );
-    check_member( state, expect_state_abs[i], "%d", lZ );
-    check_member( state, expect_state_abs[i], "%d", lRx );
-    check_member( state, expect_state_abs[i], "%d", rgdwPOV[0] );
-    check_member( state, expect_state_abs[i], "%d", rgdwPOV[1] );
-    check_member( state, expect_state_abs[i], "%#x", rgbButtons[0] );
-    check_member( state, expect_state_abs[i], "%#x", rgbButtons[1] );
-    check_member( state, expect_state_abs[i], "%#x", rgbButtons[2] );
-    winetest_pop_context();
-
-    prop_dword.diph.dwHow = DIPH_BYUSAGE;
-    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_dword.dwData = DIPROPCALIBRATIONMODE_RAW;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
-    ok( prop_dword.dwData == DIPROPCALIBRATIONMODE_RAW, "got %u expected %u\n", prop_dword.dwData, DIPROPCALIBRATIONMODE_RAW );
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    winetest_push_context( "state[%d]", i );
-    todo_wine
-    ok( state.lX == 15, "got lX %d, expected %d\n" , state.lX, 15 );
-    check_member( state, expect_state_abs[0], "%d", lY );
-    check_member( state, expect_state_abs[0], "%d", lZ );
-    check_member( state, expect_state_abs[0], "%d", lRx );
-    check_member( state, expect_state_abs[0], "%d", rgdwPOV[0] );
-    check_member( state, expect_state_abs[0], "%d", rgdwPOV[1] );
-    winetest_pop_context();
-
-    prop_dword.dwData = DIPROPCALIBRATIONMODE_COOKED;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
-
-    send_hid_input( file, &injected_input[ARRAY_SIZE(injected_input) - 1], sizeof(*injected_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    prop_dword.diph.dwHow = DIPH_DEVICE;
-    prop_dword.diph.dwObj = 0;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_JOYSTICKID, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_JOYSTICKID returned %#x\n", hr );
-    prop_dword.dwData = 0x1000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
-    prop_dword.dwData = DIPROPAUTOCENTER_ON;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
-    prop_pointer.diph.dwHow = DIPH_BYUSAGE;
-    prop_pointer.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_pointer.uData = 0xfeedcafe;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_APPDATA, &prop_pointer.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_APPDATA returned %#x\n", hr );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_AXISMODE returned %#x\n", hr );
-    prop_dword.dwData = DIPROPAXISMODE_REL;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_AXISMODE returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
-    todo_wine
-    ok( hr == DI_OK, "GetProperty DIPROP_AXISMODE returned %#x\n", hr );
-    todo_wine
-    ok( prop_dword.dwData == DIPROPAXISMODE_REL, "got %u expected %u\n", prop_dword.dwData, DIPROPAXISMODE_REL );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
-    ok( prop_dword.dwData == 0x1000, "got %#x expected %#x\n", prop_dword.dwData, 0x1000 );
-
-    prop_pointer.diph.dwHow = DIPH_BYUSAGE;
-    prop_pointer.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_APPDATA, &prop_pointer.diph );
-    todo_wine
-    ok( hr == DI_OK, "GetProperty DIPROP_APPDATA returned %#x\n", hr );
-    ok( prop_pointer.uData == 0xfeedcafe, "got %p expected %p\n", (void *)prop_pointer.uData, (void *)0xfeedcafe );
-
-    prop_dword.diph.dwHow = DIPH_DEVICE;
-    prop_dword.diph.dwObj = 0;
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
-    prop_dword.dwData = 1000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATION, &prop_dword.diph );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_CALIBRATION returned %#x\n", hr );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_DEADZONE returned %#x\n", hr );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_SATURATION returned %#x\n", hr );
-
-    for (i = 0; i < ARRAY_SIZE(injected_input); ++i)
-    {
-        winetest_push_context( "state[%d]", i );
-        hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-        ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-        todo_wine
-        check_member( state, expect_state_rel[i], "%d", lX );
-        todo_wine
-        check_member( state, expect_state_rel[i], "%d", lY );
-        todo_wine
-        check_member( state, expect_state_rel[i], "%d", lZ );
-        check_member( state, expect_state_rel[i], "%d", lRx );
-        check_member( state, expect_state_rel[i], "%d", rgdwPOV[0] );
-        check_member( state, expect_state_rel[i], "%d", rgdwPOV[1] );
-        check_member( state, expect_state_rel[i], "%#x", rgbButtons[0] );
-        check_member( state, expect_state_rel[i], "%#x", rgbButtons[1] );
-        check_member( state, expect_state_rel[i], "%#x", rgbButtons[2] );
-
-        send_hid_input( file, &injected_input[i], sizeof(*injected_input) );
-
-        res = WaitForSingleObject( event, 100 );
-        if (i == 3) ok( res == WAIT_TIMEOUT, "WaitForSingleObject succeeded\n" );
-        else ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
-        ResetEvent( event );
-        winetest_pop_context();
-    }
-
-    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
-    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
-    winetest_push_context( "state[%d]", i );
-    todo_wine
-    check_member( state, expect_state_rel[i], "%d", lX );
-    todo_wine
-    check_member( state, expect_state_rel[i], "%d", lY );
-    todo_wine
-    check_member( state, expect_state_rel[i], "%d", lZ );
-    check_member( state, expect_state_rel[i], "%d", lRx );
-    check_member( state, expect_state_rel[i], "%d", rgdwPOV[0] );
-    check_member( state, expect_state_rel[i], "%d", rgdwPOV[1] );
-    check_member( state, expect_state_rel[i], "%#x", rgbButtons[0] );
-    check_member( state, expect_state_rel[i], "%#x", rgbButtons[1] );
-    check_member( state, expect_state_rel[i], "%#x", rgbButtons[2] );
-    winetest_pop_context();
-
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, NULL );
-    ok( hr == E_POINTER, "GetForceFeedbackState returned %#x\n", hr );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DIERR_UNSUPPORTED, "GetForceFeedbackState returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, 0xdeadbeef );
-    ok( hr == DIERR_INVALIDPARAM, "SendForceFeedbackCommand returned %#x\n", hr );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_RESET );
-    ok( hr == DIERR_UNSUPPORTED, "SendForceFeedbackCommand returned %#x\n", hr );
-
-    objdata[0].dwOfs = 0xd;
-    objdata[0].dwData = 0x80;
-    res = 1;
-    hr = IDirectInputDevice8_SendDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0xdeadbeef );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
-    res = 1;
-    hr = IDirectInputDevice8_SendDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 1 /*DISDD_CONTINUE*/ );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
-    res = 1;
-    hr = IDirectInputDevice8_SendDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, NULL, NULL );
-    ok( hr == E_POINTER, "CreateEffect returned %#x\n", hr );
-    hr = IDirectInputDevice8_CreateEffect( device, NULL, NULL, &effect, NULL );
-    ok( hr == DIERR_UNSUPPORTED, "CreateEffect returned %#x\n", hr );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_NULL, NULL, &effect, NULL );
-    ok( hr == DIERR_UNSUPPORTED, "CreateEffect returned %#x\n", hr );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
-    ok( hr == DIERR_UNSUPPORTED, "CreateEffect returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
-    ok( hr == DIERR_UNSUPPORTED, "CreateEffect returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, NULL, effect, 0 );
-    ok( hr == DIERR_INVALIDPARAM, "EnumCreatedEffectObjects returned %#x\n", hr );
-    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_no_created_effect_objects, effect, 0xdeadbeef );
-    ok( hr == DIERR_INVALIDPARAM, "EnumCreatedEffectObjects returned %#x\n", hr );
-    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_no_created_effect_objects, (void *)0xdeadbeef, 0 );
-    ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_Escape( device, NULL );
-    todo_wine
-    ok( hr == E_POINTER, "Escape returned: %#x\n", hr );
-    hr = IDirectInputDevice8_Escape( device, &escape );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "Escape returned: %#x\n", hr );
-    escape.dwSize = sizeof(DIEFFESCAPE) + 1;
-    hr = IDirectInputDevice8_Escape( device, &escape );
-    todo_wine
-    ok( hr == DIERR_INVALIDPARAM, "Escape returned: %#x\n", hr );
-    escape.dwSize = sizeof(DIEFFESCAPE);
-    escape.dwCommand = 0;
-    escape.lpvInBuffer = buffer;
-    escape.cbInBuffer = 10;
-    escape.lpvOutBuffer = buffer + 10;
-    escape.cbOutBuffer = 10;
-    hr = IDirectInputDevice8_Escape( device, &escape );
-    todo_wine
-    ok( hr == DIERR_UNSUPPORTED, "Escape returned: %#x\n", hr );
-
-    ref = IDirectInputDevice8_Release( device );
-    ok( ref == 0, "Release returned %d\n", ref );
-
-    CloseHandle( event );
-    CloseHandle( file );
-
-done:
-    pnp_driver_stop();
-    cleanup_registry_keys();
-    SetCurrentDirectoryW( cwd );
-}
-
-struct device_desc
-{
-    const BYTE *report_desc_buf;
-    ULONG report_desc_len;
-    HIDP_CAPS hid_caps;
-};
-
-static BOOL test_device_types( DWORD version )
-{
-#include "psh_hid_macros.h"
-    static const unsigned char unknown_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,
-    };
-    static const unsigned char limited_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(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,
-    };
-    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,
-    };
-    static const unsigned char joystick_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(1, HID_USAGE_GENERIC_X),
-                USAGE(1, HID_USAGE_GENERIC_Y),
-                USAGE(1, HID_USAGE_GENERIC_Z),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 127),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 127),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 3),
-                INPUT(1, Data|Var|Abs),
-
-                USAGE(1, HID_USAGE_GENERIC_HATSWITCH),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 8),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 8),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                INPUT(1, Data|Var|Abs|Null),
-
-                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
-                USAGE_MINIMUM(1, 1),
-                USAGE_MAXIMUM(1, 5),
-                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 struct device_desc device_desc[] =
-    {
-        {
-            .report_desc_buf = unknown_desc,
-            .report_desc_len = sizeof(unknown_desc),
-            .hid_caps =
-            {
-                .InputReportByteLength = 1,
-            },
-        },
-        {
-            .report_desc_buf = limited_desc,
-            .report_desc_len = sizeof(limited_desc),
-            .hid_caps =
-            {
-                .InputReportByteLength = 3,
-            },
-        },
-        {
-            .report_desc_buf = gamepad_desc,
-            .report_desc_len = sizeof(gamepad_desc),
-            .hid_caps =
-            {
-                .InputReportByteLength = 3,
-            },
-        },
-        {
-            .report_desc_buf = joystick_desc,
-            .report_desc_len = sizeof(joystick_desc),
-            .hid_caps =
-            {
-                .InputReportByteLength = 5,
-            },
-        },
-    };
-    const DIDEVCAPS expect_caps[] =
-    {
-        {
-            .dwSize = sizeof(DIDEVCAPS),
-            .dwFlags = DIDC_ATTACHED|DIDC_EMULATED,
-            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPESUPPLEMENTAL_UNKNOWN << 8)|DI8DEVTYPE_SUPPLEMENTAL
-                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
-            .dwButtons = 6,
-        },
-        {
-            .dwSize = sizeof(DIDEVCAPS),
-            .dwFlags = DIDC_ATTACHED|DIDC_EMULATED,
-            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEJOYSTICK_LIMITED << 8)|DI8DEVTYPE_JOYSTICK
-                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
-            .dwAxes = 2,
-            .dwButtons = 6,
-        },
-        {
-            .dwSize = sizeof(DIDEVCAPS),
-            .dwFlags = DIDC_ATTACHED|DIDC_EMULATED,
-            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEGAMEPAD_STANDARD << 8)|DI8DEVTYPE_GAMEPAD
-                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_GAMEPAD << 8)|DIDEVTYPE_JOYSTICK,
-            .dwAxes = 2,
-            .dwButtons = 6,
-        },
-        {
-            .dwSize = sizeof(DIDEVCAPS),
-            .dwFlags = DIDC_ATTACHED|DIDC_EMULATED,
-            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEJOYSTICK_STANDARD << 8)|DI8DEVTYPE_JOYSTICK
-                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
-            .dwAxes = 3,
-            .dwPOVs = 1,
-            .dwButtons = 5,
-        },
-    };
-
-    const DIDEVICEINSTANCEW expect_devinst[] =
-    {
-        {
-            .dwSize = sizeof(DIDEVICEINSTANCEW),
-            .guidInstance = expect_guid_product,
-            .guidProduct = expect_guid_product,
-            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPESUPPLEMENTAL_UNKNOWN << 8)|DI8DEVTYPE_SUPPLEMENTAL
-                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
-            .tszInstanceName = L"Wine test root driver",
-            .tszProductName = L"Wine test root driver",
-            .guidFFDriver = GUID_NULL,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEINSTANCEW),
-            .guidInstance = expect_guid_product,
-            .guidProduct = expect_guid_product,
-            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEJOYSTICK_LIMITED << 8)|DI8DEVTYPE_JOYSTICK
-                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
-            .tszInstanceName = L"Wine test root driver",
-            .tszProductName = L"Wine test root driver",
-            .guidFFDriver = GUID_NULL,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEINSTANCEW),
-            .guidInstance = expect_guid_product,
-            .guidProduct = expect_guid_product,
-            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEGAMEPAD_STANDARD << 8)|DI8DEVTYPE_GAMEPAD
-                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_GAMEPAD << 8)|DIDEVTYPE_JOYSTICK,
-            .tszInstanceName = L"Wine test root driver",
-            .tszProductName = L"Wine test root driver",
-            .guidFFDriver = GUID_NULL,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_GAMEPAD,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEINSTANCEW),
-            .guidInstance = expect_guid_product,
-            .guidProduct = expect_guid_product,
-            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEJOYSTICK_STANDARD << 8)|DI8DEVTYPE_JOYSTICK
-                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
-            .tszInstanceName = L"Wine test root driver",
-            .tszProductName = L"Wine test root driver",
-            .guidFFDriver = GUID_NULL,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-        },
-    };
-
-    DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
-    DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
-    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
-    IDirectInputDevice8W *device;
-    BOOL success = TRUE;
-    ULONG i, ref;
-    HRESULT hr;
-
-    winetest_push_context( "version %#x", version );
-
-    for (i = 0; i < ARRAY_SIZE(device_desc) && success; ++i)
-    {
-        winetest_push_context( "desc[%d]", i );
-        GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
-        GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
-        SetCurrentDirectoryW( tempdir );
-
-        cleanup_registry_keys();
-        if (!dinput_driver_start( device_desc[i].report_desc_buf, device_desc[i].report_desc_len,
-                                  &device_desc[i].hid_caps, NULL, 0 ))
-        {
-            success = FALSE;
-            goto done;
-        }
-
-        if (FAILED(hr = create_dinput_device( version, &devinst, &device )))
-        {
-            success = FALSE;
-            goto done;
-        }
-
-        hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
-        ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
-        check_member( devinst, expect_devinst[i], "%d", dwSize );
-        todo_wine
-        check_member_guid( devinst, expect_devinst[i], guidInstance );
-        check_member_guid( devinst, expect_devinst[i], guidProduct );
-        todo_wine_if( version <= 0x700 && i == 3 )
-        check_member( devinst, expect_devinst[i], "%#x", dwDevType );
-        todo_wine
-        check_member_wstr( devinst, expect_devinst[i], tszInstanceName );
-        todo_wine
-        check_member_wstr( devinst, expect_devinst[i], tszProductName );
-        check_member_guid( devinst, expect_devinst[i], guidFFDriver );
-        check_member( devinst, expect_devinst[i], "%04x", wUsagePage );
-        check_member( devinst, expect_devinst[i], "%04x", wUsage );
-
-        hr = IDirectInputDevice8_GetCapabilities( device, &caps );
-        ok( hr == DI_OK, "GetCapabilities returned %#x\n", hr );
-        check_member( caps, expect_caps[i], "%d", dwSize );
-        check_member( caps, expect_caps[i], "%#x", dwFlags );
-        todo_wine_if( version <= 0x700 && i == 3 )
-        check_member( caps, expect_caps[i], "%#x", dwDevType );
-        check_member( caps, expect_caps[i], "%d", dwAxes );
-        check_member( caps, expect_caps[i], "%d", dwButtons );
-        check_member( caps, expect_caps[i], "%d", dwPOVs );
-        check_member( caps, expect_caps[i], "%d", dwFFSamplePeriod );
-        check_member( caps, expect_caps[i], "%d", dwFFMinTimeResolution );
-        check_member( caps, expect_caps[i], "%d", dwFirmwareRevision );
-        check_member( caps, expect_caps[i], "%d", dwHardwareRevision );
-        check_member( caps, expect_caps[i], "%d", dwFFDriverVersion );
-
-        ref = IDirectInputDevice8_Release( device );
-        ok( ref == 0, "Release returned %d\n", ref );
-
-    done:
-        pnp_driver_stop();
-        cleanup_registry_keys();
-        SetCurrentDirectoryW( cwd );
-        winetest_pop_context();
-    }
-
-    winetest_pop_context();
-
-    return success;
-}
-
-static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file, DWORD version )
-{
-    struct hid_expect expect_download[] =
-    {
-        /* set periodic */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 5,
-            .report_len = 2,
-            .report_buf = {0x05,0x19},
-        },
-        /* set envelope */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 6,
-            .report_len = 7,
-            .report_buf = {0x06,0x19,0x4c,0x02,0x00,0x04,0x00},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x01,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0xd5},
-        },
-        /* start command when DIEP_START is set */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 2,
-            .report_len = 4,
-            .report_buf = {0x02,0x01,0x01,0x01},
-        },
-    };
-    struct hid_expect expect_download_2[] =
-    {
-        /* set periodic */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 5,
-            .report_len = 2,
-            .report_buf = {0x05,0x19},
-        },
-        /* set envelope */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 6,
-            .report_len = 7,
-            .report_buf = {0x06,0x19,0x4c,0x02,0x00,0x04,0x00},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x02,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0xd5},
-        },
-    };
-    struct hid_expect expect_update[] =
-    {
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x02,0x08,0xff,0xff,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0xd5},
-        },
-    };
-    struct hid_expect expect_set_envelope[] =
-    {
-        /* set envelope */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 6,
-            .report_len = 7,
-            .report_buf = {0x06,0x19,0x4c,0x01,0x00,0x04,0x00},
-        },
-    };
-    struct hid_expect expect_start =
-    {
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {0x02, 0x01, 0x01, 0x01},
-    };
-    struct hid_expect expect_start_solo =
-    {
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {0x02, 0x01, 0x02, 0x01},
-    };
-    struct hid_expect expect_start_0 =
-    {
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {0x02, 0x01, 0x01, 0x00},
-    };
-    struct hid_expect expect_start_4 =
-    {
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {0x02, 0x01, 0x01, 0x04},
-    };
-    struct hid_expect expect_stop =
-    {
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {0x02, 0x01, 0x03, 0x00},
-    };
-    struct hid_expect expect_unload[] =
-    {
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 2,
-            .report_len = 4,
-            .report_buf = {0x02,0x01,0x03,0x00},
-        },
-        /* device reset, when unloaded from Unacquire */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1,0x01},
-        },
-    };
-    struct hid_expect expect_acquire[] =
-    {
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x01},
-        },
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 8,
-            .report_len = 2,
-            .report_buf = {8, 0x19},
-        },
-    };
-    struct hid_expect expect_reset[] =
-    {
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x01},
-        },
-    };
-    static const DWORD expect_axes_init[2] = {0};
-    const DIEFFECT expect_desc_init =
-    {
-        .dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5),
-        .dwTriggerButton = -1,
-        .rgdwAxes = (void *)expect_axes_init,
-    };
-    static const DWORD expect_axes[3] =
-    {
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR,
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR,
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR,
-    };
-    static const LONG expect_directions[3] =
-    {
-        +3000,
-        -6000,
-        0,
-    };
-    static const DIENVELOPE expect_envelope =
-    {
-        .dwSize = sizeof(DIENVELOPE),
-        .dwAttackLevel = 1000,
-        .dwAttackTime = 2000,
-        .dwFadeLevel = 3000,
-        .dwFadeTime = 4000,
-    };
-    static const DIPERIODIC expect_periodic =
-    {
-        .dwMagnitude = 1000,
-        .lOffset = 2000,
-        .dwPhase = 3000,
-        .dwPeriod = 4000,
-    };
-    const DIEFFECT expect_desc =
-    {
-        .dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5),
-        .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS,
-        .dwDuration = 1000,
-        .dwSamplePeriod = 2000,
-        .dwGain = 3000,
-        .dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
-        .dwTriggerRepeatInterval = 5000,
-        .cAxes = 3,
-        .rgdwAxes = (void *)expect_axes,
-        .rglDirection = (void *)expect_directions,
-        .lpEnvelope = (void *)&expect_envelope,
-        .cbTypeSpecificParams = sizeof(DIPERIODIC),
-        .lpvTypeSpecificParams = (void *)&expect_periodic,
-        .dwStartDelay = 6000,
-    };
-    struct check_created_effect_params check_params = {0};
-    IDirectInputEffect *effect;
-    DIPERIODIC periodic = {0};
-    DIENVELOPE envelope = {0};
-    LONG directions[4] = {0};
-    DIEFFECT desc = {0};
-    DWORD axes[4] = {0};
-    ULONG i, ref, flags;
-    HRESULT hr;
-    GUID guid;
-
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, NULL, NULL );
-    ok( hr == E_POINTER, "CreateEffect returned %#x\n", hr );
-    hr = IDirectInputDevice8_CreateEffect( device, NULL, NULL, &effect, NULL );
-    ok( hr == E_POINTER, "CreateEffect returned %#x\n", hr );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_NULL, NULL, &effect, NULL );
-    ok( hr == DIERR_DEVICENOTREG, "CreateEffect returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    if (hr != DI_OK) return;
-
-    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_no_created_effect_objects, effect, 0xdeadbeef );
-    ok( hr == DIERR_INVALIDPARAM, "EnumCreatedEffectObjects returned %#x\n", hr );
-    check_params.expect_effect = effect;
-    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_created_effect_objects, &check_params, 0 );
-    ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#x\n", hr );
-    ok( check_params.count == 1, "got count %u, expected 1\n", check_params.count );
-
-    hr = IDirectInputEffect_Initialize( effect, NULL, version, &GUID_Sine );
-    ok( hr == DIERR_INVALIDPARAM, "Initialize returned %#x\n", hr );
-    hr = IDirectInputEffect_Initialize( effect, instance, 0x800 - (version - 0x700), &GUID_Sine );
-    if (version == 0x800)
-    {
-        todo_wine
-        ok( hr == DIERR_BETADIRECTINPUTVERSION, "Initialize returned %#x\n", hr );
-    }
-    else
-    {
-        todo_wine
-        ok( hr == DIERR_OLDDIRECTINPUTVERSION, "Initialize returned %#x\n", hr );
-    }
-    hr = IDirectInputEffect_Initialize( effect, instance, 0, &GUID_Sine );
-    todo_wine
-    ok( hr == DIERR_NOTINITIALIZED, "Initialize returned %#x\n", hr );
-    hr = IDirectInputEffect_Initialize( effect, instance, version, NULL );
-    ok( hr == E_POINTER, "Initialize returned %#x\n", hr );
-
-    hr = IDirectInputEffect_Initialize( effect, instance, version, &GUID_NULL );
-    ok( hr == DIERR_DEVICENOTREG, "Initialize returned %#x\n", hr );
-    hr = IDirectInputEffect_Initialize( effect, instance, version, &GUID_Sine );
-    ok( hr == DI_OK, "Initialize returned %#x\n", hr );
-    hr = IDirectInputEffect_Initialize( effect, instance, version, &GUID_Square );
-    ok( hr == DI_OK, "Initialize returned %#x\n", hr );
-
-    hr = IDirectInputEffect_GetEffectGuid( effect, NULL );
-    ok( hr == E_POINTER, "GetEffectGuid returned %#x\n", hr );
-    hr = IDirectInputEffect_GetEffectGuid( effect, &guid );
-    ok( hr == DI_OK, "GetEffectGuid returned %#x\n", hr );
-    ok( IsEqualGUID( &guid, &GUID_Square ), "got guid %s, expected %s\n", debugstr_guid( &guid ),
-        debugstr_guid( &GUID_Square ) );
-
-    hr = IDirectInputEffect_GetParameters( effect, NULL, 0 );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_GetParameters( effect, NULL, DIEP_DURATION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, 0 );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    desc.dwSize = sizeof(DIEFFECT_DX5) + 2;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, 0 );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    desc.dwSize = sizeof(DIEFFECT_DX5);
-    hr = IDirectInputEffect_GetParameters( effect, &desc, 0 );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_STARTDELAY );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    desc.dwSize = sizeof(DIEFFECT_DX6);
-    hr = IDirectInputEffect_GetParameters( effect, &desc, 0 );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-
-    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DURATION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-
-    desc.dwDuration = 0xdeadbeef;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DURATION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%u", dwDuration );
-    memset( &desc, 0xcd, sizeof(desc) );
-    desc.dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5);
-    desc.dwFlags = 0;
-    desc.dwStartDelay = 0xdeadbeef;
-    flags = DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_TRIGGERREPEATINTERVAL |
-            (version >= 0x700 ? DIEP_STARTDELAY : 0);
-    hr = IDirectInputEffect_GetParameters( effect, &desc, flags );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%u", dwSamplePeriod );
-    check_member( desc, expect_desc_init, "%u", dwGain );
-    if (version >= 0x700) check_member( desc, expect_desc_init, "%u", dwStartDelay );
-    else ok( desc.dwStartDelay == 0xdeadbeef, "got dwStartDelay %#x\n", desc.dwStartDelay );
-    check_member( desc, expect_desc_init, "%u", dwTriggerRepeatInterval );
-
-    memset( &desc, 0xcd, sizeof(desc) );
-    desc.dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5);
-    desc.dwFlags = 0;
-    desc.lpEnvelope = NULL;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ENVELOPE );
-    ok( hr == E_POINTER, "GetParameters returned %#x\n", hr );
-    desc.lpEnvelope = &envelope;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ENVELOPE );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    envelope.dwSize = sizeof(DIENVELOPE);
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ENVELOPE );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-
-    desc.dwFlags = 0;
-    desc.cAxes = 0;
-    desc.rgdwAxes = NULL;
-    desc.rglDirection = NULL;
-    desc.lpEnvelope = NULL;
-    desc.cbTypeSpecificParams = 0;
-    desc.lpvTypeSpecificParams = NULL;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5 );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TRIGGERBUTTON );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    desc.dwFlags = DIEFF_OBJECTOFFSETS;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TRIGGERBUTTON );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%u", cAxes );
-    desc.dwFlags = DIEFF_OBJECTIDS;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TRIGGERBUTTON );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%u", cAxes );
-    desc.dwFlags |= DIEFF_CARTESIAN;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    ok( desc.dwFlags == DIEFF_OBJECTIDS, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_OBJECTIDS );
-    desc.dwFlags |= DIEFF_POLAR;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-    ok( desc.dwFlags == DIEFF_OBJECTIDS, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_OBJECTIDS );
-    desc.dwFlags |= DIEFF_SPHERICAL;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%u", cAxes );
-    ok( desc.dwFlags == DIEFF_OBJECTIDS, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_OBJECTIDS );
-
-    desc.dwFlags |= DIEFF_SPHERICAL;
-    desc.cAxes = 2;
-    desc.rgdwAxes = axes;
-    desc.rglDirection = directions;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES | DIEP_DIRECTION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%u", cAxes );
-    check_member( desc, expect_desc_init, "%u", rgdwAxes[0] );
-    check_member( desc, expect_desc_init, "%u", rgdwAxes[1] );
-    check_member( desc, expect_desc_init, "%p", rglDirection );
-    ok( desc.dwFlags == DIEFF_OBJECTIDS, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_OBJECTIDS );
-
-    desc.dwFlags |= DIEFF_SPHERICAL;
-    desc.lpEnvelope = &envelope;
-    desc.cbTypeSpecificParams = sizeof(periodic);
-    desc.lpvTypeSpecificParams = &periodic;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5 );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc_init, "%u", dwDuration );
-    check_member( desc, expect_desc_init, "%u", dwSamplePeriod );
-    check_member( desc, expect_desc_init, "%u", dwGain );
-    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
-    check_member( desc, expect_desc_init, "%u", dwTriggerRepeatInterval );
-    check_member( desc, expect_desc_init, "%u", cAxes );
-    check_member( desc, expect_desc_init, "%u", rgdwAxes[0] );
-    check_member( desc, expect_desc_init, "%u", rgdwAxes[1] );
-    check_member( desc, expect_desc_init, "%p", rglDirection );
-    check_member( desc, expect_desc_init, "%p", lpEnvelope );
-    check_member( desc, expect_desc_init, "%u", cbTypeSpecificParams );
-    if (version >= 0x700) check_member( desc, expect_desc_init, "%u", dwStartDelay );
-    else ok( desc.dwStartDelay == 0xcdcdcdcd, "got dwStartDelay %#x\n", desc.dwStartDelay );
-
-    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Download returned %#x\n", hr );
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
-
-    hr = IDirectInputEffect_SetParameters( effect, NULL, DIEP_NODOWNLOAD );
-    ok( hr == E_POINTER, "SetParameters returned %#x\n", hr );
-    memset( &desc, 0, sizeof(desc) );
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-    desc.dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5);
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-
-    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_DURATION );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_DURATION | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-
-    desc.dwTriggerButton = -1;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DURATION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc, "%u", dwDuration );
-    check_member( desc, expect_desc_init, "%u", dwSamplePeriod );
-    check_member( desc, expect_desc_init, "%u", dwGain );
-    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
-    check_member( desc, expect_desc_init, "%u", dwTriggerRepeatInterval );
-    check_member( desc, expect_desc_init, "%u", cAxes );
-    check_member( desc, expect_desc_init, "%p", rglDirection );
-    check_member( desc, expect_desc_init, "%p", lpEnvelope );
-    check_member( desc, expect_desc_init, "%u", cbTypeSpecificParams );
-    check_member( desc, expect_desc_init, "%u", dwStartDelay );
-
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
-
-    flags = DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_TRIGGERREPEATINTERVAL | DIEP_NODOWNLOAD;
-    if (version >= 0x700) flags |= DIEP_STARTDELAY;
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, flags );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    desc.dwDuration = 0;
-    flags = DIEP_DURATION | DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_TRIGGERREPEATINTERVAL;
-    if (version >= 0x700) flags |= DIEP_STARTDELAY;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, flags );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc, "%u", dwDuration );
-    check_member( desc, expect_desc, "%u", dwSamplePeriod );
-    check_member( desc, expect_desc, "%u", dwGain );
-    check_member( desc, expect_desc_init, "%#x", dwTriggerButton );
-    check_member( desc, expect_desc, "%u", dwTriggerRepeatInterval );
-    check_member( desc, expect_desc_init, "%u", cAxes );
-    check_member( desc, expect_desc_init, "%p", rglDirection );
-    check_member( desc, expect_desc_init, "%p", lpEnvelope );
-    check_member( desc, expect_desc_init, "%u", cbTypeSpecificParams );
-    if (version >= 0x700) check_member( desc, expect_desc, "%u", dwStartDelay );
-    else ok( desc.dwStartDelay == 0, "got dwStartDelay %#x\n", desc.dwStartDelay );
-
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
-
-    desc.lpEnvelope = NULL;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_ENVELOPE | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    desc.lpEnvelope = &envelope;
-    envelope.dwSize = 0;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_ENVELOPE | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_ENVELOPE | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-
-    desc.lpEnvelope = &envelope;
-    envelope.dwSize = sizeof(DIENVELOPE);
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ENVELOPE );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( envelope, expect_envelope, "%u", dwAttackLevel );
-    check_member( envelope, expect_envelope, "%u", dwAttackTime );
-    check_member( envelope, expect_envelope, "%u", dwFadeLevel );
-    check_member( envelope, expect_envelope, "%u", dwFadeTime );
-
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
-
-    desc.dwFlags = 0;
-    desc.cAxes = 0;
-    desc.rgdwAxes = NULL;
-    desc.rglDirection = NULL;
-    desc.lpEnvelope = NULL;
-    desc.cbTypeSpecificParams = 0;
-    desc.lpvTypeSpecificParams = NULL;
-    flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, flags | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TRIGGERBUTTON | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_AXES | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-
-    desc.dwFlags = DIEFF_OBJECTOFFSETS;
-    desc.cAxes = 1;
-    desc.rgdwAxes = axes;
-    desc.rgdwAxes[0] = DIJOFS_X;
-    desc.dwTriggerButton = DIJOFS_BUTTON( 1 );
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_AXES | DIEP_TRIGGERBUTTON | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_AXES | DIEP_TRIGGERBUTTON | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_ALREADYINITIALIZED, "SetParameters returned %#x\n", hr );
-
-    desc.cAxes = 0;
-    desc.dwFlags = DIEFF_OBJECTIDS;
-    desc.rgdwAxes = axes;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES | DIEP_TRIGGERBUTTON );
-    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc, "%u", cAxes );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES | DIEP_TRIGGERBUTTON );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc, "%#x", dwTriggerButton );
-    check_member( desc, expect_desc, "%u", cAxes );
-    check_member( desc, expect_desc, "%u", rgdwAxes[0] );
-    check_member( desc, expect_desc, "%u", rgdwAxes[1] );
-    check_member( desc, expect_desc, "%u", rgdwAxes[2] );
-
-    desc.dwFlags = DIEFF_OBJECTOFFSETS;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_AXES | DIEP_TRIGGERBUTTON );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    ok( desc.dwTriggerButton == 0x30, "got %#x expected %#x\n", desc.dwTriggerButton, 0x30 );
-    ok( desc.rgdwAxes[0] == 8, "got %#x expected %#x\n", desc.rgdwAxes[0], 8 );
-    ok( desc.rgdwAxes[1] == 0, "got %#x expected %#x\n", desc.rgdwAxes[1], 0 );
-    ok( desc.rgdwAxes[2] == 4, "got %#x expected %#x\n", desc.rgdwAxes[2], 4 );
-
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
-
-    desc.dwFlags = DIEFF_CARTESIAN;
-    desc.cAxes = 0;
-    desc.rglDirection = directions;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-    desc.cAxes = 3;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    desc.dwFlags = DIEFF_POLAR;
-    desc.cAxes = 3;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-
-    desc.dwFlags = DIEFF_SPHERICAL;
-    desc.cAxes = 1;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
-    ok( desc.dwFlags == DIEFF_SPHERICAL, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_SPHERICAL );
-    check_member( desc, expect_desc, "%u", cAxes );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc, "%u", cAxes );
-    ok( desc.rglDirection[0] == 3000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 3000 );
-    ok( desc.rglDirection[1] == 30000, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 30000 );
-    ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], 0 );
-    desc.dwFlags = DIEFF_CARTESIAN;
-    desc.cAxes = 2;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
-    ok( desc.dwFlags == DIEFF_CARTESIAN, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_CARTESIAN );
-    check_member( desc, expect_desc, "%u", cAxes );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc, "%u", cAxes );
-    ok( desc.rglDirection[0] == 4330, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 4330 );
-    ok( desc.rglDirection[1] == 2500, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 2500 );
-    ok( desc.rglDirection[2] == -8660, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], -8660 );
-    desc.dwFlags = DIEFF_POLAR;
-    desc.cAxes = 3;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
-
-    desc.cbTypeSpecificParams = 0;
-    desc.lpvTypeSpecificParams = &periodic;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
-    ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-    desc.cbTypeSpecificParams = sizeof(DIPERIODIC);
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( periodic, expect_periodic, "%u", dwMagnitude );
-    check_member( periodic, expect_periodic, "%d", lOffset );
-    check_member( periodic, expect_periodic, "%u", dwPhase );
-    check_member( periodic, expect_periodic, "%u", dwPeriod );
-
-    hr = IDirectInputEffect_Start( effect, 1, DIES_NODOWNLOAD );
-    ok( hr == DIERR_NOTDOWNLOADED, "Start returned %#x\n", hr );
-    hr = IDirectInputEffect_Stop( effect );
-    ok( hr == DIERR_NOTDOWNLOADED, "Stop returned %#x\n", hr );
-
-    set_hid_expect( file, expect_download, 3 * sizeof(struct hid_expect) );
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DI_OK, "Download returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DI_NOEFFECT, "Download returned %#x\n", hr );
-
-    hr = IDirectInputEffect_Start( effect, 1, 0xdeadbeef );
-    ok( hr == DIERR_INVALIDPARAM, "Start returned %#x\n", hr );
-
-    set_hid_expect( file, &expect_start_solo, sizeof(expect_start_solo) );
-    hr = IDirectInputEffect_Start( effect, 1, DIES_SOLO );
-    ok( hr == DI_OK, "Start returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_stop, sizeof(expect_stop) );
-    hr = IDirectInputEffect_Stop( effect );
-    ok( hr == DI_OK, "Stop returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_start, sizeof(expect_start) );
-    hr = IDirectInputEffect_Start( effect, 1, 0 );
-    ok( hr == DI_OK, "Start returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_start_4, sizeof(expect_start_4) );
-    hr = IDirectInputEffect_Start( effect, 4, 0 );
-    ok( hr == DI_OK, "Start returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_start_0, sizeof(expect_start_4) );
-    hr = IDirectInputEffect_Start( effect, 0, 0 );
-    ok( hr == DI_OK, "Start returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_unload, sizeof(struct hid_expect) );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_OK, "Unload returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_download, 4 * sizeof(struct hid_expect) );
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_START );
-    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_unload, sizeof(struct hid_expect) );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_OK, "Unload returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_download, 3 * sizeof(struct hid_expect) );
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DI_OK, "Download returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_unload, 2 * sizeof(struct hid_expect) );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    hr = IDirectInputEffect_Start( effect, 1, DIES_NODOWNLOAD );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Start returned %#x\n", hr );
-    hr = IDirectInputEffect_Stop( effect );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Stop returned %#x\n", hr );
-
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
-
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-
-    desc.dwFlags = DIEFF_POLAR | DIEFF_OBJECTIDS;
-    desc.cAxes = 2;
-    desc.rgdwAxes[0] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR;
-    desc.rgdwAxes[1] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR;
-    desc.rglDirection[0] = 3000;
-    desc.rglDirection[1] = 0;
-    desc.rglDirection[2] = 0;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_AXES | DIEP_DIRECTION | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    desc.rglDirection[0] = 0;
-
-    desc.dwFlags = DIEFF_SPHERICAL;
-    desc.cAxes = 1;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
-    ok( desc.dwFlags == DIEFF_SPHERICAL, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_SPHERICAL );
-    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
-    ok( desc.rglDirection[0] == 30000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 30000 );
-    ok( desc.rglDirection[1] == 0, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 0 );
-    ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], 0 );
-
-    desc.dwFlags = DIEFF_CARTESIAN;
-    desc.cAxes = 1;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
-    ok( desc.dwFlags == DIEFF_CARTESIAN, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_CARTESIAN );
-    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
-    ok( desc.rglDirection[0] == 5000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 5000 );
-    ok( desc.rglDirection[1] == -8660, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], -8660 );
-    ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], 0 );
-
-    desc.dwFlags = DIEFF_POLAR;
-    desc.cAxes = 1;
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
-    ok( desc.dwFlags == DIEFF_POLAR, "got flags %#x, expected %#x\n", desc.dwFlags, DIEFF_POLAR );
-    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    ok( desc.cAxes == 2, "got cAxes %u expected 2\n", desc.cAxes );
-    ok( desc.rglDirection[0] == 3000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 3000 );
-    ok( desc.rglDirection[1] == 0, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 0 );
-    ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n", desc.rglDirection[2], 0 );
-
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-
-    for (i = 1; i < 4; i++)
-    {
-        struct hid_expect expect_directions[] =
-        {
-            /* set periodic */
-            {
-                .code = IOCTL_HID_WRITE_REPORT,
-                .report_id = 5,
-                .report_len = 2,
-                .report_buf = {0x05,0x19},
-            },
-            /* set envelope */
-            {
-                .code = IOCTL_HID_WRITE_REPORT,
-                .report_id = 6,
-                .report_len = 7,
-                .report_buf = {0x06,0x19,0x4c,0x02,0x00,0x04,0x00},
-            },
-            /* update effect */
-            {0},
-            /* effect control */
-            {
-                .code = IOCTL_HID_WRITE_REPORT,
-                .report_id = 2,
-                .report_len = 4,
-                .report_buf = {0x02,0x01,0x03,0x00},
-            },
-        };
-        struct hid_expect expect_spherical =
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x02,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,i >= 2 ? 0x55 : 0,i >= 3 ? 0x1c : 0},
-        };
-        struct hid_expect expect_cartesian =
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x02,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,i >= 2 ? 0x63 : 0,i >= 3 ? 0x1d : 0},
-        };
-        struct hid_expect expect_polar =
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x02,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,i >= 2 ? 0x3f : 0,i >= 3 ? 0x00 : 0},
-        };
-
-        winetest_push_context( "%u axes", i );
-        hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
-        ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-
-        desc.dwFlags = DIEFF_OBJECTIDS;
-        desc.cAxes = i;
-        desc.rgdwAxes[0] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR;
-        desc.rgdwAxes[1] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR;
-        desc.rgdwAxes[2] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR;
-        desc.rglDirection[0] = 0;
-        desc.rglDirection[1] = 0;
-        desc.rglDirection[2] = 0;
-        hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_AXES | DIEP_NODOWNLOAD );
-        ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-
-        desc.dwFlags = DIEFF_CARTESIAN;
-        desc.cAxes = i == 3 ? 2 : 3;
-        desc.rglDirection[0] = 1000;
-        desc.rglDirection[1] = 2000;
-        desc.rglDirection[2] = 3000;
-        hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
-        ok( hr == DIERR_INVALIDPARAM, "SetParameters returned %#x\n", hr );
-        desc.cAxes = i;
-        hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_DIRECTION | DIEP_NODOWNLOAD );
-        ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-
-        desc.dwFlags = DIEFF_SPHERICAL;
-        desc.cAxes = i;
-        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-        ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-        desc.cAxes = 3;
-        memset( desc.rglDirection, 0xcd, 3 * sizeof(LONG) );
-        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-        ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-        ok( desc.cAxes == i, "got cAxes %u expected 2\n", desc.cAxes );
-        if (i == 1)
-        {
-            ok( desc.rglDirection[0] == 0, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 0 );
-            ok( desc.rglDirection[1] == 0xcdcdcdcd, "got rglDirection[1] %d expected %d\n",
-                desc.rglDirection[1], 0xcdcdcdcd );
-            ok( desc.rglDirection[2] == 0xcdcdcdcd, "got rglDirection[2] %d expected %d\n",
-                desc.rglDirection[2], 0xcdcdcdcd );
-        }
-        else
-        {
-            ok( desc.rglDirection[0] == 6343, "got rglDirection[0] %d expected %d\n",
-                desc.rglDirection[0], 6343 );
-            if (i == 2)
-            {
-                ok( desc.rglDirection[1] == 0, "got rglDirection[1] %d expected %d\n",
-                    desc.rglDirection[1], 0 );
-                ok( desc.rglDirection[2] == 0xcdcdcdcd, "got rglDirection[2] %d expected %d\n",
-                    desc.rglDirection[2], 0xcdcdcdcd );
-            }
-            else
-            {
-                ok( desc.rglDirection[1] == 5330, "got rglDirection[1] %d expected %d\n",
-                    desc.rglDirection[1], 5330 );
-                ok( desc.rglDirection[2] == 0, "got rglDirection[2] %d expected %d\n",
-                    desc.rglDirection[2], 0 );
-            }
-        }
-
-        desc.dwFlags = DIEFF_CARTESIAN;
-        desc.cAxes = i;
-        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-        ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-        desc.cAxes = 3;
-        memset( desc.rglDirection, 0xcd, 3 * sizeof(LONG) );
-        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-        ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-        ok( desc.cAxes == i, "got cAxes %u expected 2\n", desc.cAxes );
-        ok( desc.rglDirection[0] == 1000, "got rglDirection[0] %d expected %d\n", desc.rglDirection[0], 1000 );
-        if (i == 1)
-            ok( desc.rglDirection[1] == 0xcdcdcdcd, "got rglDirection[1] %d expected %d\n",
-                desc.rglDirection[1], 0xcdcdcdcd );
-        else
-            ok( desc.rglDirection[1] == 2000, "got rglDirection[1] %d expected %d\n",
-                desc.rglDirection[1], 2000 );
-        if (i <= 2)
-            ok( desc.rglDirection[2] == 0xcdcdcdcd, "got rglDirection[2] %d expected %d\n",
-                desc.rglDirection[2], 0xcdcdcdcd );
-        else
-            ok( desc.rglDirection[2] == 3000, "got rglDirection[2] %d expected %d\n",
-                desc.rglDirection[2], 3000 );
-
-        desc.dwFlags = DIEFF_POLAR;
-        desc.cAxes = 1;
-        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-        if (i != 2) ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-        else ok( hr == DIERR_MOREDATA, "GetParameters returned %#x\n", hr );
-        desc.cAxes = 3;
-        memset( desc.rglDirection, 0xcd, 3 * sizeof(LONG) );
-        hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_DIRECTION );
-        if (i != 2) ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
-        else
-        {
-            ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-            ok( desc.cAxes == i, "got cAxes %u expected 2\n", desc.cAxes );
-            ok( desc.rglDirection[0] == 15343, "got rglDirection[0] %d expected %d\n",
-                desc.rglDirection[0], 15343 );
-            ok( desc.rglDirection[1] == 0, "got rglDirection[1] %d expected %d\n", desc.rglDirection[1], 0 );
-            ok( desc.rglDirection[2] == 0xcdcdcdcd, "got rglDirection[2] %d expected %d\n",
-                desc.rglDirection[2], 0xcdcdcdcd );
-        }
-
-        ref = IDirectInputEffect_Release( effect );
-        ok( ref == 0, "Release returned %d\n", ref );
-
-        desc = expect_desc;
-        desc.dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS;
-        desc.cAxes = i;
-        desc.rgdwAxes = axes;
-        desc.rglDirection = directions;
-        desc.rglDirection[0] = 3000;
-        desc.rglDirection[1] = 4000;
-        desc.rglDirection[2] = 5000;
-        flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
-        expect_directions[2] = expect_spherical;
-        set_hid_expect( file, expect_directions, sizeof(expect_directions) );
-        hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, &desc, &effect, NULL );
-        ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-        ref = IDirectInputEffect_Release( effect );
-        ok( ref == 0, "Release returned %d\n", ref );
-        set_hid_expect( file, NULL, 0 );
-
-        desc = expect_desc;
-        desc.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTIDS;
-        desc.cAxes = i;
-        desc.rgdwAxes = axes;
-        desc.rglDirection = directions;
-        desc.rglDirection[0] = 6000;
-        desc.rglDirection[1] = 7000;
-        desc.rglDirection[2] = 8000;
-        flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
-        expect_directions[2] = expect_cartesian;
-        set_hid_expect( file, expect_directions, sizeof(expect_directions) );
-        hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, &desc, &effect, NULL );
-        ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-        ref = IDirectInputEffect_Release( effect );
-        ok( ref == 0, "Release returned %d\n", ref );
-        set_hid_expect( file, NULL, 0 );
-
-        if (i == 2)
-        {
-            desc = expect_desc;
-            desc.dwFlags = DIEFF_POLAR | DIEFF_OBJECTIDS;
-            desc.cAxes = i;
-            desc.rgdwAxes = axes;
-            desc.rglDirection = directions;
-            desc.rglDirection[0] = 9000;
-            desc.rglDirection[1] = 10000;
-            desc.rglDirection[2] = 11000;
-            flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
-            expect_directions[2] = expect_polar;
-            set_hid_expect( file, expect_directions, sizeof(expect_directions) );
-            hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, &desc, &effect, NULL );
-            ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-            ref = IDirectInputEffect_Release( effect );
-            ok( ref == 0, "Release returned %d\n", ref );
-            set_hid_expect( file, NULL, 0 );
-        }
-
-        winetest_pop_context();
-    }
-
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-
-    set_hid_expect( file, expect_download_2, sizeof(expect_download_2) );
-    flags = version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, flags );
-    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    desc = expect_desc;
-    desc.dwDuration = INFINITE;
-    desc.dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD|DIEP_DURATION|DIEP_TRIGGERBUTTON );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    set_hid_expect( file, expect_update, sizeof(expect_update) );
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, 0 );
-    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* these updates are sent asynchronously */
-    desc = expect_desc;
-    desc.dwDuration = INFINITE;
-    desc.dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD|DIEP_DURATION|DIEP_TRIGGERBUTTON );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    set_hid_expect( file, expect_update, sizeof(expect_update) );
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, 0 );
-    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* these updates are sent asynchronously */
-
-    desc = expect_desc;
-    desc.lpEnvelope = &envelope;
-    desc.lpEnvelope->dwAttackTime = 1000;
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_NODOWNLOAD|DIEP_ENVELOPE );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    set_hid_expect( file, expect_set_envelope, sizeof(expect_set_envelope) );
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, 0 );
-    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* these updates are sent asynchronously */
-
-    set_hid_expect( file, &expect_stop, sizeof(expect_stop) );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, &expect_desc, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-}
-
-static void test_condition_effect( IDirectInputDevice8W *device, HANDLE file, DWORD version )
-{
-    struct hid_expect expect_create[] =
-    {
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 7,
-            .report_len = 8,
-            .report_buf = {0x07,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 7,
-            .report_len = 8,
-            .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0x00},
-        },
-    };
-    struct hid_expect expect_create_1[] =
-    {
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 7,
-            .report_len = 8,
-            .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x3f,0x00},
-        },
-    };
-    struct hid_expect expect_create_2[] =
-    {
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 7,
-            .report_len = 8,
-            .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0xf1},
-        },
-    };
-    struct hid_expect expect_create_3[] =
-    {
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 7,
-            .report_len = 8,
-            .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x55,0x00},
-        },
-    };
-    struct hid_expect expect_destroy =
-    {
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {0x02, 0x01, 0x03, 0x00},
-    };
-    static const DWORD expect_axes[3] =
-    {
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR,
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR,
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR,
-    };
-    static const LONG expect_directions[3] = {
-        +3000,
-        0,
-        0,
-    };
-    static const DIENVELOPE expect_envelope =
-    {
-        .dwSize = sizeof(DIENVELOPE),
-        .dwAttackLevel = 1000,
-        .dwAttackTime = 2000,
-        .dwFadeLevel = 3000,
-        .dwFadeTime = 4000,
-    };
-    static const DICONDITION expect_condition[3] =
-    {
-        {
-            .lOffset = -500,
-            .lPositiveCoefficient = 2000,
-            .lNegativeCoefficient = -3000,
-            .dwPositiveSaturation = -4000,
-            .dwNegativeSaturation = -5000,
-            .lDeadBand = 6000,
-        },
-        {
-            .lOffset = 6000,
-            .lPositiveCoefficient = 5000,
-            .lNegativeCoefficient = -4000,
-            .dwPositiveSaturation = 3000,
-            .dwNegativeSaturation = 2000,
-            .lDeadBand = 1000,
-        },
-        {
-            .lOffset = -7000,
-            .lPositiveCoefficient = -8000,
-            .lNegativeCoefficient = 9000,
-            .dwPositiveSaturation = 10000,
-            .dwNegativeSaturation = 11000,
-            .lDeadBand = -12000,
-        },
-    };
-    const DIEFFECT expect_desc =
-    {
-        .dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5),
-        .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS,
-        .dwDuration = 1000,
-        .dwSamplePeriod = 2000,
-        .dwGain = 3000,
-        .dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
-        .dwTriggerRepeatInterval = 5000,
-        .cAxes = 2,
-        .rgdwAxes = (void *)expect_axes,
-        .rglDirection = (void *)expect_directions,
-        .lpEnvelope = (void *)&expect_envelope,
-        .cbTypeSpecificParams = 2 * sizeof(DICONDITION),
-        .lpvTypeSpecificParams = (void *)expect_condition,
-        .dwStartDelay = 6000,
-    };
-    struct check_created_effect_params check_params = {0};
-    DIENVELOPE envelope =
-    {.dwSize = sizeof(DIENVELOPE)};
-    DICONDITION condition[2] = {0};
-    IDirectInputEffect *effect;
-    LONG directions[4] = {0};
-    DWORD axes[4] = {0};
-    DIEFFECT desc =
-    {
-        .dwSize = version >= 0x700 ? sizeof(DIEFFECT_DX6) : sizeof(DIEFFECT_DX5),
-        .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS,
-        .cAxes = 4,
-        .rgdwAxes = axes,
-        .rglDirection = directions,
-        .lpEnvelope = &envelope,
-        .cbTypeSpecificParams = 2 * sizeof(DICONDITION),
-        .lpvTypeSpecificParams = condition,
-    };
-    HRESULT hr;
-    ULONG ref;
-    GUID guid;
-
-    set_hid_expect( file, expect_create, sizeof(expect_create) );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &expect_desc, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    check_params.expect_effect = effect;
-    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_created_effect_objects, &check_params, 0 );
-    ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#x\n", hr );
-    ok( check_params.count == 1, "got count %u, expected 1\n", check_params.count );
-
-    hr = IDirectInputEffect_GetEffectGuid( effect, &guid );
-    ok( hr == DI_OK, "GetEffectGuid returned %#x\n", hr );
-    ok( IsEqualGUID( &guid, &GUID_Spring ), "got guid %s, expected %s\n", debugstr_guid( &guid ),
-        debugstr_guid( &GUID_Spring ) );
-
-    hr = IDirectInputEffect_GetParameters( effect, &desc, version >= 0x700 ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5 );
-    ok( hr == DI_OK, "GetParameters returned %#x\n", hr );
-    check_member( desc, expect_desc, "%u", dwDuration );
-    check_member( desc, expect_desc, "%u", dwSamplePeriod );
-    check_member( desc, expect_desc, "%u", dwGain );
-    check_member( desc, expect_desc, "%#x", dwTriggerButton );
-    check_member( desc, expect_desc, "%u", dwTriggerRepeatInterval );
-    check_member( desc, expect_desc, "%u", cAxes );
-    check_member( desc, expect_desc, "%#x", rgdwAxes[0] );
-    check_member( desc, expect_desc, "%#x", rgdwAxes[1] );
-    check_member( desc, expect_desc, "%d", rglDirection[0] );
-    check_member( desc, expect_desc, "%d", rglDirection[1] );
-    check_member( desc, expect_desc, "%u", cbTypeSpecificParams );
-    if (version >= 0x700) check_member( desc, expect_desc, "%u", dwStartDelay );
-    else ok( desc.dwStartDelay == 0, "got dwStartDelay %#x\n", desc.dwStartDelay );
-    check_member( envelope, expect_envelope, "%u", dwAttackLevel );
-    check_member( envelope, expect_envelope, "%u", dwAttackTime );
-    check_member( envelope, expect_envelope, "%u", dwFadeLevel );
-    check_member( envelope, expect_envelope, "%u", dwFadeTime );
-    check_member( condition[0], expect_condition[0], "%d", lOffset );
-    check_member( condition[0], expect_condition[0], "%d", lPositiveCoefficient );
-    check_member( condition[0], expect_condition[0], "%d", lNegativeCoefficient );
-    check_member( condition[0], expect_condition[0], "%u", dwPositiveSaturation );
-    check_member( condition[0], expect_condition[0], "%u", dwNegativeSaturation );
-    check_member( condition[0], expect_condition[0], "%d", lDeadBand );
-    check_member( condition[1], expect_condition[1], "%d", lOffset );
-    check_member( condition[1], expect_condition[1], "%d", lPositiveCoefficient );
-    check_member( condition[1], expect_condition[1], "%d", lNegativeCoefficient );
-    check_member( condition[1], expect_condition[1], "%u", dwPositiveSaturation );
-    check_member( condition[1], expect_condition[1], "%u", dwNegativeSaturation );
-    check_member( condition[1], expect_condition[1], "%d", lDeadBand );
-
-    set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, NULL, 0 );
-
-    desc = expect_desc;
-    desc.cAxes = 1;
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
-    ok( hr == DIERR_INVALIDPARAM, "CreateEffect returned %#x\n", hr );
-    desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION);
-    desc.lpvTypeSpecificParams = (void *)&expect_condition[1];
-    set_hid_expect( file, expect_create_1, sizeof(expect_create_1) );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, NULL, 0 );
-
-    desc = expect_desc;
-    desc.cAxes = 3;
-    desc.rglDirection = directions;
-    desc.rglDirection[0] = +3000;
-    desc.rglDirection[1] = -2000;
-    desc.rglDirection[2] = +1000;
-    desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION);
-    desc.lpvTypeSpecificParams = (void *)&expect_condition[1];
-    set_hid_expect( file, expect_create_2, sizeof(expect_create_2) );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, NULL, 0 );
-
-    desc = expect_desc;
-    desc.cAxes = 2;
-    desc.rgdwAxes = axes;
-    desc.rgdwAxes[0] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR;
-    desc.rgdwAxes[1] = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR;
-    desc.rglDirection = directions;
-    desc.rglDirection[0] = +3000;
-    desc.rglDirection[1] = -2000;
-    desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION);
-    desc.lpvTypeSpecificParams = (void *)&expect_condition[1];
-    set_hid_expect( file, expect_create_3, sizeof(expect_create_3) );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, NULL, 0 );
-
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, NULL, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    desc = expect_desc;
-    desc.cAxes = 0;
-    desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION);
-    desc.lpvTypeSpecificParams = (void *)&expect_condition[0];
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    desc.cbTypeSpecificParams = 0 * sizeof(DICONDITION);
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS );
-    ok( hr == DIERR_MOREDATA, "SetParameters returned %#x\n", hr );
-    ok( desc.cbTypeSpecificParams == 1 * sizeof(DICONDITION), "got %u\n", desc.cbTypeSpecificParams );
-    desc.cbTypeSpecificParams = 0 * sizeof(DICONDITION);
-    hr = IDirectInputEffect_SetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-    desc.cbTypeSpecificParams = 0 * sizeof(DICONDITION);
-    hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_TYPESPECIFICPARAMS );
-    ok( hr == DI_OK, "SetParameters returned %#x\n", hr );
-    ok( desc.cbTypeSpecificParams == 0 * sizeof(DICONDITION), "got %u\n", desc.cbTypeSpecificParams );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-}
-
-static void test_force_feedback_joystick( DWORD version )
-{
-#include "psh_hid_macros.h"
-    const unsigned char report_descriptor[] = {
-        USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
-        USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
-        COLLECTION(1, Application),
-            USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 1),
-
-                USAGE(1, HID_USAGE_GENERIC_X),
-                USAGE(1, HID_USAGE_GENERIC_Y),
-                USAGE(1, HID_USAGE_GENERIC_Z),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 3),
-                INPUT(1, Data|Var|Abs),
-
-                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
-                USAGE_MINIMUM(1, 1),
-                USAGE_MAXIMUM(1, 2),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 1),
-                REPORT_COUNT(1, 2),
-                INPUT(1, Data|Var|Abs),
-                REPORT_COUNT(1, 6),
-                INPUT(1, Cnst|Var|Abs),
-            END_COLLECTION,
-
-            USAGE_PAGE(1, HID_USAGE_PAGE_PID),
-            USAGE(1, PID_USAGE_STATE_REPORT),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 2),
-
-                USAGE(1, PID_USAGE_DEVICE_PAUSED),
-                USAGE(1, PID_USAGE_ACTUATORS_ENABLED),
-                USAGE(1, PID_USAGE_SAFETY_SWITCH),
-                USAGE(1, PID_USAGE_ACTUATOR_OVERRIDE_SWITCH),
-                USAGE(1, PID_USAGE_ACTUATOR_POWER),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 1),
-                REPORT_COUNT(1, 5),
-                INPUT(1, Data|Var|Abs),
-                REPORT_COUNT(1, 3),
-                INPUT(1, Cnst|Var|Abs),
-
-                USAGE(1, PID_USAGE_EFFECT_PLAYING),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 1),
-                REPORT_COUNT(1, 1),
-                INPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                LOGICAL_MINIMUM(1, 0x00),
-                REPORT_SIZE(1, 7),
-                REPORT_COUNT(1, 1),
-                INPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE_PAGE(1, HID_USAGE_PAGE_PID),
-            USAGE(1, PID_USAGE_DEVICE_CONTROL_REPORT),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 1),
-
-                USAGE(1, PID_USAGE_DEVICE_CONTROL),
-                COLLECTION(1, Logical),
-                    USAGE(1, PID_USAGE_DC_DEVICE_RESET),
-                    LOGICAL_MINIMUM(1, 1),
-                    LOGICAL_MAXIMUM(1, 2),
-                    PHYSICAL_MINIMUM(1, 1),
-                    PHYSICAL_MAXIMUM(1, 2),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 1),
-                    OUTPUT(1, Data|Ary|Abs),
-                END_COLLECTION,
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_EFFECT_OPERATION_REPORT),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 2),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_EFFECT_OPERATION),
-                COLLECTION(1, NamedArray),
-                    USAGE(1, PID_USAGE_OP_EFFECT_START),
-                    USAGE(1, PID_USAGE_OP_EFFECT_START_SOLO),
-                    USAGE(1, PID_USAGE_OP_EFFECT_STOP),
-                    LOGICAL_MINIMUM(1, 1),
-                    LOGICAL_MAXIMUM(1, 3),
-                    PHYSICAL_MINIMUM(1, 1),
-                    PHYSICAL_MAXIMUM(1, 3),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 1),
-                    OUTPUT(1, Data|Ary|Abs),
-                END_COLLECTION,
-
-                USAGE(1, PID_USAGE_LOOP_COUNT),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_SET_EFFECT_REPORT),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 3),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_EFFECT_TYPE),
-                COLLECTION(1, NamedArray),
-                    USAGE(1, PID_USAGE_ET_SQUARE),
-                    USAGE(1, PID_USAGE_ET_SINE),
-                    USAGE(1, PID_USAGE_ET_SPRING),
-                    LOGICAL_MINIMUM(1, 1),
-                    LOGICAL_MAXIMUM(1, 3),
-                    PHYSICAL_MINIMUM(1, 1),
-                    PHYSICAL_MAXIMUM(1, 3),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 1),
-                    OUTPUT(1, Data|Ary|Abs),
-                END_COLLECTION,
-
-                USAGE(1, PID_USAGE_AXES_ENABLE),
-                COLLECTION(1, Logical),
-                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_X),
-                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Y),
-                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Z),
-                    LOGICAL_MINIMUM(1, 0),
-                    LOGICAL_MAXIMUM(1, 1),
-                    PHYSICAL_MINIMUM(1, 0),
-                    PHYSICAL_MAXIMUM(1, 1),
-                    REPORT_SIZE(1, 1),
-                    REPORT_COUNT(1, 3),
-                    OUTPUT(1, Data|Var|Abs),
-                END_COLLECTION,
-                USAGE(1, PID_USAGE_DIRECTION_ENABLE),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-                REPORT_COUNT(1, 4),
-                OUTPUT(1, Cnst|Var|Abs),
-
-                USAGE(1, PID_USAGE_DURATION),
-                USAGE(1, PID_USAGE_START_DELAY),
-                UNIT(2, 0x1003),      /* Eng Lin:Time */
-                UNIT_EXPONENT(1, -3), /* 10^-3 */
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x7fff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x7fff),
-                REPORT_SIZE(1, 16),
-                REPORT_COUNT(1, 2),
-                OUTPUT(1, Data|Var|Abs),
-                UNIT(1, 0),
-                UNIT_EXPONENT(1, 0),
-
-                USAGE(1, PID_USAGE_TRIGGER_BUTTON),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 0x08),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(1, 0x08),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_DIRECTION),
-                COLLECTION(1, Logical),
-                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1),
-                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2),
-                    UNIT(1, 0x14),        /* Eng Rot:Angular Pos */
-                    UNIT_EXPONENT(1, -2), /* 10^-2 */
-                    LOGICAL_MINIMUM(1, 0),
-                    LOGICAL_MAXIMUM(2, 0x00ff),
-                    PHYSICAL_MINIMUM(1, 0),
-                    PHYSICAL_MAXIMUM(4, 0x00008ca0),
-                    UNIT(1, 0),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 2),
-                    OUTPUT(1, Data|Var|Abs),
-                    UNIT_EXPONENT(1, 0),
-                    UNIT(1, 0),
-                END_COLLECTION,
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_SET_PERIODIC_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 5),
-
-                USAGE(1, PID_USAGE_MAGNITUDE),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x00ff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_SET_ENVELOPE_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 6),
-
-                USAGE(1, PID_USAGE_ATTACK_LEVEL),
-                USAGE(1, PID_USAGE_FADE_LEVEL),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x00ff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 2),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_ATTACK_TIME),
-                USAGE(1, PID_USAGE_FADE_TIME),
-                UNIT(2, 0x1003),      /* Eng Lin:Time */
-                UNIT_EXPONENT(1, -3), /* 10^-3 */
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x7fff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x7fff),
-                REPORT_SIZE(1, 16),
-                REPORT_COUNT(1, 2),
-                OUTPUT(1, Data|Var|Abs),
-                PHYSICAL_MAXIMUM(1, 0),
-                UNIT_EXPONENT(1, 0),
-                UNIT(1, 0),
-            END_COLLECTION,
-
-
-            USAGE(1, PID_USAGE_SET_CONDITION_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 7),
-
-                USAGE(1, PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET),
-                COLLECTION(1, Logical),
-                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1),
-                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2),
-                    LOGICAL_MINIMUM(1, 0),
-                    LOGICAL_MAXIMUM(1, 1),
-                    PHYSICAL_MINIMUM(1, 0),
-                    PHYSICAL_MAXIMUM(1, 1),
-                    REPORT_SIZE(1, 2),
-                    REPORT_COUNT(1, 2),
-                    OUTPUT(1, Data|Var|Abs),
-                END_COLLECTION,
-                REPORT_SIZE(1, 4),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Cnst|Var|Abs),
-
-                USAGE(1, PID_USAGE_CP_OFFSET),
-                LOGICAL_MINIMUM(1, 0x80),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(2, 0xd8f0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_POSITIVE_COEFFICIENT),
-                USAGE(1, PID_USAGE_NEGATIVE_COEFFICIENT),
-                LOGICAL_MINIMUM(1, 0x80),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(2, 0xd8f0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 2),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_POSITIVE_SATURATION),
-                USAGE(1, PID_USAGE_NEGATIVE_SATURATION),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x00ff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 2),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_DEAD_BAND),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x00ff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-
-            USAGE(1, PID_USAGE_DEVICE_GAIN_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 8),
-
-                USAGE(1, PID_USAGE_DEVICE_GAIN),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x00ff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-        END_COLLECTION,
-    };
-#undef REPORT_ID_OR_USAGE_PAGE
-#include "pop_hid_macros.h"
-
-    static const HIDP_CAPS hid_caps =
-    {
-        .InputReportByteLength = 5,
-    };
-    const DIDEVCAPS expect_caps =
-    {
-        .dwSize = sizeof(DIDEVCAPS),
-        .dwFlags = DIDC_FORCEFEEDBACK | DIDC_ATTACHED | DIDC_EMULATED | DIDC_STARTDELAY |
-                   DIDC_FFFADE | DIDC_FFATTACK | DIDC_DEADBAND | DIDC_SATURATION,
-        .dwDevType = version >= 0x800 ? DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK
-                                      : DIDEVTYPE_HID | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_JOYSTICK,
-        .dwAxes = 3,
-        .dwButtons = 2,
-        .dwFFSamplePeriod = 1000000,
-        .dwFFMinTimeResolution = 1000000,
-        .dwHardwareRevision = 1,
-        .dwFFDriverVersion = 1,
-    };
-    struct hid_expect expect_acquire[] =
-    {
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x01},
-        },
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 8,
-            .report_len = 2,
-            .report_buf = {8, 0x19},
-        },
-    };
-    struct hid_expect expect_reset[] =
-    {
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x01},
-        },
-    };
-    struct hid_expect expect_set_device_gain_1 =
-    {
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 8,
-        .report_len = 2,
-        .report_buf = {8, 0x19},
-    };
-    struct hid_expect expect_set_device_gain_2 =
-    {
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 8,
-        .report_len = 2,
-        .report_buf = {8, 0x33},
-    };
-
-    const DIDEVICEINSTANCEW expect_devinst =
-    {
-        .dwSize = sizeof(DIDEVICEINSTANCEW),
-        .guidInstance = expect_guid_product,
-        .guidProduct = expect_guid_product,
-        .dwDevType = version >= 0x800 ? DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK
-                                      : DIDEVTYPE_HID | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_JOYSTICK,
-        .tszInstanceName = L"Wine test root driver",
-        .tszProductName = L"Wine test root driver",
-        .guidFFDriver = IID_IDirectInputPIDDriver,
-        .wUsagePage = HID_USAGE_PAGE_GENERIC,
-        .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-    };
-    const DIDEVICEOBJECTINSTANCEW expect_objects_5[] =
-    {
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_XAxis,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(0)|DIDFT_FFACTUATOR,
-            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
-            .tszName = L"X Axis",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_X,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_YAxis,
-            .dwOfs = 0x4,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(1)|DIDFT_FFACTUATOR,
-            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
-            .tszName = L"Y Axis",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_Y,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_ZAxis,
-            .dwOfs = 0x8,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(2)|DIDFT_FFACTUATOR,
-            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
-            .tszName = L"Z Axis",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_Z,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Button,
-            .dwOfs = 0x30,
-            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(0)|DIDFT_FFEFFECTTRIGGER,
-            .dwFlags = DIDOI_FFEFFECTTRIGGER,
-            .tszName = L"Button 0",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_BUTTON,
-            .wUsage = 0x1,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Button,
-            .dwOfs = 0x31,
-            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(1)|DIDFT_FFEFFECTTRIGGER,
-            .dwFlags = DIDOI_FFEFFECTTRIGGER,
-            .tszName = L"Button 1",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_BUTTON,
-            .wUsage = 0x2,
-            .wReportId = 1,
-        },
-    };
-    const DIDEVICEOBJECTINSTANCEW expect_objects[] =
-    {
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_ZAxis,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(2)|DIDFT_FFACTUATOR,
-            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
-            .tszName = L"Z Axis",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_Z,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_YAxis,
-            .dwOfs = 0x4,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(1)|DIDFT_FFACTUATOR,
-            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
-            .tszName = L"Y Axis",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_Y,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_XAxis,
-            .dwOfs = 0x8,
-            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(0)|DIDFT_FFACTUATOR,
-            .dwFlags = DIDOI_ASPECTPOSITION|DIDOI_FFACTUATOR,
-            .tszName = L"X Axis",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_X,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Button,
-            .dwOfs = version >= 0x800 ? 0x68 : 0x10,
-            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(0)|DIDFT_FFEFFECTTRIGGER,
-            .dwFlags = DIDOI_FFEFFECTTRIGGER,
-            .tszName = L"Button 0",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_BUTTON,
-            .wUsage = 0x1,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Button,
-            .dwOfs = version >= 0x800 ? 0x69 : 0x11,
-            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(1)|DIDFT_FFEFFECTTRIGGER,
-            .dwFlags = DIDOI_FFEFFECTTRIGGER,
-            .tszName = L"Button 1",
-            .wCollectionNumber = 1,
-            .wUsagePage = HID_USAGE_PAGE_BUTTON,
-            .wUsage = 0x2,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x70 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(12)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"DC Device Reset",
-            .wCollectionNumber = 4,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DC_DEVICE_RESET,
-            .wReportId = 1,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x10 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(13)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Effect Block Index",
-            .wCollectionNumber = 5,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_EFFECT_BLOCK_INDEX,
-            .wReportId = 2,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x71 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(14)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Op Effect Start",
-            .wCollectionNumber = 6,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_OP_EFFECT_START,
-            .wReportId = 2,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x72 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(15)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Op Effect Start Solo",
-            .wCollectionNumber = 6,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_OP_EFFECT_START_SOLO,
-            .wReportId = 2,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x73 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(16)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Op Effect Stop",
-            .wCollectionNumber = 6,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_OP_EFFECT_STOP,
-            .wReportId = 2,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x14 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(17)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Loop Count",
-            .wCollectionNumber = 5,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_LOOP_COUNT,
-            .wReportId = 2,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x18 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(18)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Effect Block Index",
-            .wCollectionNumber = 7,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_EFFECT_BLOCK_INDEX,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x74 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(19)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"ET Square",
-            .wCollectionNumber = 8,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_ET_SQUARE,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x75 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(20)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"ET Sine",
-            .wCollectionNumber = 8,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_ET_SINE,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x76 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(21)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"ET Spring",
-            .wCollectionNumber = 8,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_ET_SPRING,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x77 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(22)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Z Axis",
-            .wCollectionNumber = 9,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_Z,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x78 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(23)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Y Axis",
-            .wCollectionNumber = 9,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_Y,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x79 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(24)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"X Axis",
-            .wCollectionNumber = 9,
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_X,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x7a : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(25)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Direction Enable",
-            .wCollectionNumber = 7,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DIRECTION_ENABLE,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x1c : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(26)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Start Delay",
-            .wCollectionNumber = 7,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_START_DELAY,
-            .wReportId = 3,
-            .dwDimension = 0x1003,
-            .wExponent = -3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x20 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(27)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Duration",
-            .wCollectionNumber = 7,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DURATION,
-            .wReportId = 3,
-            .dwDimension = 0x1003,
-            .wExponent = -3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x24 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(28)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Trigger Button",
-            .wCollectionNumber = 7,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_TRIGGER_BUTTON,
-            .wReportId = 3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x28 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(29)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Unknown 29",
-            .wCollectionNumber = 10,
-            .wUsagePage = HID_USAGE_PAGE_ORDINAL,
-            .wUsage = 2,
-            .wReportId = 3,
-            .wExponent = -2,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x2c : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(30)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Unknown 30",
-            .wCollectionNumber = 10,
-            .wUsagePage = HID_USAGE_PAGE_ORDINAL,
-            .wUsage = 1,
-            .wReportId = 3,
-            .wExponent = -2,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x30 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(31)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Magnitude",
-            .wCollectionNumber = 11,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_MAGNITUDE,
-            .wReportId = 5,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x34 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(32)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Fade Level",
-            .wCollectionNumber = 12,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_FADE_LEVEL,
-            .wReportId = 6,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x38 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(33)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Attack Level",
-            .wCollectionNumber = 12,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_ATTACK_LEVEL,
-            .wReportId = 6,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x3c : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(34)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Fade Time",
-            .wCollectionNumber = 12,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_FADE_TIME,
-            .wReportId = 6,
-            .dwDimension = 0x1003,
-            .wExponent = -3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x40 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(35)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Attack Time",
-            .wCollectionNumber = 12,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_ATTACK_TIME,
-            .wReportId = 6,
-            .dwDimension = 0x1003,
-            .wExponent = -3,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x44 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(36)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Unknown 36",
-            .wCollectionNumber = 14,
-            .wUsagePage = HID_USAGE_PAGE_ORDINAL,
-            .wUsage = 2,
-            .wReportId = 7,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x48 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(37)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Unknown 37",
-            .wCollectionNumber = 14,
-            .wUsagePage = HID_USAGE_PAGE_ORDINAL,
-            .wUsage = 1,
-            .wReportId = 7,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x4c : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(38)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"CP Offset",
-            .wCollectionNumber = 13,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_CP_OFFSET,
-            .wReportId = 7,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x50 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(39)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Negative Coefficient",
-            .wCollectionNumber = 13,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_NEGATIVE_COEFFICIENT,
-            .wReportId = 7,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x54 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(40)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Positive Coefficient",
-            .wCollectionNumber = 13,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_POSITIVE_COEFFICIENT,
-            .wReportId = 7,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x58 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(41)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Negative Saturation",
-            .wCollectionNumber = 13,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_NEGATIVE_SATURATION,
-            .wReportId = 7,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x5c : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(42)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Positive Saturation",
-            .wCollectionNumber = 13,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_POSITIVE_SATURATION,
-            .wReportId = 7,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x60 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(43)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Dead Band",
-            .wCollectionNumber = 13,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DEAD_BAND,
-            .wReportId = 7,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwOfs = version >= 0x800 ? 0x64 : 0,
-            .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(44)|DIDFT_OUTPUT,
-            .dwFlags = 0x80008000,
-            .tszName = L"Device Gain",
-            .wCollectionNumber = 15,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DEVICE_GAIN,
-            .wReportId = 8,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(0),
-            .tszName = L"Collection 0 - Joystick",
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(1),
-            .tszName = L"Collection 1 - Joystick",
-            .wUsagePage = HID_USAGE_PAGE_GENERIC,
-            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(2),
-            .tszName = L"Collection 2 - PID State Report",
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_STATE_REPORT,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(3),
-            .tszName = L"Collection 3 - PID Device Control Report",
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DEVICE_CONTROL_REPORT,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(4),
-            .tszName = L"Collection 4 - PID Device Control",
-            .wCollectionNumber = 3,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DEVICE_CONTROL,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(5),
-            .tszName = L"Collection 5 - Effect Operation Report",
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_EFFECT_OPERATION_REPORT,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(6),
-            .tszName = L"Collection 6 - Effect Operation",
-            .wCollectionNumber = 5,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_EFFECT_OPERATION,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(7),
-            .tszName = L"Collection 7 - Set Effect Report",
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_SET_EFFECT_REPORT,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(8),
-            .tszName = L"Collection 8 - Effect Type",
-            .wCollectionNumber = 7,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_EFFECT_TYPE,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(9),
-            .tszName = L"Collection 9 - Axes Enable",
-            .wCollectionNumber = 7,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_AXES_ENABLE,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(10),
-            .tszName = L"Collection 10 - Direction",
-            .wCollectionNumber = 7,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DIRECTION,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(11),
-            .tszName = L"Collection 11 - Set Periodic Report",
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_SET_PERIODIC_REPORT,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(12),
-            .tszName = L"Collection 12 - Set Envelope Report",
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_SET_ENVELOPE_REPORT,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(13),
-            .tszName = L"Collection 13 - Set Condition Report",
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_SET_CONDITION_REPORT,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(14),
-            .tszName = L"Collection 14 - Type Specific Block Offset",
-            .wCollectionNumber = 13,
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET,
-        },
-        {
-            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
-            .guidType = GUID_Unknown,
-            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(15),
-            .tszName = L"Collection 15 - Device Gain Report",
-            .wUsagePage = HID_USAGE_PAGE_PID,
-            .wUsage = PID_USAGE_DEVICE_GAIN_REPORT,
-        },
-    };
-    const DIEFFECTINFOW expect_effects[] =
-    {
-        {
-            .dwSize = sizeof(DIEFFECTINFOW),
-            .guid = GUID_Square,
-            .dwEffType = DIEFT_PERIODIC | DIEFT_STARTDELAY | DIEFT_FFFADE | DIEFT_FFATTACK,
-            .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
-                              DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE,
-            .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
-                              DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE,
-            .tszName = L"GUID_Square",
-        },
-        {
-            .dwSize = sizeof(DIEFFECTINFOW),
-            .guid = GUID_Sine,
-            .dwEffType = DIEFT_PERIODIC | DIEFT_STARTDELAY | DIEFT_FFFADE | DIEFT_FFATTACK,
-            .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
-                              DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE,
-            .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
-                              DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE,
-            .tszName = L"GUID_Sine",
-        },
-        {
-            .dwSize = sizeof(DIEFFECTINFOW),
-            .guid = GUID_Spring,
-            .dwEffType = DIEFT_CONDITION | DIEFT_STARTDELAY | DIEFT_DEADBAND | DIEFT_SATURATION,
-            .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
-                              DIEP_DURATION,
-            .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY |
-                              DIEP_DURATION,
-            .tszName = L"GUID_Spring",
-        }
-    };
-
-    struct check_objects_todos todo_objects_5[ARRAY_SIZE(expect_objects_5)] =
-    {
-        {.guid = TRUE, .type = TRUE, .usage = TRUE, .name = TRUE},
-        {0},
-        {.guid = TRUE, .type = TRUE, .usage = TRUE, .name = TRUE},
-    };
-    struct check_objects_params check_objects_params =
-    {
-        .version = version,
-        .expect_count = version < 0x700 ? ARRAY_SIZE(expect_objects_5) : ARRAY_SIZE(expect_objects),
-        .expect_objs = version < 0x700 ? expect_objects_5 : expect_objects,
-        .todo_objs = version < 0x700 ? todo_objects_5 : NULL,
-        .todo_extra = version < 0x700 ? TRUE : FALSE,
-    };
-    struct check_effects_params check_effects_params =
-    {
-        .expect_count = ARRAY_SIZE(expect_effects),
-        .expect_effects = expect_effects,
-    };
-    DIPROPDWORD prop_dword =
-    {
-        .diph =
-        {
-            .dwSize = sizeof(DIPROPDWORD),
-            .dwHeaderSize = sizeof(DIPROPHEADER),
-            .dwHow = DIPH_DEVICE,
-        },
-    };
-    DIPROPGUIDANDPATH prop_guid_path =
-    {
-        .diph =
-        {
-            .dwSize = sizeof(DIPROPGUIDANDPATH),
-            .dwHeaderSize = sizeof(DIPROPHEADER),
-            .dwHow = DIPH_DEVICE,
-        },
-    };
-    DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
-    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
-    DIDEVICEOBJECTDATA objdata = {0};
-    DIEFFECTINFOW effectinfo = {0};
-    IDirectInputDevice8W *device;
-    DIEFFESCAPE escape = {0};
-    DIDEVCAPS caps = {0};
-    char buffer[1024];
-    ULONG res, ref;
-    HANDLE file;
-    HRESULT hr;
-    HWND hwnd;
-
-    winetest_push_context( "version %#x", version );
-
-    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
-    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
-    SetCurrentDirectoryW( tempdir );
-
-    cleanup_registry_keys();
-    if (!dinput_driver_start( report_descriptor, sizeof(report_descriptor), &hid_caps, NULL, 0 )) goto done;
-    if (FAILED(hr = create_dinput_device( version, &devinst, &device ))) goto done;
-
-    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
-    ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
-    check_member( devinst, expect_devinst, "%d", dwSize );
-    todo_wine
-    check_member_guid( devinst, expect_devinst, guidInstance );
-    check_member_guid( devinst, expect_devinst, guidProduct );
-    check_member( devinst, expect_devinst, "%#x", dwDevType );
-    todo_wine
-    check_member_wstr( devinst, expect_devinst, tszInstanceName );
-    todo_wine
-    check_member_wstr( devinst, expect_devinst, tszProductName );
-    check_member_guid( devinst, expect_devinst, guidFFDriver );
-    check_member( devinst, expect_devinst, "%04x", wUsagePage );
-    check_member( devinst, expect_devinst, "%04x", wUsage );
-
-    caps.dwSize = sizeof(DIDEVCAPS);
-    hr = IDirectInputDevice8_GetCapabilities( device, &caps );
-    ok( hr == DI_OK, "GetCapabilities returned %#x\n", hr );
-    check_member( caps, expect_caps, "%d", dwSize );
-    check_member( caps, expect_caps, "%#x", dwFlags );
-    check_member( caps, expect_caps, "%#x", dwDevType );
-    check_member( caps, expect_caps, "%d", dwAxes );
-    check_member( caps, expect_caps, "%d", dwButtons );
-    check_member( caps, expect_caps, "%d", dwPOVs );
-    check_member( caps, expect_caps, "%d", dwFFSamplePeriod );
-    check_member( caps, expect_caps, "%d", dwFFMinTimeResolution );
-    check_member( caps, expect_caps, "%d", dwFirmwareRevision );
-    check_member( caps, expect_caps, "%d", dwHardwareRevision );
-    check_member( caps, expect_caps, "%d", dwFFDriverVersion );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_FFGAIN returned %#x\n", hr );
-    ok( prop_dword.dwData == 10000, "got %u expected %u\n", prop_dword.dwData, 10000 );
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_EnumObjects( device, check_objects, &check_objects_params, DIDFT_ALL );
-    ok( hr == DI_OK, "EnumObjects returned %#x\n", hr );
-    ok( check_objects_params.index >= check_objects_params.expect_count, "missing %u objects\n",
-        check_objects_params.expect_count - check_objects_params.index );
-
-    res = 0;
-    hr = IDirectInputDevice8_EnumEffects( device, check_effect_count, &res, 0xfe );
-    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
-    ok( res == 0, "got %u expected %u\n", res, 0 );
-    res = 0;
-    hr = IDirectInputDevice8_EnumEffects( device, check_effect_count, &res, DIEFT_PERIODIC );
-    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
-    ok( res == 2, "got %u expected %u\n", res, 2 );
-    hr = IDirectInputDevice8_EnumEffects( device, check_effects, &check_effects_params, DIEFT_ALL );
-    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
-    ok( check_effects_params.index >= check_effects_params.expect_count, "missing %u effects\n",
-        check_effects_params.expect_count - check_effects_params.index );
-
-    effectinfo.dwSize = sizeof(DIEFFECTINFOW);
-    hr = IDirectInputDevice8_GetEffectInfo( device, &effectinfo, &GUID_Sine );
-    ok( hr == DI_OK, "GetEffectInfo returned %#x\n", hr );
-    check_member_guid( effectinfo, expect_effects[1], guid );
-    check_member( effectinfo, expect_effects[1], "%#x", dwEffType );
-    check_member( effectinfo, expect_effects[1], "%#x", dwStaticParams );
-    check_member( effectinfo, expect_effects[1], "%#x", dwDynamicParams );
-    check_member_wstr( effectinfo, expect_effects[1], tszName );
-
-    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
-
-    file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
-                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
-                        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
-    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
-
-    hwnd = CreateWindowW( L"static", L"dinput", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200,
-                          NULL, NULL, NULL, NULL );
-
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE );
-    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
-
-    prop_dword.diph.dwHow = DIPH_BYUSAGE;
-    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
-    prop_dword.dwData = DIPROPAUTOCENTER_ON;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
-    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
-    prop_dword.diph.dwHow = DIPH_DEVICE;
-    prop_dword.diph.dwObj = 0;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
-    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
-    prop_dword.dwData = 1000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DIERR_READONLY, "SetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetForceFeedbackState returned %#x\n", hr );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_RESET );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "SendForceFeedbackCommand returned %#x\n", hr );
-
-    escape.dwSize = sizeof(DIEFFESCAPE);
-    escape.dwCommand = 0;
-    escape.lpvInBuffer = buffer;
-    escape.cbInBuffer = 10;
-    escape.lpvOutBuffer = buffer + 10;
-    escape.cbOutBuffer = 10;
-    hr = IDirectInputDevice8_Escape( device, &escape );
-    todo_wine
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Escape returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE );
-    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
-
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-
-    set_hid_expect( file, &expect_set_device_gain_2, sizeof(expect_set_device_gain_2) );
-    prop_dword.dwData = 2000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-
-    set_hid_expect( file, &expect_set_device_gain_1, sizeof(expect_set_device_gain_1) );
-    prop_dword.dwData = 1000;
-    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
-    ok( hr == DI_OK, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-
-    hr = IDirectInputDevice8_Escape( device, &escape );
-    todo_wine
-    ok( hr == DIERR_UNSUPPORTED, "Escape returned: %#x\n", hr );
-
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    todo_wine
-    ok( hr == 0x80040301, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    todo_wine
-    ok( hr == 0x80040301, "GetForceFeedbackState returned %#x\n", hr );
-
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, 0xdeadbeef );
-    ok( hr == DIERR_INVALIDPARAM, "SendForceFeedbackCommand returned %#x\n", hr );
-
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_RESET );
-    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
-    wait_hid_expect( file, 100 ); /* device gain reports are written asynchronously */
-
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_STOPALL );
-    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_PAUSE );
-    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_CONTINUE );
-    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_SETACTUATORSON );
-    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_SETACTUATORSOFF );
-    ok( hr == HIDP_STATUS_USAGE_NOT_FOUND, "SendForceFeedbackCommand returned %#x\n", hr );
-
-    objdata.dwOfs = 0x1e;
-    objdata.dwData = 0x80;
-    res = 1;
-    hr = IDirectInputDevice8_SendDeviceData( device, sizeof(DIDEVICEOBJECTDATA), &objdata, &res, 0 );
-    if (version < 0x800) ok( hr == DI_OK, "SendDeviceData returned %#x\n", hr );
-    else todo_wine ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
-
-    test_periodic_effect( device, file, version );
-    test_condition_effect( device, file, version );
-
-    set_hid_expect( file, expect_reset, sizeof(expect_reset) );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    ref = IDirectInputDevice8_Release( device );
-    ok( ref == 0, "Release returned %d\n", ref );
-
-    DestroyWindow( hwnd );
-    CloseHandle( file );
-
-done:
-    pnp_driver_stop();
-    cleanup_registry_keys();
-    SetCurrentDirectoryW( cwd );
-    winetest_pop_context();
-}
-
-static void test_device_managed_effect(void)
-{
-#include "psh_hid_macros.h"
-    const unsigned char report_descriptor[] = {
-        USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
-        USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
-        COLLECTION(1, Application),
-            USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 1),
-
-                USAGE(1, HID_USAGE_GENERIC_X),
-                USAGE(1, HID_USAGE_GENERIC_Y),
-                USAGE(1, HID_USAGE_GENERIC_Z),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 3),
-                INPUT(1, Data|Var|Abs),
-
-                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
-                USAGE_MINIMUM(1, 1),
-                USAGE_MAXIMUM(1, 2),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 1),
-                REPORT_COUNT(1, 2),
-                INPUT(1, Data|Var|Abs),
-                REPORT_COUNT(1, 6),
-                INPUT(1, Cnst|Var|Abs),
-            END_COLLECTION,
-
-            USAGE_PAGE(1, HID_USAGE_PAGE_PID),
-            USAGE(1, PID_USAGE_STATE_REPORT),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 2),
-
-                USAGE(1, PID_USAGE_DEVICE_PAUSED),
-                USAGE(1, PID_USAGE_ACTUATORS_ENABLED),
-                USAGE(1, PID_USAGE_SAFETY_SWITCH),
-                USAGE(1, PID_USAGE_ACTUATOR_OVERRIDE_SWITCH),
-                USAGE(1, PID_USAGE_ACTUATOR_POWER),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 1),
-                REPORT_COUNT(1, 5),
-                INPUT(1, Data|Var|Abs),
-                REPORT_COUNT(1, 3),
-                INPUT(1, Cnst|Var|Abs),
-
-                USAGE(1, PID_USAGE_EFFECT_PLAYING),
-                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),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                INPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE_PAGE(1, HID_USAGE_PAGE_PID),
-            USAGE(1, PID_USAGE_DEVICE_CONTROL_REPORT),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 1),
-
-                USAGE(1, PID_USAGE_DEVICE_CONTROL),
-                COLLECTION(1, Logical),
-                    USAGE(1, PID_USAGE_DC_DEVICE_RESET),
-                    USAGE(1, PID_USAGE_DC_DEVICE_PAUSE),
-                    USAGE(1, PID_USAGE_DC_DEVICE_CONTINUE),
-                    USAGE(1, PID_USAGE_DC_ENABLE_ACTUATORS),
-                    USAGE(1, PID_USAGE_DC_DISABLE_ACTUATORS),
-                    USAGE(1, PID_USAGE_DC_STOP_ALL_EFFECTS),
-                    LOGICAL_MINIMUM(1, 1),
-                    LOGICAL_MAXIMUM(1, 6),
-                    PHYSICAL_MINIMUM(1, 1),
-                    PHYSICAL_MAXIMUM(1, 6),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 1),
-                    OUTPUT(1, Data|Ary|Abs),
-                END_COLLECTION,
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_EFFECT_OPERATION_REPORT),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 2),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_EFFECT_OPERATION),
-                COLLECTION(1, NamedArray),
-                    USAGE(1, PID_USAGE_OP_EFFECT_START),
-                    USAGE(1, PID_USAGE_OP_EFFECT_START_SOLO),
-                    USAGE(1, PID_USAGE_OP_EFFECT_STOP),
-                    LOGICAL_MINIMUM(1, 1),
-                    LOGICAL_MAXIMUM(1, 3),
-                    PHYSICAL_MINIMUM(1, 1),
-                    PHYSICAL_MAXIMUM(1, 3),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 1),
-                    OUTPUT(1, Data|Ary|Abs),
-                END_COLLECTION,
-
-                USAGE(1, PID_USAGE_LOOP_COUNT),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_SET_EFFECT_REPORT),
-            COLLECTION(1, Report),
-                REPORT_ID(1, 3),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_EFFECT_TYPE),
-                COLLECTION(1, NamedArray),
-                    USAGE(1, PID_USAGE_ET_SQUARE),
-                    USAGE(1, PID_USAGE_ET_SINE),
-                    USAGE(1, PID_USAGE_ET_SPRING),
-                    LOGICAL_MINIMUM(1, 1),
-                    LOGICAL_MAXIMUM(1, 3),
-                    PHYSICAL_MINIMUM(1, 1),
-                    PHYSICAL_MAXIMUM(1, 3),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 1),
-                    OUTPUT(1, Data|Ary|Abs),
-                END_COLLECTION,
-
-                USAGE(1, PID_USAGE_AXES_ENABLE),
-                COLLECTION(1, Logical),
-                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_X),
-                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Y),
-                    USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Z),
-                    LOGICAL_MINIMUM(1, 0),
-                    LOGICAL_MAXIMUM(1, 1),
-                    PHYSICAL_MINIMUM(1, 0),
-                    PHYSICAL_MAXIMUM(1, 1),
-                    REPORT_SIZE(1, 1),
-                    REPORT_COUNT(1, 3),
-                    OUTPUT(1, Data|Var|Abs),
-                END_COLLECTION,
-                USAGE(1, PID_USAGE_DIRECTION_ENABLE),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-                REPORT_COUNT(1, 4),
-                OUTPUT(1, Cnst|Var|Abs),
-
-                USAGE(1, PID_USAGE_DURATION),
-                USAGE(1, PID_USAGE_START_DELAY),
-                UNIT(2, 0x1003),      /* Eng Lin:Time */
-                UNIT_EXPONENT(1, -3), /* 10^-3 */
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x7fff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x7fff),
-                REPORT_SIZE(1, 16),
-                REPORT_COUNT(1, 2),
-                OUTPUT(1, Data|Var|Abs),
-                UNIT(1, 0),
-                UNIT_EXPONENT(1, 0),
-
-                USAGE(1, PID_USAGE_TRIGGER_BUTTON),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 0x08),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(1, 0x08),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_DIRECTION),
-                COLLECTION(1, Logical),
-                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1),
-                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2),
-                    UNIT(1, 0x14),        /* Eng Rot:Angular Pos */
-                    UNIT_EXPONENT(1, -2), /* 10^-2 */
-                    LOGICAL_MINIMUM(1, 0),
-                    LOGICAL_MAXIMUM(2, 0x00ff),
-                    PHYSICAL_MINIMUM(1, 0),
-                    PHYSICAL_MAXIMUM(4, 0x00008ca0),
-                    UNIT(1, 0),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 2),
-                    OUTPUT(1, Data|Var|Abs),
-                    UNIT_EXPONENT(1, 0),
-                    UNIT(1, 0),
-                END_COLLECTION,
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_SET_CONDITION_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 4),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_PARAMETER_BLOCK_OFFSET),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 4),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET),
-                COLLECTION(1, Logical),
-                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1),
-                    USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2),
-                    LOGICAL_MINIMUM(1, 0),
-                    LOGICAL_MAXIMUM(1, 1),
-                    PHYSICAL_MINIMUM(1, 0),
-                    PHYSICAL_MAXIMUM(1, 1),
-                    REPORT_SIZE(1, 2),
-                    REPORT_COUNT(1, 2),
-                    OUTPUT(1, Data|Var|Abs),
-                END_COLLECTION,
-
-                USAGE(1, PID_USAGE_CP_OFFSET),
-                LOGICAL_MINIMUM(1, 0x80),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(2, 0xd8f0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_POSITIVE_COEFFICIENT),
-                USAGE(1, PID_USAGE_NEGATIVE_COEFFICIENT),
-                LOGICAL_MINIMUM(1, 0x80),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(2, 0xd8f0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 2),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_POSITIVE_SATURATION),
-                USAGE(1, PID_USAGE_NEGATIVE_SATURATION),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x00ff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 2),
-                OUTPUT(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_DEAD_BAND),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x00ff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_BLOCK_FREE_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 5),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_DEVICE_GAIN_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 6),
-
-                USAGE(1, PID_USAGE_DEVICE_GAIN),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(2, 0x00ff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(2, 0x2710),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                OUTPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_POOL_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 1),
-
-                USAGE(1, PID_USAGE_RAM_POOL_SIZE),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(4, 0xffff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(4, 0xffff),
-                REPORT_SIZE(1, 16),
-                REPORT_COUNT(1, 1),
-                FEATURE(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_SIMULTANEOUS_EFFECTS_MAX),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                FEATURE(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_DEVICE_MANAGED_POOL),
-                USAGE(1, PID_USAGE_SHARED_PARAMETER_BLOCKS),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 1),
-                REPORT_COUNT(1, 8),
-                FEATURE(1, Data|Var|Abs),
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_CREATE_NEW_EFFECT_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 2),
-
-                USAGE(1, PID_USAGE_EFFECT_TYPE),
-                COLLECTION(1, NamedArray),
-                    USAGE(1, PID_USAGE_ET_SQUARE),
-                    USAGE(1, PID_USAGE_ET_SINE),
-                    USAGE(1, PID_USAGE_ET_SPRING),
-                    LOGICAL_MINIMUM(1, 1),
-                    LOGICAL_MAXIMUM(1, 3),
-                    PHYSICAL_MINIMUM(1, 1),
-                    PHYSICAL_MAXIMUM(1, 3),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 1),
-                    FEATURE(1, Data|Ary|Abs),
-                END_COLLECTION,
-            END_COLLECTION,
-
-            USAGE(1, PID_USAGE_BLOCK_LOAD_REPORT),
-            COLLECTION(1, Logical),
-                REPORT_ID(1, 3),
-
-                USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 0x7f),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(1, 0x7f),
-                REPORT_SIZE(1, 8),
-                REPORT_COUNT(1, 1),
-                FEATURE(1, Data|Var|Abs),
-
-                USAGE(1, PID_USAGE_BLOCK_LOAD_STATUS),
-                COLLECTION(1, NamedArray),
-                    USAGE(1, PID_USAGE_BLOCK_LOAD_SUCCESS),
-                    USAGE(1, PID_USAGE_BLOCK_LOAD_FULL),
-                    USAGE(1, PID_USAGE_BLOCK_LOAD_ERROR),
-                    LOGICAL_MINIMUM(1, 1),
-                    LOGICAL_MAXIMUM(1, 3),
-                    PHYSICAL_MINIMUM(1, 1),
-                    PHYSICAL_MAXIMUM(1, 3),
-                    REPORT_SIZE(1, 8),
-                    REPORT_COUNT(1, 1),
-                    FEATURE(1, Data|Ary|Abs),
-                END_COLLECTION,
-
-                USAGE(1, PID_USAGE_RAM_POOL_AVAILABLE),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(4, 0xffff),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(4, 0xffff),
-                REPORT_SIZE(1, 16),
-                REPORT_COUNT(1, 1),
-                FEATURE(1, Data|Var|Abs),
-            END_COLLECTION,
-        END_COLLECTION,
-    };
-#include "pop_hid_macros.h"
-
-    static const HIDP_CAPS hid_caps =
-    {
-        .InputReportByteLength = 5,
-    };
-    struct hid_expect expect_acquire[] =
-    {
-        /* device control */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x01},
-        },
-        /* device gain */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 6,
-            .report_len = 2,
-            .report_buf = {6, 0xff},
-        },
-    };
-    struct hid_expect expect_reset[] =
-    {
-        /* device control */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x01},
-        },
-    };
-    struct hid_expect expect_enable_actuators[] =
-    {
-        /* device control */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x04},
-        },
-    };
-    struct hid_expect expect_disable_actuators[] =
-    {
-        /* device control */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x05},
-        },
-    };
-    struct hid_expect expect_stop_all[] =
-    {
-        /* device control */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x06},
-        },
-    };
-    struct hid_expect expect_device_pause[] =
-    {
-        /* device control */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x02},
-        },
-    };
-    struct hid_expect expect_device_continue[] =
-    {
-        /* device control */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 1,
-            .report_len = 2,
-            .report_buf = {1, 0x03},
-        },
-    };
-    struct hid_expect expect_create[] =
-    {
-        /* create new effect */
-        {
-            .code = IOCTL_HID_SET_FEATURE,
-            .report_id = 2,
-            .report_len = 2,
-            .report_buf = {2,0x03},
-        },
-        /* block load */
-        {
-            .code = IOCTL_HID_GET_FEATURE,
-            .report_id = 3,
-            .report_len = 5,
-            .report_buf = {3,0x01,0x01,0x00,0x00},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 4,
-            .report_len = 9,
-            .report_buf = {4,0x01,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 4,
-            .report_len = 9,
-            .report_buf = {4,0x01,0x01,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {3,0x01,0x03,0x08,0x01,0x00,0x06,0x00,0x01,0x55,0x00},
-        },
-    };
-    struct hid_expect expect_create_2[] =
-    {
-        /* create new effect */
-        {
-            .code = IOCTL_HID_SET_FEATURE,
-            .report_id = 2,
-            .report_len = 2,
-            .report_buf = {2,0x03},
-        },
-        /* block load */
-        {
-            .code = IOCTL_HID_GET_FEATURE,
-            .report_id = 3,
-            .report_len = 5,
-            .report_buf = {3,0x02,0x01,0x00,0x00},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 4,
-            .report_len = 9,
-            .report_buf = {4,0x02,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 4,
-            .report_len = 9,
-            .report_buf = {4,0x02,0x01,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {3,0x02,0x03,0x08,0x01,0x00,0x06,0x00,0x01,0x55,0x00},
-        },
-    };
-    struct hid_expect expect_create_delay[] =
-    {
-        /* create new effect */
-        {
-            .code = IOCTL_HID_SET_FEATURE,
-            .report_id = 2,
-            .report_len = 2,
-            .report_buf = {2,0x03},
-        },
-        /* block load */
-        {
-            .code = IOCTL_HID_GET_FEATURE,
-            .report_id = 3,
-            .report_len = 5,
-            .report_buf = {3,0x01,0x01,0x00,0x00},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 4,
-            .report_len = 9,
-            .report_buf = {4,0x01,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 4,
-            .report_len = 9,
-            .report_buf = {4,0x01,0x01,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {3,0x01,0x03,0x08,0x01,0x00,0xff,0x7f,0x01,0x55,0x00},
-        },
-    };
-    struct hid_expect expect_create_duration[] =
-    {
-        /* create new effect */
-        {
-            .code = IOCTL_HID_SET_FEATURE,
-            .report_id = 2,
-            .report_len = 2,
-            .report_buf = {2,0x03},
-        },
-        /* block load */
-        {
-            .code = IOCTL_HID_GET_FEATURE,
-            .report_id = 3,
-            .report_len = 5,
-            .report_buf = {3,0x01,0x01,0x00,0x00},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 4,
-            .report_len = 9,
-            .report_buf = {4,0x01,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99},
-        },
-        /* set condition */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 4,
-            .report_len = 9,
-            .report_buf = {4,0x01,0x01,0x4c,0x3f,0xcc,0x4c,0x33,0x19},
-        },
-        /* update effect */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 3,
-            .report_len = 11,
-            .report_buf = {3,0x01,0x03,0x08,0x00,0x00,0x00,0x00,0x01,0x55,0x00},
-        },
-    };
-    struct hid_expect expect_start =
-    {
-        /* effect control */
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {2,0x01,0x01,0x01},
-    };
-    struct hid_expect expect_start_2 =
-    {
-        /* effect control */
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {2,0x02,0x02,0x01},
-    };
-    struct hid_expect expect_stop =
-    {
-        /* effect control */
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {2,0x01,0x03,0x00},
-    };
-    struct hid_expect expect_stop_2 =
-    {
-        /* effect control */
-        .code = IOCTL_HID_WRITE_REPORT,
-        .report_id = 2,
-        .report_len = 4,
-        .report_buf = {2,0x02,0x03,0x00},
-    };
-    struct hid_expect expect_destroy[] =
-    {
-        /* effect operation */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 2,
-            .report_len = 4,
-            .report_buf = {2,0x01,0x03,0x00},
-        },
-        /* block free */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 5,
-            .report_len = 2,
-            .report_buf = {5,0x01},
-        },
-    };
-    struct hid_expect expect_destroy_2[] =
-    {
-        /* effect operation */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 2,
-            .report_len = 4,
-            .report_buf = {2,0x02,0x03,0x00},
-        },
-        /* block free */
-        {
-            .code = IOCTL_HID_WRITE_REPORT,
-            .report_id = 5,
-            .report_len = 2,
-            .report_buf = {5,0x02},
-        },
-    };
-    struct hid_expect device_state_input[] =
-    {
-        /* effect state */
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_id = 2,
-            .report_len = 4,
-            .report_buf = {2,0xff,0x00,0xff},
-        },
-        /* device state */
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_id = 1,
-            .report_len = 5,
-            .report_buf = {1,0x12,0x34,0x56,0xff},
-        },
-    };
-    struct hid_expect device_state_input_0[] =
-    {
-        /* effect state */
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_id = 2,
-            .report_len = 4,
-            .report_buf = {2,0xff,0x00,0xff},
-        },
-        /* device state */
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_id = 1,
-            .report_len = 5,
-            .report_buf = {1,0x56,0x12,0x34,0xff},
-        },
-    };
-    struct hid_expect device_state_input_1[] =
-    {
-        /* effect state */
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_id = 2,
-            .report_len = 4,
-            .report_buf = {2,0x00,0x01,0x01},
-        },
-        /* device state */
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_id = 1,
-            .report_len = 5,
-            .report_buf = {1,0x65,0x43,0x21,0x00},
-        },
-    };
-    struct hid_expect device_state_input_2[] =
-    {
-        /* effect state */
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_id = 2,
-            .report_len = 4,
-            .report_buf = {2,0x03,0x00,0x01},
-        },
-        /* device state */
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_id = 1,
-            .report_len = 5,
-            .report_buf = {1,0x12,0x34,0x56,0xff},
-        },
-    };
-    struct hid_expect expect_pool[] =
-    {
-        /* device pool */
-        {
-            .code = IOCTL_HID_GET_FEATURE,
-            .report_id = 1,
-            .report_len = 5,
-            .report_buf = {1,0x10,0x00,0x04,0x03},
-            .todo = TRUE,
-        },
-        /* device pool */
-        {
-            .code = IOCTL_HID_GET_FEATURE,
-            .report_id = 1,
-            .report_len = 5,
-            .report_buf = {1,0x10,0x00,0x04,0x03},
-            .todo = TRUE,
-        },
-    };
-    static const DWORD expect_axes[3] =
-    {
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR,
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR,
-        DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR,
-    };
-    static const LONG expect_directions[3] = {
-        +3000,
-        0,
-        0,
-    };
-    static const DIENVELOPE expect_envelope =
-    {
-        .dwSize = sizeof(DIENVELOPE),
-        .dwAttackLevel = 1000,
-        .dwAttackTime = 2000,
-        .dwFadeLevel = 3000,
-        .dwFadeTime = 4000,
-    };
-    static const DICONDITION expect_condition[3] =
-    {
-        {
-            .lOffset = -500,
-            .lPositiveCoefficient = 2000,
-            .lNegativeCoefficient = -3000,
-            .dwPositiveSaturation = -4000,
-            .dwNegativeSaturation = -5000,
-            .lDeadBand = 6000,
-        },
-        {
-            .lOffset = 6000,
-            .lPositiveCoefficient = 5000,
-            .lNegativeCoefficient = -4000,
-            .dwPositiveSaturation = 3000,
-            .dwNegativeSaturation = 2000,
-            .lDeadBand = 1000,
-        },
-        {
-            .lOffset = -7000,
-            .lPositiveCoefficient = -8000,
-            .lNegativeCoefficient = 9000,
-            .dwPositiveSaturation = 10000,
-            .dwNegativeSaturation = 11000,
-            .lDeadBand = -12000,
-        },
-    };
-    const DIEFFECT expect_desc =
-    {
-        .dwSize = sizeof(DIEFFECT_DX6),
-        .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS,
-        .dwDuration = 1000,
-        .dwSamplePeriod = 2000,
-        .dwGain = 3000,
-        .dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER,
-        .dwTriggerRepeatInterval = 5000,
-        .cAxes = 2,
-        .rgdwAxes = (void *)expect_axes,
-        .rglDirection = (void *)expect_directions,
-        .lpEnvelope = (void *)&expect_envelope,
-        .cbTypeSpecificParams = 2 * sizeof(DICONDITION),
-        .lpvTypeSpecificParams = (void *)expect_condition,
-        .dwStartDelay = 6000,
-    };
-    DIPROPGUIDANDPATH prop_guid_path =
-    {
-        .diph =
-        {
-            .dwSize = sizeof(DIPROPGUIDANDPATH),
-            .dwHeaderSize = sizeof(DIPROPHEADER),
-            .dwHow = DIPH_DEVICE,
-        },
-    };
-    DIPROPDWORD prop_dword =
-    {
-        .diph =
-        {
-            .dwSize = sizeof(DIPROPDWORD),
-            .dwHeaderSize = sizeof(DIPROPHEADER),
-            .dwHow = DIPH_DEVICE,
-        },
-    };
-    DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
-    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
-    IDirectInputDevice8W *device;
-    IDirectInputEffect *effect, *effect2;
-    HANDLE file, event;
-    ULONG res, ref;
-    DIEFFECT desc;
-    DWORD flags;
-    HRESULT hr;
-    HWND hwnd;
-
-    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
-    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
-    SetCurrentDirectoryW( tempdir );
-
-    cleanup_registry_keys();
-    if (!dinput_driver_start( report_descriptor, sizeof(report_descriptor), &hid_caps,
-                              expect_pool, sizeof(expect_pool) )) goto done;
-    if (FAILED(hr = create_dinput_device( DIRECTINPUT_VERSION, &devinst, &device ))) goto done;
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
-    file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
-                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
-                        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
-    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
-
-    hwnd = CreateWindowW( L"static", L"dinput", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200,
-                          NULL, NULL, NULL, NULL );
-
-    event = CreateEventW( NULL, FALSE, FALSE, NULL );
-    ok( event != NULL, "CreateEventW failed, last error %u\n", GetLastError() );
-    hr = IDirectInputDevice8_SetEventNotification( device, event );
-    ok( hr == DI_OK, "SetEventNotification returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE );
-    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetForceFeedbackState returned %#x\n", hr );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_RESET );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "SendForceFeedbackCommand returned %#x\n", hr );
-
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    wait_hid_expect( file, 100 );
-
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    ok( prop_dword.dwData == 0, "got DIPROP_FFLOAD %#x\n", prop_dword.dwData );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED|DIGFFS_EMPTY;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    ok( prop_dword.dwData == 0, "got DIPROP_FFLOAD %#x\n", prop_dword.dwData );
-    set_hid_expect( file, NULL, 0 );
-
-    send_hid_input( file, device_state_input, sizeof(struct hid_expect) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_TIMEOUT, "WaitForSingleObject returned %#x\n", res );
-    send_hid_input( file, device_state_input, sizeof(device_state_input) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
-
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_PAUSED|DIGFFS_EMPTY|DIGFFS_ACTUATORSON|DIGFFS_POWERON|DIGFFS_SAFETYSWITCHON|DIGFFS_USERFFSWITCHON;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, NULL, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-
-    hr = IDirectInputEffect_GetEffectStatus( effect, NULL );
-    ok( hr == E_POINTER, "GetEffectStatus returned %#x\n", hr );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DIERR_NOTDOWNLOADED, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-
-    flags = DIEP_ALLPARAMS;
-    hr = IDirectInputEffect_SetParameters( effect, &expect_desc, flags | DIEP_NODOWNLOAD );
-    ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr );
-
-    set_hid_expect( file, expect_reset, sizeof(struct hid_expect) );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetEffectStatus returned %#x\n", hr );
-
-    set_hid_expect( file, expect_acquire, sizeof(expect_acquire) );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-    wait_hid_expect( file, 100 );
-
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DIERR_NOTDOWNLOADED, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED|DIGFFS_EMPTY;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_create, sizeof(expect_create) );
-    hr = IDirectInputEffect_Download( effect );
-    ok( hr == DI_OK, "Download returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    prop_dword.dwData = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
-    ok( prop_dword.dwData == 0, "got DIPROP_FFLOAD %#x\n", prop_dword.dwData );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_start, sizeof(expect_start) );
-    hr = IDirectInputEffect_Start( effect, 1, DIES_NODOWNLOAD );
-    ok( hr == DI_OK, "Start returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_create_2, sizeof(expect_create_2) );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &expect_desc, &effect2, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    set_hid_expect( file, &expect_start_2, sizeof(expect_start_2) );
-    hr = IDirectInputEffect_Start( effect2, 1, DIES_SOLO );
-    ok( hr == DI_OK, "Start returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect2, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, &expect_stop_2, sizeof(expect_stop_2) );
-    hr = IDirectInputEffect_Stop( effect2 );
-    ok( hr == DI_OK, "Stop returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect2, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-    set_hid_expect( file, expect_destroy_2, sizeof(expect_destroy_2) );
-    ref = IDirectInputEffect_Release( effect2 );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, NULL, 0 );
-
-    /* sending commands has no direct effect on status */
-    set_hid_expect( file, expect_stop_all, sizeof(expect_stop_all) );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_STOPALL );
-    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_device_pause, sizeof(expect_device_pause) );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_PAUSE );
-    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_device_continue, sizeof(expect_device_continue) );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_CONTINUE );
-    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_disable_actuators, sizeof(expect_disable_actuators) );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_SETACTUATORSOFF );
-    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_enable_actuators, sizeof(expect_enable_actuators) );
-    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_SETACTUATORSON );
-    ok( hr == DI_OK, "SendForceFeedbackCommand returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_stop, sizeof(expect_stop) );
-    hr = IDirectInputEffect_Stop( effect );
-    ok( hr == DI_OK, "Stop returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_STOPPED;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    send_hid_input( file, device_state_input_0, sizeof(device_state_input_0) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_PAUSED|DIGFFS_ACTUATORSON|DIGFFS_POWERON|DIGFFS_SAFETYSWITCHON|DIGFFS_USERFFSWITCHON;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    send_hid_input( file, device_state_input_1, sizeof(device_state_input_1) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_ACTUATORSOFF|DIGFFS_POWEROFF|DIGFFS_SAFETYSWITCHOFF|DIGFFS_USERFFSWITCHOFF;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    send_hid_input( file, device_state_input_2, sizeof(device_state_input_2) );
-    res = WaitForSingleObject( event, 100 );
-    ok( res == WAIT_OBJECT_0, "WaitForSingleObject returned %#x\n", res );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_PAUSED|DIGFFS_ACTUATORSON|DIGFFS_POWEROFF|DIGFFS_SAFETYSWITCHOFF|DIGFFS_USERFFSWITCHOFF;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, &expect_stop, sizeof(expect_stop) );
-    hr = IDirectInputEffect_Stop( effect );
-    ok( hr == DI_OK, "Stop returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_PAUSED|DIGFFS_ACTUATORSON|DIGFFS_POWEROFF|DIGFFS_SAFETYSWITCHOFF|DIGFFS_USERFFSWITCHOFF;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_destroy, sizeof(expect_destroy) );
-    hr = IDirectInputEffect_Unload( effect );
-    ok( hr == DI_OK, "Unload returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DIERR_NOTDOWNLOADED, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-    set_hid_expect( file, expect_pool, sizeof(struct hid_expect) );
-    res = 0xdeadbeef;
-    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
-    ok( hr == DI_OK, "GetForceFeedbackState returned %#x\n", hr );
-    flags = DIGFFS_EMPTY|DIGFFS_PAUSED|DIGFFS_ACTUATORSON|DIGFFS_POWEROFF|DIGFFS_SAFETYSWITCHOFF|DIGFFS_USERFFSWITCHOFF;
-    ok( res == flags, "got state %#x\n", res );
-    set_hid_expect( file, NULL, 0 );
-
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-
-    /* start delay has no direct effect on effect status */
-    desc = expect_desc;
-    desc.dwStartDelay = 32767000;
-    set_hid_expect( file, expect_create_delay, sizeof(expect_create_delay) );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-    set_hid_expect( file, &expect_start, sizeof(expect_start) );
-    hr = IDirectInputEffect_Start( effect, 1, 0 );
-    ok( hr == DI_OK, "Start returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, expect_destroy, sizeof(expect_destroy) );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, NULL, 0 );
-
-    /* duration has no direct effect on effect status */
-    desc = expect_desc;
-    desc.dwDuration = 100;
-    desc.dwStartDelay = 0;
-    set_hid_expect( file, expect_create_duration, sizeof(expect_create_duration) );
-    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL );
-    ok( hr == DI_OK, "CreateEffect returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == 0, "got status %#x\n", res );
-    set_hid_expect( file, &expect_start, sizeof(expect_start) );
-    hr = IDirectInputEffect_Start( effect, 1, 0 );
-    ok( hr == DI_OK, "Start returned %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-    Sleep( 100 );
-    res = 0xdeadbeef;
-    hr = IDirectInputEffect_GetEffectStatus( effect, &res );
-    ok( hr == DI_OK, "GetEffectStatus returned %#x\n", hr );
-    ok( res == DIEGES_PLAYING, "got status %#x\n", res );
-    set_hid_expect( file, expect_destroy, sizeof(expect_destroy) );
-    ref = IDirectInputEffect_Release( effect );
-    ok( ref == 0, "Release returned %d\n", ref );
-    set_hid_expect( file, NULL, 0 );
-
-    set_hid_expect( file, expect_reset, sizeof(struct hid_expect) );
-    hr = IDirectInputDevice8_Unacquire( device );
-    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
-    set_hid_expect( file, NULL, 0 );
-
-    ref = IDirectInputDevice8_Release( device );
-    ok( ref == 0, "Release returned %d\n", ref );
-
-    DestroyWindow( hwnd );
-    CloseHandle( event );
-    CloseHandle( file );
-
-done:
-    pnp_driver_stop();
-    cleanup_registry_keys();
-    SetCurrentDirectoryW( cwd );
-    winetest_pop_context();
-}
-
-static void test_winmm_joystick(void)
-{
-#include "psh_hid_macros.h"
-    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, Report),
-                REPORT_ID(1, 1),
-
-                USAGE(1, HID_USAGE_GENERIC_X),
-                USAGE(1, HID_USAGE_GENERIC_Y),
-                USAGE(1, HID_USAGE_GENERIC_Z),
-                USAGE(1, HID_USAGE_GENERIC_WHEEL),
-                USAGE(1, HID_USAGE_GENERIC_SLIDER),
-                USAGE(1, HID_USAGE_GENERIC_RX),
-                USAGE(1, HID_USAGE_GENERIC_RY),
-                USAGE(1, HID_USAGE_GENERIC_RZ),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(4, 0xffff),
-                PHYSICAL_MINIMUM(1, 1),
-                PHYSICAL_MAXIMUM(4, 0xffff),
-                REPORT_SIZE(1, 16),
-                REPORT_COUNT(1, 8),
-                INPUT(1, Data|Var|Abs),
-
-                USAGE(1, HID_USAGE_GENERIC_HATSWITCH),
-                LOGICAL_MINIMUM(1, 1),
-                LOGICAL_MAXIMUM(1, 8),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 8),
-                REPORT_SIZE(1, 4),
-                REPORT_COUNT(1, 1),
-                INPUT(1, Data|Var|Abs|Null),
-
-                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
-                USAGE_MINIMUM(1, 1),
-                USAGE_MAXIMUM(1, 4),
-                LOGICAL_MINIMUM(1, 0),
-                LOGICAL_MAXIMUM(1, 1),
-                PHYSICAL_MINIMUM(1, 0),
-                PHYSICAL_MAXIMUM(1, 1),
-                REPORT_SIZE(1, 1),
-                REPORT_COUNT(1, 4),
-                INPUT(1, Data|Var|Abs),
-            END_COLLECTION,
-        END_COLLECTION,
-    };
-#include "pop_hid_macros.h"
-
-    static const HIDP_CAPS hid_caps =
-    {
-        .InputReportByteLength = 18,
-    };
-    static const JOYCAPS2W expect_regcaps =
-    {
-        .szRegKey = L"DINPUT.DLL",
-    };
-    static const JOYCAPS2W expect_caps =
-    {
-        .wMid = 0x1209,
-        .wPid = 0x0001,
-        .szPname = L"Microsoft PC-joystick driver",
-        .wXmax = 0xffff,
-        .wYmax = 0xffff,
-        .wZmax = 0xffff,
-        .wNumButtons = 4,
-        .wPeriodMin = 10,
-        .wPeriodMax = 1000,
-        .wRmax = 0xffff,
-        .wUmax = 0xffff,
-        .wVmax = 0xffff,
-        .wCaps = JOYCAPS_HASZ|JOYCAPS_HASR|JOYCAPS_HASU|JOYCAPS_HASV|JOYCAPS_HASPOV|JOYCAPS_POV4DIR,
-        .wMaxAxes = 6,
-        .wNumAxes = 6,
-        .wMaxButtons = 32,
-        .szRegKey = L"DINPUT.DLL",
-    };
-    struct hid_expect injected_input[] =
-    {
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_buf = {1,0x00,0x00,0x00,0x08,0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x38,0xf1},
-        },
-        {
-            .code = IOCTL_HID_READ_REPORT,
-            .report_buf = {1,0x00,0x38,0x00,0x30,0x00,0x28,0x00,0x20,0x00,0x18,0x00,0x10,0x00,0x08,0x00,0x00,0x63},
-        },
-    };
-    static const JOYINFOEX expect_infoex[] =
-    {
-        {
-            .dwSize = sizeof(JOYINFOEX), .dwFlags = 0xff,
-            .dwXpos = 0x7fff, .dwYpos = 0x7fff, .dwZpos = 0x7fff, .dwRpos = 0x7fff, .dwUpos = 0x7fff, .dwVpos = 0x7fff,
-            .dwButtons = 0, .dwButtonNumber = 0, .dwPOV = 0xffff,
-            .dwReserved1 = 0xcdcdcdcd, .dwReserved2 = 0xcdcdcdcd,
-        },
-        {
-            .dwSize = sizeof(JOYINFOEX), .dwFlags = 0xff,
-            .dwXpos = 0, .dwYpos = 0x07ff, .dwZpos = 0x17ff, .dwRpos = 0x37ff, .dwUpos = 0x1fff, .dwVpos = 0x27ff,
-            .dwButtons = 0xf, .dwButtonNumber = 0x4, .dwPOV = 0,
-            .dwReserved1 = 0xcdcdcdcd, .dwReserved2 = 0xcdcdcdcd,
-        },
-        {
-            .dwSize = sizeof(JOYINFOEX), .dwFlags = 0xff,
-            .dwXpos = 0x37ff, .dwYpos = 0x2fff, .dwZpos = 0x1fff, .dwRpos = 0, .dwUpos = 0x17ff, .dwVpos = 0x0fff,
-            .dwButtons = 0x6, .dwButtonNumber = 0x2, .dwPOV = 0x2328,
-            .dwReserved1 = 0xcdcdcdcd, .dwReserved2 = 0xcdcdcdcd,
-        },
-    };
-    static const JOYINFO expect_info =
-    {
-        .wXpos = 0x7fff,
-        .wYpos = 0x7fff,
-        .wZpos = 0x7fff,
-        .wButtons = 0,
-    };
-    JOYINFOEX infoex = {.dwSize = sizeof(JOYINFOEX)};
-    DIPROPGUIDANDPATH prop_guid_path =
-    {
-        .diph =
-        {
-            .dwSize = sizeof(DIPROPGUIDANDPATH),
-            .dwHeaderSize = sizeof(DIPROPHEADER),
-            .dwHow = DIPH_DEVICE,
-        },
-    };
-    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
-    DIDEVICEINSTANCEW devinst = {0};
-    IDirectInputDevice8W *device;
-    JOYCAPS2W caps = {0};
-    JOYINFO info = {0};
-    HANDLE event, file;
-    HRESULT hr;
-    UINT ret;
-
-    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
-    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
-    SetCurrentDirectoryW( tempdir );
-
-    cleanup_registry_keys();
-
-    ret = joyGetNumDevs();
-    ok( ret == 16, "joyGetNumDevs returned %u\n", ret );
-
-    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) );
-    ok( ret == JOYERR_PARMS, "joyGetDevCapsW returned %u\n", ret );
-
-    memset( &caps, 0xcd, sizeof(caps) );
-    ret = joyGetDevCapsW( -1, (JOYCAPSW *)&caps, sizeof(caps) );
-    ok( ret == 0, "joyGetDevCapsW returned %u\n", ret );
-    check_member( caps, expect_regcaps, "%#x", wMid );
-    check_member( caps, expect_regcaps, "%#x", wPid );
-    check_member_wstr( caps, expect_regcaps, szPname );
-    check_member( caps, expect_regcaps, "%#x", wXmin );
-    check_member( caps, expect_regcaps, "%#x", wXmax );
-    check_member( caps, expect_regcaps, "%#x", wYmin );
-    check_member( caps, expect_regcaps, "%#x", wYmax );
-    check_member( caps, expect_regcaps, "%#x", wZmin );
-    check_member( caps, expect_regcaps, "%#x", wZmax );
-    check_member( caps, expect_regcaps, "%#x", wNumButtons );
-    check_member( caps, expect_regcaps, "%#x", wPeriodMin );
-    check_member( caps, expect_regcaps, "%#x", wPeriodMax );
-    check_member( caps, expect_regcaps, "%#x", wRmin );
-    check_member( caps, expect_regcaps, "%#x", wRmax );
-    check_member( caps, expect_regcaps, "%#x", wUmin );
-    check_member( caps, expect_regcaps, "%#x", wUmax );
-    check_member( caps, expect_regcaps, "%#x", wVmin );
-    check_member( caps, expect_regcaps, "%#x", wVmax );
-    check_member( caps, expect_regcaps, "%#x", wCaps );
-    check_member( caps, expect_regcaps, "%#x", wMaxAxes );
-    check_member( caps, expect_regcaps, "%#x", wNumAxes );
-    check_member( caps, expect_regcaps, "%#x", wMaxButtons );
-    check_member_wstr( caps, expect_regcaps, szRegKey );
-    check_member_wstr( caps, expect_regcaps, szOEMVxD );
-    check_member_guid( caps, expect_regcaps, ManufacturerGuid );
-    check_member_guid( caps, expect_regcaps, ProductGuid );
-    check_member_guid( caps, expect_regcaps, NameGuid );
-
-    if (!dinput_driver_start( report_desc, sizeof(report_desc), &hid_caps, NULL, 0 )) goto done;
-
-    ret = joyGetNumDevs();
-    ok( ret == 16, "joyGetNumDevs returned %u\n", ret );
-
-    ret = joyGetPosEx( 1, &infoex );
-    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
-    ret = joyGetPosEx( 0, &infoex );
-    /* first call for an index sometimes fail */
-    if (ret == JOYERR_PARMS) ret = joyGetPosEx( 0, &infoex );
-    ok( ret == 0, "joyGetPosEx returned %u\n", ret );
-
-    ret = joyGetDevCapsW( 1, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) );
-    ok( ret == JOYERR_PARMS, "joyGetDevCapsW returned %u\n", ret );
-
-    memset( &caps, 0xcd, sizeof(caps) );
-    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(caps) );
-    ok( ret == 0, "joyGetDevCapsW returned %u\n", ret );
-    check_member( caps, expect_caps, "%#x", wMid );
-    check_member( caps, expect_caps, "%#x", wPid );
-    todo_wine
-    check_member_wstr( caps, expect_caps, szPname );
-    check_member( caps, expect_caps, "%#x", wXmin );
-    check_member( caps, expect_caps, "%#x", wXmax );
-    check_member( caps, expect_caps, "%#x", wYmin );
-    check_member( caps, expect_caps, "%#x", wYmax );
-    check_member( caps, expect_caps, "%#x", wZmin );
-    check_member( caps, expect_caps, "%#x", wZmax );
-    check_member( caps, expect_caps, "%#x", wNumButtons );
-    check_member( caps, expect_caps, "%#x", wPeriodMin );
-    check_member( caps, expect_caps, "%#x", wPeriodMax );
-    check_member( caps, expect_caps, "%#x", wRmin );
-    check_member( caps, expect_caps, "%#x", wRmax );
-    check_member( caps, expect_caps, "%#x", wUmin );
-    check_member( caps, expect_caps, "%#x", wUmax );
-    check_member( caps, expect_caps, "%#x", wVmin );
-    check_member( caps, expect_caps, "%#x", wVmax );
-    check_member( caps, expect_caps, "%#x", wCaps );
-    check_member( caps, expect_caps, "%#x", wMaxAxes );
-    check_member( caps, expect_caps, "%#x", wNumAxes );
-    check_member( caps, expect_caps, "%#x", wMaxButtons );
-    check_member_wstr( caps, expect_caps, szRegKey );
-    check_member_wstr( caps, expect_caps, szOEMVxD );
-    check_member_guid( caps, expect_caps, ManufacturerGuid );
-    check_member_guid( caps, expect_caps, ProductGuid );
-    check_member_guid( caps, expect_caps, NameGuid );
-
-    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) );
-    ok( ret == 0, "joyGetDevCapsW returned %u\n", ret );
-    ret = joyGetDevCapsW( 0, NULL, sizeof(JOYCAPSW) );
-    ok( ret == MMSYSERR_INVALPARAM, "joyGetDevCapsW returned %u\n", ret );
-    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) + 4 );
-    ok( ret == JOYERR_PARMS, "joyGetDevCapsW returned %u\n", ret );
-    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) - 4 );
-    ok( ret == JOYERR_PARMS, "joyGetDevCapsW returned %u\n", ret );
-
-    infoex.dwSize = sizeof(JOYINFOEX);
-    infoex.dwFlags = JOY_RETURNALL;
-    ret = joyGetPosEx( -1, &infoex );
-    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
-    ret = joyGetPosEx( 1, &infoex );
-    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
-    ret = joyGetPosEx( 16, &infoex );
-    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
-
-    memset( &infoex, 0xcd, sizeof(infoex) );
-    infoex.dwSize = sizeof(JOYINFOEX);
-    infoex.dwFlags = JOY_RETURNALL;
-    ret = joyGetPosEx( 0, &infoex );
-    ok( ret == 0, "joyGetPosEx returned %u\n", ret );
-    check_member( infoex, expect_infoex[0], "%#x", dwSize );
-    check_member( infoex, expect_infoex[0], "%#x", dwFlags );
-    check_member( infoex, expect_infoex[0], "%#x", dwXpos );
-    check_member( infoex, expect_infoex[0], "%#x", dwYpos );
-    check_member( infoex, expect_infoex[0], "%#x", dwZpos );
-    check_member( infoex, expect_infoex[0], "%#x", dwRpos );
-    check_member( infoex, expect_infoex[0], "%#x", dwUpos );
-    check_member( infoex, expect_infoex[0], "%#x", dwVpos );
-    check_member( infoex, expect_infoex[0], "%#x", dwButtons );
-    check_member( infoex, expect_infoex[0], "%#x", dwButtonNumber );
-    check_member( infoex, expect_infoex[0], "%#x", dwPOV );
-    check_member( infoex, expect_infoex[0], "%#x", dwReserved1 );
-    check_member( infoex, expect_infoex[0], "%#x", dwReserved2 );
-
-    infoex.dwSize = sizeof(JOYINFOEX) - 4;
-    ret = joyGetPosEx( 0, &infoex );
-    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
-
-    ret = joyGetPos( -1, &info );
-    ok( ret == JOYERR_PARMS, "joyGetPos returned %u\n", ret );
-    ret = joyGetPos( 1, &info );
-    ok( ret == JOYERR_PARMS, "joyGetPos returned %u\n", ret );
-    memset( &info, 0xcd, sizeof(info) );
-    ret = joyGetPos( 0, &info );
-    ok( ret == 0, "joyGetPos returned %u\n", ret );
-    check_member( info, expect_info, "%#x", wXpos );
-    check_member( info, expect_info, "%#x", wYpos );
-    check_member( info, expect_info, "%#x", wZpos );
-    check_member( info, expect_info, "%#x", wButtons );
-
-    if (FAILED(hr = create_dinput_device( DIRECTINPUT_VERSION, &devinst, &device ))) goto done;
-
-    event = CreateEventW( NULL, FALSE, FALSE, NULL );
-    ok( event != NULL, "CreateEventW failed, last error %u\n", GetLastError() );
-    hr = IDirectInputDevice8_SetEventNotification( device, event );
-    ok( hr == DI_OK, "SetEventNotification returned: %#x\n", hr );
-
-    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
-    ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
-    file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
-                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
-                        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
-    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
-
-    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
-    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
-    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE );
-    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
-    hr = IDirectInputDevice8_Acquire( device );
-    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
-
-    send_hid_input( file, &injected_input[0], sizeof(struct hid_expect) );
-    ret = WaitForSingleObject( event, 100 );
-    ok( ret != WAIT_TIMEOUT, "WaitForSingleObject returned %#x\n", ret );
-    Sleep( 50 ); /* leave some time for winmm to keep up */
-
-    memset( &infoex, 0xcd, sizeof(infoex) );
-    infoex.dwSize = sizeof(JOYINFOEX);
-    infoex.dwFlags = JOY_RETURNALL;
-    ret = joyGetPosEx( 0, &infoex );
-    ok( ret == 0, "joyGetPosEx returned %u\n", ret );
-    check_member( infoex, expect_infoex[1], "%#x", dwSize );
-    check_member( infoex, expect_infoex[1], "%#x", dwFlags );
-    check_member( infoex, expect_infoex[1], "%#x", dwXpos );
-    check_member( infoex, expect_infoex[1], "%#x", dwYpos );
-    check_member( infoex, expect_infoex[1], "%#x", dwZpos );
-    check_member( infoex, expect_infoex[1], "%#x", dwRpos );
-    check_member( infoex, expect_infoex[1], "%#x", dwUpos );
-    check_member( infoex, expect_infoex[1], "%#x", dwVpos );
-    check_member( infoex, expect_infoex[1], "%#x", dwButtons );
-    check_member( infoex, expect_infoex[1], "%#x", dwButtonNumber );
-    check_member( infoex, expect_infoex[1], "%#x", dwPOV );
-
-    send_hid_input( file, &injected_input[1], sizeof(struct hid_expect) );
-    ret = WaitForSingleObject( event, 100 );
-    ok( ret != WAIT_TIMEOUT, "WaitForSingleObject returned %#x\n", ret );
-    Sleep( 50 ); /* leave some time for winmm to keep up */
-
-    memset( &infoex, 0xcd, sizeof(infoex) );
-    infoex.dwSize = sizeof(JOYINFOEX);
-    infoex.dwFlags = JOY_RETURNALL;
-    ret = joyGetPosEx( 0, &infoex );
-    ok( ret == 0, "joyGetPosEx returned %u\n", ret );
-    check_member( infoex, expect_infoex[2], "%#x", dwSize );
-    check_member( infoex, expect_infoex[2], "%#x", dwFlags );
-    check_member( infoex, expect_infoex[2], "%#x", dwXpos );
-    check_member( infoex, expect_infoex[2], "%#x", dwYpos );
-    check_member( infoex, expect_infoex[2], "%#x", dwZpos );
-    check_member( infoex, expect_infoex[2], "%#x", dwRpos );
-    check_member( infoex, expect_infoex[2], "%#x", dwUpos );
-    check_member( infoex, expect_infoex[2], "%#x", dwVpos );
-    check_member( infoex, expect_infoex[2], "%#x", dwButtons );
-    check_member( infoex, expect_infoex[2], "%#x", dwButtonNumber );
-    check_member( infoex, expect_infoex[2], "%#x", dwPOV );
-
-    ret = IDirectInputDevice8_Release( device );
-    ok( ret == 0, "Release returned %d\n", ret );
-
-    CloseHandle( event );
-    CloseHandle( file );
-
-done:
-    pnp_driver_stop();
-    cleanup_registry_keys();
-    SetCurrentDirectoryW( cwd );
-}
-
 START_TEST( hid )
 {
-    HANDLE mapping;
-    BOOL is_wow64;
+    if (!dinput_test_init()) return;
 
-    instance = GetModuleHandleW( NULL );
-    localized = GetUserDefaultLCID() != MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
-    pSignerSign = (void *)GetProcAddress( LoadLibraryW( L"mssign32" ), "SignerSign" );
-
-    if (IsWow64Process( GetCurrentProcess(), &is_wow64 ) && is_wow64)
-    {
-        skip( "Running in WoW64.\n" );
-        return;
-    }
-
-    mapping = CreateFileMappingW( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(*test_data),
-                                  L"Global\\winetest_dinput_section" );
-    if (!mapping && GetLastError() == ERROR_ACCESS_DENIED)
-    {
-        win_skip( "Failed to create test data mapping.\n" );
-        return;
-    }
-    ok( !!mapping, "got error %u\n", GetLastError() );
-    test_data = MapViewOfFile( mapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 1024 );
-    test_data->running_under_wine = !strcmp( winetest_platform, "wine" );
-    test_data->winetest_report_success = winetest_report_success;
-    test_data->winetest_debug = winetest_debug;
-
-    okfile = CreateFileW( L"C:\\windows\\winetest_dinput_okfile", GENERIC_READ | GENERIC_WRITE,
-                          FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL );
-    ok( okfile != INVALID_HANDLE_VALUE, "failed to create file, error %u\n", GetLastError() );
-
-    subtest( "driver_hid" );
     test_hidp_kdr();
     test_hid_driver( 0, FALSE );
     test_hid_driver( 1, FALSE );
     test_hid_driver( 0, TRUE );
     test_hid_driver( 1, TRUE );
 
-    CoInitialize( NULL );
-    if (test_device_types( 0x800 ))
-    {
-        /* This needs to be done before doing anything involving dinput.dll
-         * on Windows, or the tests will fail, dinput8.dll is fine though. */
-        test_winmm_joystick();
-
-        test_device_types( 0x500 );
-        test_device_types( 0x700 );
-
-        test_simple_joystick();
-        test_force_feedback_joystick( 0x500 );
-        test_force_feedback_joystick( 0x700 );
-        test_force_feedback_joystick( 0x800 );
-
-        test_device_managed_effect();
-    }
-    CoUninitialize();
-
-    UnmapViewOfFile( test_data );
-    CloseHandle( mapping );
-    CloseHandle( okfile );
-    DeleteFileW( L"C:\\windows\\winetest_dinput_okfile" );
+    dinput_test_exit();
 }
diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c
new file mode 100644
index 00000000000..f1fd4be36e5
--- /dev/null
+++ b/dlls/dinput/tests/joystick8.c
@@ -0,0 +1,2594 @@
+/*
+ * Copyright 2022 Rémi Bernon for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define DIRECTINPUT_VERSION 0x0800
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+
+#define COBJMACROS
+#include "dinput.h"
+#include "dinputd.h"
+#include "devguid.h"
+#include "mmsystem.h"
+
+#include "wine/hid.h"
+
+#include "dinput_test.h"
+
+struct check_objects_todos
+{
+    BOOL type;
+    BOOL guid;
+    BOOL usage;
+    BOOL name;
+};
+
+struct check_objects_params
+{
+    DWORD version;
+    UINT index;
+    UINT expect_count;
+    const DIDEVICEOBJECTINSTANCEW *expect_objs;
+    const struct check_objects_todos *todo_objs;
+    BOOL todo_extra;
+};
+
+static BOOL CALLBACK check_objects( const DIDEVICEOBJECTINSTANCEW *obj, void *args )
+{
+    static const DIDEVICEOBJECTINSTANCEW unexpected_obj = {0};
+    static const struct check_objects_todos todo_none = {0};
+    struct check_objects_params *params = args;
+    const DIDEVICEOBJECTINSTANCEW *exp = params->expect_objs + params->index;
+    const struct check_objects_todos *todo;
+
+    if (!params->todo_objs) todo = &todo_none;
+    else todo = params->todo_objs + params->index;
+
+    todo_wine_if( params->todo_extra && params->index >= params->expect_count )
+    ok( params->index < params->expect_count, "unexpected extra object\n" );
+    if (params->index >= params->expect_count) return DIENUM_STOP;
+
+    winetest_push_context( "obj[%d]", params->index );
+
+    ok( params->index < params->expect_count, "unexpected extra object\n" );
+    if (params->index >= params->expect_count) exp = &unexpected_obj;
+
+    check_member( *obj, *exp, "%u", dwSize );
+    todo_wine_if( todo->guid )
+    check_member_guid( *obj, *exp, guidType );
+    todo_wine_if( params->version < 0x700 && (obj->dwType & DIDFT_BUTTON) )
+    check_member( *obj, *exp, "%#x", dwOfs );
+    todo_wine_if( todo->type )
+    check_member( *obj, *exp, "%#x", dwType );
+    check_member( *obj, *exp, "%#x", dwFlags );
+    if (!localized) todo_wine_if( todo->name )check_member_wstr( *obj, *exp, tszName );
+    check_member( *obj, *exp, "%u", dwFFMaxForce );
+    check_member( *obj, *exp, "%u", dwFFForceResolution );
+    check_member( *obj, *exp, "%u", wCollectionNumber );
+    check_member( *obj, *exp, "%u", wDesignatorIndex );
+    check_member( *obj, *exp, "%#04x", wUsagePage );
+    todo_wine_if( todo->usage )
+    check_member( *obj, *exp, "%#04x", wUsage );
+    check_member( *obj, *exp, "%#04x", dwDimension );
+    check_member( *obj, *exp, "%#04x", wExponent );
+    check_member( *obj, *exp, "%u", wReportId );
+
+    winetest_pop_context();
+
+    params->index++;
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK check_object_count( const DIDEVICEOBJECTINSTANCEW *obj, void *args )
+{
+    DWORD *count = args;
+    *count = *count + 1;
+    return DIENUM_CONTINUE;
+}
+
+struct check_effects_params
+{
+    UINT index;
+    UINT expect_count;
+    const DIEFFECTINFOW *expect_effects;
+};
+
+static BOOL CALLBACK check_effects( const DIEFFECTINFOW *effect, void *args )
+{
+    static const DIEFFECTINFOW unexpected_effect = {0};
+    struct check_effects_params *params = args;
+    const DIEFFECTINFOW *exp = params->expect_effects + params->index;
+
+    winetest_push_context( "effect[%d]", params->index );
+
+    ok( params->index < params->expect_count, "unexpected extra object\n" );
+    if (params->index >= params->expect_count) exp = &unexpected_effect;
+
+    check_member( *effect, *exp, "%u", dwSize );
+    check_member_guid( *effect, *exp, guid );
+    check_member( *effect, *exp, "%#x", dwEffType );
+    check_member( *effect, *exp, "%#x", dwStaticParams );
+    check_member( *effect, *exp, "%#x", dwDynamicParams );
+    check_member_wstr( *effect, *exp, tszName );
+
+    winetest_pop_context();
+    params->index++;
+
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK check_effect_count( const DIEFFECTINFOW *effect, void *args )
+{
+    DWORD *count = args;
+    *count = *count + 1;
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK check_no_created_effect_objects( IDirectInputEffect *effect, void *context )
+{
+    ok( 0, "unexpected effect %p\n", effect );
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK enum_device_count( const DIDEVICEINSTANCEW *devinst, void *context )
+{
+    DWORD *count = context;
+    *count = *count + 1;
+    return DIENUM_CONTINUE;
+}
+
+static void check_dinput_devices( DWORD version, DIDEVICEINSTANCEW *devinst )
+{
+    DIPROPDWORD prop_dword =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPDWORD),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    IDirectInputDevice8W *device;
+    IDirectInput8W *di8;
+    IDirectInputW *di;
+    ULONG ref, count;
+    HRESULT hr;
+
+    if (version >= 0x800)
+    {
+        hr = DirectInput8Create( instance, version, &IID_IDirectInput8W, (void **)&di8, NULL );
+        ok( hr == DI_OK, "DirectInput8Create returned %#x\n", hr );
+
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, NULL, NULL, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, enum_device_count, &count, 0xdeadbeef );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput8_EnumDevices( di8, 0xdeadbeef, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 3, "got count %u, expected 0\n", count );
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_DEVICE, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 0, "got count %u, expected 0\n", count );
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_POINTER, enum_device_count, &count,
+                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        todo_wine
+        ok( count == 3, "got count %u, expected 3\n", count );
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_KEYBOARD, enum_device_count, &count,
+                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        todo_wine
+        ok( count == 3, "got count %u, expected 3\n", count );
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_GAMECTRL, enum_device_count, &count,
+                                        DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS | DIEDFL_INCLUDEHIDDEN );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_FORCEFEEDBACK );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        if (IsEqualGUID( &devinst->guidFFDriver, &GUID_NULL )) ok( count == 0, "got count %u, expected 0\n", count );
+        else ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput8_EnumDevices( di8, (devinst->dwDevType & 0xff) + 1, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        if ((devinst->dwDevType & 0xff) != DI8DEVTYPE_SUPPLEMENTAL) ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        else ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        ok( count == 0, "got count %u, expected 0\n", count );
+
+        hr = IDirectInput8_CreateDevice( di8, &devinst->guidInstance, NULL, NULL );
+        ok( hr == E_POINTER, "CreateDevice returned %#x\n", hr );
+        hr = IDirectInput8_CreateDevice( di8, NULL, &device, NULL );
+        ok( hr == E_POINTER, "CreateDevice returned %#x\n", hr );
+        hr = IDirectInput8_CreateDevice( di8, &GUID_NULL, &device, NULL );
+        ok( hr == DIERR_DEVICENOTREG, "CreateDevice returned %#x\n", hr );
+
+        hr = IDirectInput8_CreateDevice( di8, &devinst->guidInstance, &device, NULL );
+        ok( hr == DI_OK, "CreateDevice returned %#x\n", hr );
+
+        prop_dword.dwData = 0xdeadbeef;
+        hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, &prop_dword.diph );
+        ok( hr == DI_OK, "GetProperty DIPROP_VIDPID returned %#x\n", hr );
+        /* Wine may get the wrong device here, because the test driver creates another instance of
+           hidclass.sys, and gets duplicate rawinput handles, which we use in the guidInstance */
+        todo_wine_if( prop_dword.dwData != EXPECT_VIDPID )
+        ok( prop_dword.dwData == EXPECT_VIDPID, "got %#x expected %#x\n", prop_dword.dwData, EXPECT_VIDPID );
+
+        ref = IDirectInputDevice8_Release( device );
+        ok( ref == 0, "Release returned %d\n", ref );
+    }
+    else
+    {
+        hr = DirectInputCreateEx( instance, version, &IID_IDirectInput2W, (void **)&di, NULL );
+        ok( hr == DI_OK, "DirectInputCreateEx returned %#x\n", hr );
+
+        hr = IDirectInput_EnumDevices( di, 0, NULL, NULL, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, 0xdeadbeef );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput_EnumDevices( di, 0xdeadbeef, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, DIEDFL_INCLUDEHIDDEN );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, 0, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 3, "got count %u, expected 0\n", count );
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_DEVICE, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 0, "got count %u, expected 0\n", count );
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_MOUSE, enum_device_count, &count,
+                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        todo_wine
+        ok( count == 3, "got count %u, expected 3\n", count );
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_KEYBOARD, enum_device_count, &count,
+                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        todo_wine
+        ok( count == 3, "got count %u, expected 3\n", count );
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, DIDEVTYPE_JOYSTICK, enum_device_count, &count,
+                                       DIEDFL_INCLUDEALIASES | DIEDFL_INCLUDEPHANTOMS );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        ok( count == 1, "got count %u, expected 1\n", count );
+
+        count = 0;
+        hr = IDirectInput_EnumDevices( di, (devinst->dwDevType & 0xff), enum_device_count, &count, DIEDFL_FORCEFEEDBACK );
+        ok( hr == DI_OK, "EnumDevices returned: %#x\n", hr );
+        if (IsEqualGUID( &devinst->guidFFDriver, &GUID_NULL )) ok( count == 0, "got count %u, expected 0\n", count );
+        else ok( count == 1, "got count %u, expected 1\n", count );
+
+        hr = IDirectInput_EnumDevices( di, 0x14, enum_device_count, &count, DIEDFL_ALLDEVICES );
+        ok( hr == DIERR_INVALIDPARAM, "EnumDevices returned: %#x\n", hr );
+    }
+}
+
+static void test_simple_joystick(void)
+{
+#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, Report),
+                REPORT_ID(1, 1),
+
+                USAGE(1, HID_USAGE_GENERIC_WHEEL),
+                USAGE(4, (0xff01u<<16)|(0x1234)),
+                USAGE(1, HID_USAGE_GENERIC_X),
+                USAGE(1, HID_USAGE_GENERIC_Y),
+                USAGE(4, (HID_USAGE_PAGE_SIMULATION<<16)|HID_USAGE_SIMULATION_RUDDER),
+                USAGE(4, (HID_USAGE_PAGE_DIGITIZER<<16)|HID_USAGE_DIGITIZER_TIP_PRESSURE),
+                USAGE(4, (HID_USAGE_PAGE_CONSUMER<<16)|HID_USAGE_CONSUMER_VOLUME),
+                LOGICAL_MINIMUM(1, 0xe7),
+                LOGICAL_MAXIMUM(1, 0x38),
+                PHYSICAL_MINIMUM(1, 0xe7),
+                PHYSICAL_MAXIMUM(1, 0x38),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 7),
+                INPUT(1, Data|Var|Abs),
+
+                USAGE(1, HID_USAGE_GENERIC_HATSWITCH),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 8),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 8),
+                REPORT_SIZE(1, 4),
+                REPORT_COUNT(1, 1),
+                INPUT(1, Data|Var|Abs|Null),
+
+                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
+                USAGE_MINIMUM(1, 1),
+                USAGE_MAXIMUM(1, 2),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 4),
+                INPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+        END_COLLECTION,
+    };
+#undef REPORT_ID_OR_USAGE_PAGE
+#include "pop_hid_macros.h"
+
+    static const HIDP_CAPS hid_caps =
+    {
+        .InputReportByteLength = 9,
+    };
+    static const DIDEVCAPS expect_caps =
+    {
+        .dwSize = sizeof(DIDEVCAPS),
+        .dwFlags = DIDC_ATTACHED | DIDC_EMULATED,
+        .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK,
+        .dwAxes = 6,
+        .dwPOVs = 1,
+        .dwButtons = 2,
+    };
+    struct hid_expect injected_input[] =
+    {
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_buf = {1,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0},
+        },
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_buf = {1,0x10,0x10,0x38,0x38,0x10,0x10,0x10,0xf8},
+        },
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_buf = {1,0x10,0x10,0x01,0x01,0x10,0x10,0x10,0x00},
+        },
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_buf = {1,0x10,0x10,0x01,0x01,0x10,0x10,0x10,0x00},
+        },
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_buf = {1,0x10,0x10,0x80,0x80,0x10,0x10,0x10,0xff},
+        },
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_buf = {1,0x10,0x10,0x10,0xee,0x10,0x10,0x10,0x54},
+        },
+    };
+    static const struct DIJOYSTATE2 expect_state[] =
+    {
+        {.lX = 32767, .lY = 32767, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = 32767, .lY = 32767, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = 65535, .lY = 65535, .lZ = 32767, .rgdwPOV = {31500, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
+        {.lX = 20779, .lY = 20779, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = 20779, .lY = 20779, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = 0, .lY = 0, .lZ = 32767, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
+        {.lX = 32767, .lY = 5594, .lZ = 32767, .rgdwPOV = {13500, -1, -1, -1}, .rgbButtons = {0x80}},
+    };
+    static const struct DIJOYSTATE2 expect_state_abs[] =
+    {
+        {.lX = -9000, .lY = 26000, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = -9000, .lY = 26000, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = -4000, .lY = 51000, .lZ = 26000, .rgdwPOV = {31500, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
+        {.lX = -10667, .lY = 12905, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = -10667, .lY = 12905, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = -14000, .lY = 1000, .lZ = 26000, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
+        {.lX = -9000, .lY = 1000, .lZ = 26000, .rgdwPOV = {13500, -1, -1, -1}, .rgbButtons = {0x80}},
+    };
+    static const struct DIJOYSTATE2 expect_state_rel[] =
+    {
+        {.lX = 0, .lY = 0, .rgdwPOV = {13500, -1, -1, -1}, .rgbButtons = {0x80, 0}},
+        {.lX = 9016, .lY = -984, .lZ = -25984, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = 40, .lY = 40, .rgdwPOV = {31500, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
+        {.lX = -55, .lY = -55, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = 0, .lY = 0, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0, 0}},
+        {.lX = -129, .lY = -129, .rgdwPOV = {-1, -1, -1, -1}, .rgbButtons = {0x80, 0x80}},
+        {.lX = 144, .lY = 110, .rgdwPOV = {13500, -1, -1, -1}, .rgbButtons = {0x80}},
+    };
+    static const DIDEVICEOBJECTDATA expect_objdata[] =
+    {
+        {.dwOfs = 0x4, .dwData = 0xffff, .dwSequence = 0xa},
+        {.dwOfs = 0x4, .dwData = 0xffff, .dwSequence = 0xa},
+        {.dwOfs = 0, .dwData = 0xffff, .dwSequence = 0xa},
+        {.dwOfs = 0x20, .dwData = 31500, .dwSequence = 0xa},
+        {.dwOfs = 0x30, .dwData = 0x80, .dwSequence = 0xa},
+        {.dwOfs = 0x4, .dwData = 0x512b, .dwSequence = 0xd},
+        {.dwOfs = 0, .dwData = 0x512b, .dwSequence = 0xd},
+        {.dwOfs = 0x20, .dwData = -1, .dwSequence = 0xd},
+        {.dwOfs = 0x30, .dwData = 0, .dwSequence = 0xd},
+        {.dwOfs = 0x31, .dwData = 0, .dwSequence = 0xd},
+        {.dwOfs = 0x4, .dwData = 0, .dwSequence = 0xf},
+        {.dwOfs = 0, .dwData = 0, .dwSequence = 0xf},
+        {.dwOfs = 0x30, .dwData = 0x80, .dwSequence = 0xf},
+        {.dwOfs = 0x31, .dwData = 0x80, .dwSequence = 0xf},
+    };
+
+    const DIDEVICEINSTANCEW expect_devinst =
+    {
+        .dwSize = sizeof(DIDEVICEINSTANCEW),
+        .guidInstance = expect_guid_product,
+        .guidProduct = expect_guid_product,
+        .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK,
+        .tszInstanceName = L"Wine test root driver",
+        .tszProductName = L"Wine test root driver",
+        .guidFFDriver = GUID_NULL,
+        .wUsagePage = HID_USAGE_PAGE_GENERIC,
+        .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+    };
+    const DIDEVICEOBJECTINSTANCEW expect_objects[] =
+    {
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(6),
+            .tszName = L"Volume",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_CONSUMER,
+            .wUsage = HID_USAGE_CONSUMER_VOLUME,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwOfs = 0x4,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(7),
+            .tszName = L"Tip Pressure",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_DIGITIZER,
+            .wUsage = HID_USAGE_DIGITIZER_TIP_PRESSURE,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_RzAxis,
+            .dwOfs = 0x8,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(5),
+            .dwFlags = DIDOI_ASPECTPOSITION,
+            .tszName = L"Rudder",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_SIMULATION,
+            .wUsage = HID_USAGE_SIMULATION_RUDDER,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_YAxis,
+            .dwOfs = 0xc,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(1),
+            .dwFlags = DIDOI_ASPECTPOSITION,
+            .tszName = L"Y Axis",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_Y,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_XAxis,
+            .dwOfs = 0x10,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(0),
+            .dwFlags = DIDOI_ASPECTPOSITION,
+            .tszName = L"X Axis",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_X,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_ZAxis,
+            .dwOfs = 0x18,
+            .dwType = DIDFT_ABSAXIS|DIDFT_MAKEINSTANCE(2),
+            .dwFlags = DIDOI_ASPECTPOSITION,
+            .tszName = L"Wheel",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_WHEEL,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_POV,
+            .dwOfs = 0x1c,
+            .dwType = DIDFT_POV|DIDFT_MAKEINSTANCE(0),
+            .tszName = L"Hat Switch",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_HATSWITCH,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Button,
+            .dwOfs = 0x20,
+            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(0),
+            .tszName = L"Button 0",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_BUTTON,
+            .wUsage = 0x1,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Button,
+            .dwOfs = 0x21,
+            .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(1),
+            .tszName = L"Button 1",
+            .wCollectionNumber = 1,
+            .wUsagePage = HID_USAGE_PAGE_BUTTON,
+            .wUsage = 0x2,
+            .wReportId = 1,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(0),
+            .tszName = L"Collection 0 - Joystick",
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
+            .guidType = GUID_Unknown,
+            .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(1),
+            .tszName = L"Collection 1 - Joystick",
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+        },
+    };
+    const DIEFFECTINFOW expect_effects[] = {};
+
+    struct check_objects_params check_objects_params =
+    {
+        .version = DIRECTINPUT_VERSION,
+        .expect_count = ARRAY_SIZE(expect_objects),
+        .expect_objs = expect_objects,
+    };
+    struct check_effects_params check_effects_params =
+    {
+        .expect_count = ARRAY_SIZE(expect_effects),
+        .expect_effects = expect_effects,
+    };
+    DIPROPGUIDANDPATH prop_guid_path =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPGUIDANDPATH),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    DIPROPSTRING prop_string =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPSTRING),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    DIPROPDWORD prop_dword =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPDWORD),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    DIPROPRANGE prop_range =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPRANGE),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    DIPROPPOINTER prop_pointer =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPPOINTER),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+        },
+    };
+    DIOBJECTDATAFORMAT objdataformat[32] = {{0}};
+    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
+    DIDEVICEOBJECTDATA objdata[32] = {{0}};
+    DIDEVICEOBJECTINSTANCEW objinst = {0};
+    DIDEVICEINSTANCEW devinst = {0};
+    DIEFFECTINFOW effectinfo = {0};
+    DIDATAFORMAT dataformat = {0};
+    IDirectInputDevice8W *device;
+    IDirectInputEffect *effect;
+    DIEFFESCAPE escape = {0};
+    DIDEVCAPS caps = {0};
+    HANDLE event, file;
+    char buffer[1024];
+    DIJOYSTATE2 state;
+    ULONG i, res, ref;
+    HRESULT hr;
+    WCHAR *tmp;
+    GUID guid;
+    HWND hwnd;
+
+    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
+    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
+    SetCurrentDirectoryW( tempdir );
+
+    cleanup_registry_keys();
+    if (!dinput_driver_start( report_desc, sizeof(report_desc), &hid_caps, NULL, 0 )) goto done;
+    if (FAILED(hr = dinput_test_create_device( DIRECTINPUT_VERSION, &devinst, &device ))) goto done;
+
+    check_dinput_devices( DIRECTINPUT_VERSION, &devinst );
+
+    hr = IDirectInputDevice8_Initialize( device, instance, 0x0700, &GUID_NULL );
+    todo_wine
+    ok( hr == DIERR_BETADIRECTINPUTVERSION, "Initialize returned %#x\n", hr );
+    hr = IDirectInputDevice8_Initialize( device, instance, DIRECTINPUT_VERSION, NULL );
+    todo_wine
+    ok( hr == E_POINTER, "Initialize returned %#x\n", hr );
+    hr = IDirectInputDevice8_Initialize( device, NULL, DIRECTINPUT_VERSION, &GUID_NULL );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "Initialize returned %#x\n", hr );
+    hr = IDirectInputDevice8_Initialize( device, instance, DIRECTINPUT_VERSION, &GUID_NULL );
+    todo_wine
+    ok( hr == REGDB_E_CLASSNOTREG, "Initialize returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_Initialize( device, instance, DIRECTINPUT_VERSION, &devinst.guidInstance );
+    ok( hr == DI_OK, "Initialize returned %#x\n", hr );
+    guid = devinst.guidInstance;
+    memset( &devinst, 0, sizeof(devinst) );
+    devinst.dwSize = sizeof(DIDEVICEINSTANCEW);
+    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
+    ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
+    ok( IsEqualGUID( &guid, &devinst.guidInstance ), "got %s expected %s\n", debugstr_guid( &guid ),
+        debugstr_guid( &devinst.guidInstance ) );
+    hr = IDirectInputDevice8_Initialize( device, instance, DIRECTINPUT_VERSION, &devinst.guidProduct );
+    ok( hr == DI_OK, "Initialize returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_GetDeviceInfo( device, NULL );
+    ok( hr == E_POINTER, "GetDeviceInfo returned %#x\n", hr );
+    devinst.dwSize = sizeof(DIDEVICEINSTANCEW) + 1;
+    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
+    ok( hr == DIERR_INVALIDPARAM, "GetDeviceInfo returned %#x\n", hr );
+
+    devinst.dwSize = sizeof(DIDEVICEINSTANCE_DX3W);
+    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
+    ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
+    todo_wine
+    check_member_guid( devinst, expect_devinst, guidInstance );
+    check_member_guid( devinst, expect_devinst, guidProduct );
+    check_member( devinst, expect_devinst, "%#x", dwDevType );
+    todo_wine
+    check_member_wstr( devinst, expect_devinst, tszInstanceName );
+    todo_wine
+    check_member_wstr( devinst, expect_devinst, tszProductName );
+
+    memset( &devinst, 0, sizeof(devinst) );
+    devinst.dwSize = sizeof(DIDEVICEINSTANCEW);
+    hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
+    ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
+    check_member( devinst, expect_devinst, "%d", dwSize );
+    todo_wine
+    check_member_guid( devinst, expect_devinst, guidInstance );
+    check_member_guid( devinst, expect_devinst, guidProduct );
+    check_member( devinst, expect_devinst, "%#x", dwDevType );
+    todo_wine
+    check_member_wstr( devinst, expect_devinst, tszInstanceName );
+    todo_wine
+    check_member_wstr( devinst, expect_devinst, tszProductName );
+    check_member_guid( devinst, expect_devinst, guidFFDriver );
+    check_member( devinst, expect_devinst, "%04x", wUsagePage );
+    check_member( devinst, expect_devinst, "%04x", wUsage );
+
+    hr = IDirectInputDevice8_GetCapabilities( device, NULL );
+    ok( hr == E_POINTER, "GetCapabilities returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetCapabilities( device, &caps );
+    ok( hr == DIERR_INVALIDPARAM, "GetCapabilities returned %#x\n", hr );
+    caps.dwSize = sizeof(DIDEVCAPS);
+    hr = IDirectInputDevice8_GetCapabilities( device, &caps );
+    ok( hr == DI_OK, "GetCapabilities returned %#x\n", hr );
+    check_member( caps, expect_caps, "%d", dwSize );
+    check_member( caps, expect_caps, "%#x", dwFlags );
+    check_member( caps, expect_caps, "%#x", dwDevType );
+    check_member( caps, expect_caps, "%d", dwAxes );
+    check_member( caps, expect_caps, "%d", dwButtons );
+    check_member( caps, expect_caps, "%d", dwPOVs );
+    check_member( caps, expect_caps, "%d", dwFFSamplePeriod );
+    check_member( caps, expect_caps, "%d", dwFFMinTimeResolution );
+    check_member( caps, expect_caps, "%d", dwFirmwareRevision );
+    check_member( caps, expect_caps, "%d", dwHardwareRevision );
+    check_member( caps, expect_caps, "%d", dwFFDriverVersion );
+
+    hr = IDirectInputDevice8_GetProperty( device, NULL, NULL );
+    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, &GUID_NULL, NULL );
+    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, NULL );
+    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, &prop_string.diph );
+    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
+    prop_dword.diph.dwHeaderSize = sizeof(DIPROPHEADER) - 1;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "GetProperty returned %#x\n", hr );
+    prop_dword.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_VIDPID, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_VIDPID returned %#x\n", hr );
+    ok( prop_dword.dwData == EXPECT_VIDPID, "got %#x expected %#x\n", prop_dword.dwData, EXPECT_VIDPID );
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
+    todo_wine
+    ok( IsEqualGUID( &prop_guid_path.guidClass, &GUID_DEVCLASS_HIDCLASS ), "got guid %s\n",
+        debugstr_guid( &prop_guid_path.guidClass ) );
+    todo_wine
+    ok( !wcsncmp( prop_guid_path.wszPath, expect_path, wcslen( expect_path ) ), "got path %s\n",
+        debugstr_w(prop_guid_path.wszPath) );
+    if (!(tmp = wcsrchr( prop_guid_path.wszPath, '&' )))
+        todo_wine ok( 0, "got path %s\n", debugstr_w(prop_guid_path.wszPath) );
+    else
+    {
+        ok( !wcscmp( wcsrchr( prop_guid_path.wszPath, '&' ), expect_path_end ), "got path %s\n",
+            debugstr_w(prop_guid_path.wszPath) );
+    }
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_INSTANCENAME, &prop_string.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_INSTANCENAME returned %#x\n", hr );
+    todo_wine
+    ok( !wcscmp( prop_string.wsz, expect_devinst.tszInstanceName ), "got instance %s\n",
+        debugstr_w(prop_string.wsz) );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_PRODUCTNAME, &prop_string.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_PRODUCTNAME returned %#x\n", hr );
+    todo_wine
+    ok( !wcscmp( prop_string.wsz, expect_devinst.tszProductName ), "got product %s\n",
+        debugstr_w(prop_string.wsz) );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_TYPENAME, &prop_string.diph );
+    todo_wine
+    ok( hr == DI_OK, "GetProperty DIPROP_TYPENAME returned %#x\n", hr );
+    todo_wine
+    ok( !wcscmp( prop_string.wsz, expect_vidpid_str ), "got type %s\n", debugstr_w(prop_string.wsz) );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_USERNAME, &prop_string.diph );
+    ok( hr == S_FALSE, "GetProperty DIPROP_USERNAME returned %#x\n", hr );
+    todo_wine
+    ok( !wcscmp( prop_string.wsz, L"" ), "got user %s\n", debugstr_w(prop_string.wsz) );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_JOYSTICKID, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_JOYSTICKID returned %#x\n", hr );
+    todo_wine
+    ok( prop_dword.dwData == 0, "got %#x expected %#x\n", prop_dword.dwData, 0 );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
+    todo_wine
+    ok( hr == DI_OK, "GetProperty DIPROP_AXISMODE returned %#x\n", hr );
+    todo_wine
+    ok( prop_dword.dwData == DIPROPAXISMODE_ABS, "got %u expected %u\n", prop_dword.dwData, DIPROPAXISMODE_ABS );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
+    ok( prop_dword.dwData == 0, "got %#x expected %#x\n", prop_dword.dwData, 0 );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_FFGAIN returned %#x\n", hr );
+    ok( prop_dword.dwData == 10000, "got %u expected %u\n", prop_dword.dwData, 10000 );
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATION, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "GetProperty DIPROP_CALIBRATION returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GRANULARITY, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_GRANULARITY returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_RANGE returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_KEYNAME, &prop_string.diph );
+    ok( hr == DIERR_INVALIDPARAM, "GetProperty DIPROP_KEYNAME returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_LOGICALRANGE, &prop_range.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_LOGICALRANGE returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_PHYSICALRANGE, &prop_range.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_PHYSICALRANGE returned %#x\n", hr );
+
+    prop_dword.diph.dwHow = DIPH_BYUSAGE;
+    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    ok( prop_dword.dwData == 0, "got %u expected %u\n", prop_dword.dwData, 0 );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GRANULARITY, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_GRANULARITY returned %#x\n", hr );
+    ok( prop_dword.dwData == 1, "got %u expected %u\n", prop_dword.dwData, 1 );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
+    ok( prop_dword.dwData == 10000, "got %u expected %u\n", prop_dword.dwData, 10000 );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
+    ok( prop_dword.dwData == DIPROPCALIBRATIONMODE_COOKED, "got %u expected %u\n",
+        prop_dword.dwData, DIPROPCALIBRATIONMODE_COOKED );
+
+    prop_string.diph.dwHow = DIPH_BYUSAGE;
+    prop_string.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_KEYNAME, &prop_string.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_KEYNAME returned %#x\n", hr );
+    ok( !wcscmp( prop_string.wsz, expect_objects[4].tszName ), "got DIPROP_KEYNAME %s\n",
+        debugstr_w(prop_string.wsz) );
+    prop_string.diph.dwObj = MAKELONG( 0x1, HID_USAGE_PAGE_BUTTON );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_KEYNAME, &prop_string.diph );
+    todo_wine
+    ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_KEYNAME returned %#x\n", hr );
+    prop_string.diph.dwHow = DIPH_BYUSAGE;
+    prop_string.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_KEYNAME, &prop_string.diph );
+    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_KEYNAME returned %#x\n", hr );
+
+    prop_range.diph.dwHow = DIPH_BYUSAGE;
+    prop_range.diph.dwObj = MAKELONG( 0, 0 );
+    prop_range.lMin = 0xdeadbeef;
+    prop_range.lMax = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_RANGE returned %#x\n", hr );
+    prop_range.diph.dwObj = MAKELONG( 0, HID_USAGE_PAGE_GENERIC );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_RANGE returned %#x\n", hr );
+    prop_range.diph.dwObj = MAKELONG( HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_RANGE returned %#x\n", hr );
+    prop_range.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_range.lMin = 0xdeadbeef;
+    prop_range.lMax = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_RANGE returned %#x\n", hr );
+    ok( prop_range.lMin == 0, "got %d expected %d\n", prop_range.lMin, 0 );
+    ok( prop_range.lMax == 65535, "got %d expected %d\n", prop_range.lMax, 65535 );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_LOGICALRANGE, &prop_range.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_LOGICALRANGE returned %#x\n", hr );
+    ok( prop_range.lMin == -25, "got %d expected %d\n", prop_range.lMin, -25 );
+    ok( prop_range.lMax == 56, "got %d expected %d\n", prop_range.lMax, 56 );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_PHYSICALRANGE, &prop_range.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_PHYSICALRANGE returned %#x\n", hr );
+    ok( prop_range.lMin == -25, "got %d expected %d\n", prop_range.lMin, -25 );
+    ok( prop_range.lMax == 56, "got %d expected %d\n", prop_range.lMax, 56 );
+
+    prop_pointer.diph.dwHow = DIPH_BYUSAGE;
+    prop_pointer.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_APPDATA, &prop_pointer.diph );
+    todo_wine
+    ok( hr == DIERR_NOTINITIALIZED, "GetProperty DIPROP_APPDATA returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_EnumObjects( device, NULL, NULL, DIDFT_ALL );
+    ok( hr == DIERR_INVALIDPARAM, "EnumObjects returned %#x\n", hr );
+    hr = IDirectInputDevice8_EnumObjects( device, check_object_count, &res, 0x20 );
+    ok( hr == DIERR_INVALIDPARAM, "EnumObjects returned %#x\n", hr );
+    res = 0;
+    hr = IDirectInputDevice8_EnumObjects( device, check_object_count, &res, DIDFT_AXIS | DIDFT_PSHBUTTON );
+    ok( hr == DI_OK, "EnumObjects returned %#x\n", hr );
+    ok( res == 8, "got %u expected %u\n", res, 8 );
+    hr = IDirectInputDevice8_EnumObjects( device, check_objects, &check_objects_params, DIDFT_ALL );
+    ok( hr == DI_OK, "EnumObjects returned %#x\n", hr );
+    ok( check_objects_params.index >= check_objects_params.expect_count, "missing %u objects\n",
+        check_objects_params.expect_count - check_objects_params.index );
+
+    hr = IDirectInputDevice8_GetObjectInfo( device, NULL, 0, DIPH_DEVICE );
+    ok( hr == E_POINTER, "GetObjectInfo returned: %#x\n", hr );
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, 0, DIPH_DEVICE );
+    ok( hr == DIERR_INVALIDPARAM, "GetObjectInfo returned: %#x\n", hr );
+    objinst.dwSize = sizeof(DIDEVICEOBJECTINSTANCEW);
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, 0, DIPH_DEVICE );
+    ok( hr == DIERR_INVALIDPARAM, "GetObjectInfo returned: %#x\n", hr );
+
+    res = MAKELONG( HID_USAGE_GENERIC_Z, HID_USAGE_PAGE_GENERIC );
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, res, DIPH_BYUSAGE );
+    ok( hr == DIERR_NOTFOUND, "GetObjectInfo returned: %#x\n", hr );
+    res = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, res, DIPH_BYUSAGE );
+    ok( hr == DI_OK, "GetObjectInfo returned: %#x\n", hr );
+
+    check_member( objinst, expect_objects[4], "%u", dwSize );
+    check_member_guid( objinst, expect_objects[4], guidType );
+    check_member( objinst, expect_objects[4], "%#x", dwOfs );
+    check_member( objinst, expect_objects[4], "%#x", dwType );
+    check_member( objinst, expect_objects[4], "%#x", dwFlags );
+    if (!localized) check_member_wstr( objinst, expect_objects[4], tszName );
+    check_member( objinst, expect_objects[4], "%u", dwFFMaxForce );
+    check_member( objinst, expect_objects[4], "%u", dwFFForceResolution );
+    check_member( objinst, expect_objects[4], "%u", wCollectionNumber );
+    check_member( objinst, expect_objects[4], "%u", wDesignatorIndex );
+    check_member( objinst, expect_objects[4], "%#04x", wUsagePage );
+    check_member( objinst, expect_objects[4], "%#04x", wUsage );
+    check_member( objinst, expect_objects[4], "%#04x", dwDimension );
+    check_member( objinst, expect_objects[4], "%#04x", wExponent );
+    check_member( objinst, expect_objects[4], "%u", wReportId );
+
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, 0x14, DIPH_BYOFFSET );
+    ok( hr == DIERR_NOTFOUND, "GetObjectInfo returned: %#x\n", hr );
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, 0, DIPH_BYOFFSET );
+    ok( hr == DIERR_NOTFOUND, "GetObjectInfo returned: %#x\n", hr );
+    res = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 3 );
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, res, DIPH_BYID );
+    ok( hr == DIERR_NOTFOUND, "GetObjectInfo returned: %#x\n", hr );
+    res = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 1 );
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, res, DIPH_BYID );
+    ok( hr == DI_OK, "GetObjectInfo returned: %#x\n", hr );
+
+    check_member( objinst, expect_objects[8], "%u", dwSize );
+    check_member_guid( objinst, expect_objects[8], guidType );
+    check_member( objinst, expect_objects[8], "%#x", dwOfs );
+    check_member( objinst, expect_objects[8], "%#x", dwType );
+    check_member( objinst, expect_objects[8], "%#x", dwFlags );
+    if (!localized) check_member_wstr( objinst, expect_objects[8], tszName );
+    check_member( objinst, expect_objects[8], "%u", dwFFMaxForce );
+    check_member( objinst, expect_objects[8], "%u", dwFFForceResolution );
+    check_member( objinst, expect_objects[8], "%u", wCollectionNumber );
+    check_member( objinst, expect_objects[8], "%u", wDesignatorIndex );
+    check_member( objinst, expect_objects[8], "%#04x", wUsagePage );
+    check_member( objinst, expect_objects[8], "%#04x", wUsage );
+    check_member( objinst, expect_objects[8], "%#04x", dwDimension );
+    check_member( objinst, expect_objects[8], "%#04x", wExponent );
+    check_member( objinst, expect_objects[8], "%u", wReportId );
+
+    hr = IDirectInputDevice8_EnumEffects( device, NULL, NULL, DIEFT_ALL );
+    ok( hr == DIERR_INVALIDPARAM, "EnumEffects returned %#x\n", hr );
+    res = 0;
+    hr = IDirectInputDevice8_EnumEffects( device, check_effect_count, &res, 0xfe );
+    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
+    ok( res == 0, "got %u expected %u\n", res, 0 );
+    res = 0;
+    hr = IDirectInputDevice8_EnumEffects( device, check_effect_count, &res, DIEFT_PERIODIC );
+    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
+    ok( res == 0, "got %u expected %u\n", res, 0 );
+    hr = IDirectInputDevice8_EnumEffects( device, check_effects, &check_effects_params, DIEFT_ALL );
+    ok( hr == DI_OK, "EnumEffects returned %#x\n", hr );
+    ok( check_effects_params.index >= check_effects_params.expect_count, "missing %u effects\n",
+        check_effects_params.expect_count - check_effects_params.index );
+
+    hr = IDirectInputDevice8_GetEffectInfo( device, NULL, &GUID_Sine );
+    ok( hr == E_POINTER, "GetEffectInfo returned %#x\n", hr );
+    effectinfo.dwSize = sizeof(DIEFFECTINFOW) + 1;
+    hr = IDirectInputDevice8_GetEffectInfo( device, &effectinfo, &GUID_Sine );
+    ok( hr == DIERR_INVALIDPARAM, "GetEffectInfo returned %#x\n", hr );
+    effectinfo.dwSize = sizeof(DIEFFECTINFOW);
+    hr = IDirectInputDevice8_GetEffectInfo( device, &effectinfo, &GUID_NULL );
+    ok( hr == DIERR_DEVICENOTREG, "GetEffectInfo returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetEffectInfo( device, &effectinfo, &GUID_Sine );
+    ok( hr == DIERR_DEVICENOTREG, "GetEffectInfo returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_SetDataFormat( device, NULL );
+    ok( hr == E_POINTER, "SetDataFormat returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    dataformat.dwSize = sizeof(DIDATAFORMAT);
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    dataformat.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_GetObjectInfo( device, &objinst, DIJOFS_Y, DIPH_BYOFFSET );
+    ok( hr == DI_OK, "GetObjectInfo returned: %#x\n", hr );
+
+    check_member( objinst, expect_objects[3], "%u", dwSize );
+    check_member_guid( objinst, expect_objects[3], guidType );
+    check_member( objinst, expect_objects[3], "%#x", dwOfs );
+    check_member( objinst, expect_objects[3], "%#x", dwType );
+    check_member( objinst, expect_objects[3], "%#x", dwFlags );
+    if (!localized) check_member_wstr( objinst, expect_objects[3], tszName );
+    check_member( objinst, expect_objects[3], "%u", dwFFMaxForce );
+    check_member( objinst, expect_objects[3], "%u", dwFFForceResolution );
+    check_member( objinst, expect_objects[3], "%u", wCollectionNumber );
+    check_member( objinst, expect_objects[3], "%u", wDesignatorIndex );
+    check_member( objinst, expect_objects[3], "%#04x", wUsagePage );
+    check_member( objinst, expect_objects[3], "%#04x", wUsage );
+    check_member( objinst, expect_objects[3], "%#04x", dwDimension );
+    check_member( objinst, expect_objects[3], "%#04x", wExponent );
+    check_member( objinst, expect_objects[3], "%u", wReportId );
+
+    hr = IDirectInputDevice8_SetEventNotification( device, (HANDLE)0xdeadbeef );
+    todo_wine
+    ok( hr == E_HANDLE, "SetEventNotification returned: %#x\n", hr );
+    event = CreateEventW( NULL, FALSE, FALSE, NULL );
+    ok( event != NULL, "CreateEventW failed, last error %u\n", GetLastError() );
+    hr = IDirectInputDevice8_SetEventNotification( device, event );
+    ok( hr == DI_OK, "SetEventNotification returned: %#x\n", hr );
+
+    file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
+    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
+
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, 0 );
+    ok( hr == DIERR_INVALIDPARAM, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_BACKGROUND );
+    ok( hr == DIERR_INVALIDPARAM, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE );
+    ok( hr == E_HANDLE, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_BACKGROUND | DISCL_EXCLUSIVE );
+    ok( hr == E_HANDLE, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_FOREGROUND | DISCL_EXCLUSIVE );
+    ok( hr == E_HANDLE, "SetCooperativeLevel returned: %#x\n", hr );
+
+    hwnd = CreateWindowW( L"static", L"dinput", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200,
+                          NULL, NULL, NULL, NULL );
+    SetForegroundWindow( hwnd );
+
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE );
+    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE );
+    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE );
+    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_NOEFFECT, "Unacquire returned: %#x\n", hr );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE );
+    ok( hr == DIERR_ACQUIRED, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    DestroyWindow( hwnd );
+
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE );
+    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+    ok( hr == DIERR_NOTACQUIRED, "GetDeviceState returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_Poll( device );
+    ok( hr == DIERR_NOTACQUIRED, "Poll returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_Poll( device );
+    ok( hr == DI_NOEFFECT, "Poll returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2) + 1, &state );
+    ok( hr == DIERR_INVALIDPARAM, "GetDeviceState returned: %#x\n", hr );
+
+    for (i = 0; i < ARRAY_SIZE(injected_input); ++i)
+    {
+        winetest_push_context( "state[%d]", i );
+        hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+        ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+        check_member( state, expect_state[i], "%d", lX );
+        check_member( state, expect_state[i], "%d", lY );
+        check_member( state, expect_state[i], "%d", lZ );
+        check_member( state, expect_state[i], "%d", lRx );
+        check_member( state, expect_state[i], "%#x", rgdwPOV[0] );
+        check_member( state, expect_state[i], "%#x", rgdwPOV[1] );
+        check_member( state, expect_state[i], "%#x", rgbButtons[0] );
+        check_member( state, expect_state[i], "%#x", rgbButtons[1] );
+        check_member( state, expect_state[i], "%#x", rgbButtons[2] );
+
+        send_hid_input( file, &injected_input[i], sizeof(*injected_input) );
+
+        res = WaitForSingleObject( event, 100 );
+        if (i == 0 || i == 3) ok( res == WAIT_TIMEOUT, "WaitForSingleObject succeeded\n" );
+        else ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+        ResetEvent( event );
+        winetest_pop_context();
+    }
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    winetest_push_context( "state[%d]", i );
+    check_member( state, expect_state[i], "%d", lX );
+    check_member( state, expect_state[i], "%d", lY );
+    check_member( state, expect_state[i], "%d", lZ );
+    check_member( state, expect_state[i], "%d", lRx );
+    check_member( state, expect_state[i], "%#x", rgdwPOV[0] );
+    check_member( state, expect_state[i], "%#x", rgdwPOV[1] );
+    check_member( state, expect_state[i], "%#x", rgbButtons[0] );
+    check_member( state, expect_state[i], "%#x", rgbButtons[1] );
+    check_member( state, expect_state[i], "%#x", rgbButtons[2] );
+    winetest_pop_context();
+
+    res = 1;
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA) - 1, objdata, &res, DIGDD_PEEK );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "GetDeviceData returned %#x\n", hr );
+    res = 1;
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, DIGDD_PEEK );
+    ok( hr == DIERR_NOTBUFFERED, "GetDeviceData returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    prop_dword.diph.dwHow = DIPH_BYUSAGE;
+    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_dword.dwData = 1;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
+    prop_dword.diph.dwHow = DIPH_DEVICE;
+    prop_dword.diph.dwObj = 0;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    res = 1;
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, DIGDD_PEEK );
+    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
+    ok( res == 0, "got %u expected %u\n", res, 0 );
+
+    send_hid_input( file, &injected_input[0], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+
+    res = 1;
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, DIGDD_PEEK );
+    ok( hr == DI_BUFFEROVERFLOW, "GetDeviceData returned %#x\n", hr );
+    ok( res == 0, "got %u expected %u\n", res, 0 );
+    res = 1;
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
+    todo_wine
+    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
+    ok( res == 0, "got %u expected %u\n", res, 0 );
+
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    prop_dword.diph.dwHow = DIPH_DEVICE;
+    prop_dword.diph.dwObj = 0;
+    prop_dword.dwData = 10;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    send_hid_input( file, &injected_input[1], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+
+    res = 1;
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, DIGDD_PEEK );
+    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
+    ok( res == 1, "got %u expected %u\n", res, 1 );
+    check_member( objdata[0], expect_objdata[0], "%#x", dwOfs );
+    check_member( objdata[0], expect_objdata[0], "%#x", dwData );
+    ok( objdata[0].uAppData == -1, "got %p, expected %p\n", (void *)objdata[0].uAppData, (void *)-1 );
+    res = 4;
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
+    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
+    ok( res == 4, "got %u expected %u\n", res, 4 );
+    for (i = 0; i < 4; ++i)
+    {
+        winetest_push_context( "objdata[%d]", i );
+        check_member( objdata[i], expect_objdata[1 + i], "%#x", dwOfs );
+        check_member( objdata[i], expect_objdata[1 + i], "%#x", dwData );
+        ok( objdata[i].uAppData == -1, "got %p, expected %p\n", (void *)objdata[i].uAppData, (void *)-1 );
+        winetest_pop_context();
+    }
+
+    send_hid_input( file, &injected_input[2], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+    send_hid_input( file, &injected_input[4], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+
+    res = 1;
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
+    ok( hr == DI_BUFFEROVERFLOW, "GetDeviceData returned %#x\n", hr );
+    ok( res == 1, "got %u expected %u\n", res, 1 );
+    todo_wine
+    check_member( objdata[0], expect_objdata[5], "%#x", dwOfs );
+    todo_wine
+    check_member( objdata[0], expect_objdata[5], "%#x", dwData );
+    ok( objdata[0].uAppData == -1, "got %p, expected %p\n", (void *)objdata[0].uAppData, (void *)-1 );
+    res = ARRAY_SIZE(objdata);
+    hr = IDirectInputDevice8_GetDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
+    ok( hr == DI_OK, "GetDeviceData returned %#x\n", hr );
+    ok( res == 8, "got %u expected %u\n", res, 8 );
+    for (i = 0; i < 8; ++i)
+    {
+        winetest_push_context( "objdata[%d]", i );
+        todo_wine
+        check_member( objdata[i], expect_objdata[6 + i], "%#x", dwOfs );
+        todo_wine_if( i == 1 || i == 2 || i == 6 )
+        check_member( objdata[i], expect_objdata[6 + i], "%#x", dwData );
+        ok( objdata[i].uAppData == -1, "got %p, expected %p\n", (void *)objdata[i].uAppData, (void *)-1 );
+        winetest_pop_context();
+    }
+
+    send_hid_input( file, &injected_input[3], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    check_member( state, expect_state[3], "%d", lX );
+    check_member( state, expect_state[3], "%d", lY );
+    check_member( state, expect_state[3], "%d", lZ );
+    check_member( state, expect_state[3], "%d", lRx );
+    check_member( state, expect_state[3], "%d", rgdwPOV[0] );
+    check_member( state, expect_state[3], "%d", rgdwPOV[1] );
+    check_member( state, expect_state[3], "%#x", rgbButtons[0] );
+    check_member( state, expect_state[3], "%#x", rgbButtons[1] );
+    check_member( state, expect_state[3], "%#x", rgbButtons[2] );
+
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    dataformat.dwSize = sizeof(DIDATAFORMAT);
+    dataformat.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
+    dataformat.dwFlags = DIDF_ABSAXIS;
+    dataformat.dwDataSize = sizeof(buffer);
+    dataformat.dwNumObjs = 0;
+    dataformat.rgodf = objdataformat;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+
+    dataformat.dwNumObjs = 1;
+    dataformat.dwDataSize = 8;
+    objdataformat[0].pguid = NULL;
+    objdataformat[0].dwOfs = 2;
+    objdataformat[0].dwType = DIDFT_AXIS | DIDFT_ANYINSTANCE;
+    objdataformat[0].dwFlags = 0;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    objdataformat[0].dwOfs = 4;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+    dataformat.dwDataSize = 10;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    dataformat.dwDataSize = 8;
+    objdataformat[0].dwOfs = 2;
+    objdataformat[0].dwType = DIDFT_OPTIONAL | 0xff | DIDFT_ANYINSTANCE;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+
+    dataformat.dwNumObjs = 2;
+    dataformat.dwDataSize = 5;
+    objdataformat[0].pguid = NULL;
+    objdataformat[0].dwOfs = 4;
+    objdataformat[0].dwType = DIDFT_BUTTON | DIDFT_ANYINSTANCE;
+    objdataformat[0].dwFlags = 0;
+    objdataformat[1].pguid = NULL;
+    objdataformat[1].dwOfs = 0;
+    objdataformat[1].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE( 0 );
+    objdataformat[1].dwFlags = 0;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    dataformat.dwDataSize = 8;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+
+    dataformat.dwNumObjs = 4;
+    dataformat.dwDataSize = 12;
+    objdataformat[0].pguid = NULL;
+    objdataformat[0].dwOfs = 0;
+    objdataformat[0].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE( 0 );
+    objdataformat[0].dwFlags = 0;
+    objdataformat[1].pguid = NULL;
+    objdataformat[1].dwOfs = 0;
+    objdataformat[1].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE( 0 );
+    objdataformat[1].dwFlags = 0;
+    objdataformat[2].pguid = &GUID_ZAxis;
+    objdataformat[2].dwOfs = 8;
+    objdataformat[2].dwType = 0xff | DIDFT_ANYINSTANCE;
+    objdataformat[2].dwFlags = 0;
+    objdataformat[3].pguid = &GUID_POV;
+    objdataformat[3].dwOfs = 0;
+    objdataformat[3].dwType = 0xff | DIDFT_ANYINSTANCE;
+    objdataformat[3].dwFlags = 0;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    objdataformat[1].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE( 12 );
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    objdataformat[1].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE( 0xff );
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    objdataformat[1].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE( 1 );
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+    objdataformat[1].pguid = &GUID_RzAxis;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    objdataformat[1].pguid = &GUID_Unknown;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DIERR_INVALIDPARAM, "SetDataFormat returned: %#x\n", hr );
+    objdataformat[1].pguid = &GUID_YAxis;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+    objdataformat[1].pguid = NULL;
+    objdataformat[1].dwType = DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_MAKEINSTANCE( 0 );
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+
+    dataformat.dwNumObjs = 0;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    send_hid_input( file, &injected_input[4], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    todo_wine
+    ok( res == WAIT_TIMEOUT, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+
+    hr = IDirectInputDevice8_GetDeviceState( device, dataformat.dwDataSize, buffer );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    dataformat.dwNumObjs = 4;
+    hr = IDirectInputDevice8_SetDataFormat( device, &dataformat );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    send_hid_input( file, &injected_input[4], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    todo_wine
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+    send_hid_input( file, &injected_input[3], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+
+    hr = IDirectInputDevice8_GetDeviceState( device, dataformat.dwDataSize, buffer );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    ok( ((ULONG *)buffer)[0] == 0x512b, "got %#x, expected %#x\n", ((ULONG *)buffer)[0], 0x512b );
+    ok( ((ULONG *)buffer)[1] == 0, "got %#x, expected %#x\n", ((ULONG *)buffer)[1], 0 );
+    ok( ((ULONG *)buffer)[2] == 0x7fff, "got %#x, expected %#x\n", ((ULONG *)buffer)[2], 0x7fff );
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    send_hid_input( file, &injected_input[4], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+    send_hid_input( file, &injected_input[3], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+    ResetEvent( event );
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    check_member( state, expect_state[3], "%d", lX );
+    check_member( state, expect_state[3], "%d", lY );
+    check_member( state, expect_state[3], "%d", lZ );
+    check_member( state, expect_state[3], "%d", lRx );
+    check_member( state, expect_state[3], "%d", rgdwPOV[0] );
+    check_member( state, expect_state[3], "%d", rgdwPOV[1] );
+    check_member( state, expect_state[3], "%#x", rgbButtons[0] );
+    check_member( state, expect_state[3], "%#x", rgbButtons[1] );
+    check_member( state, expect_state[3], "%#x", rgbButtons[2] );
+
+    prop_range.diph.dwHow = DIPH_DEVICE;
+    prop_range.diph.dwObj = 0;
+    prop_range.lMin = 1000;
+    prop_range.lMax = 51000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_RANGE returned %#x\n", hr );
+    prop_range.diph.dwHow = DIPH_BYUSAGE;
+    prop_range.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_range.lMin = -4000;
+    prop_range.lMax = -14000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_RANGE returned %#x\n", hr );
+    prop_range.lMin = -14000;
+    prop_range.lMax = -4000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_RANGE returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_LOGICALRANGE, &prop_range.diph );
+    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_LOGICALRANGE returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_PHYSICALRANGE, &prop_range.diph );
+    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_PHYSICALRANGE returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_LOGICALRANGE, &prop_range.diph );
+    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_LOGICALRANGE returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_PHYSICALRANGE, &prop_range.diph );
+    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_PHYSICALRANGE returned %#x\n", hr );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    prop_range.diph.dwHow = DIPH_DEVICE;
+    prop_range.diph.dwObj = 0;
+    prop_range.lMin = 0xdeadbeef;
+    prop_range.lMax = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_RANGE returned %#x\n", hr );
+    prop_range.diph.dwHow = DIPH_BYUSAGE;
+    prop_range.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_range.lMin = 0xdeadbeef;
+    prop_range.lMax = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_RANGE returned %#x\n", hr );
+    ok( prop_range.lMin == -14000, "got %d expected %d\n", prop_range.lMin, -14000 );
+    ok( prop_range.lMax == -4000, "got %d expected %d\n", prop_range.lMax, -4000 );
+    prop_range.diph.dwHow = DIPH_BYUSAGE;
+    prop_range.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_Y, HID_USAGE_PAGE_GENERIC );
+    prop_range.lMin = 0xdeadbeef;
+    prop_range.lMax = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_RANGE, &prop_range.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_RANGE returned %#x\n", hr );
+    ok( prop_range.lMin == 1000, "got %d expected %d\n", prop_range.lMin, 1000 );
+    ok( prop_range.lMax == 51000, "got %d expected %d\n", prop_range.lMax, 51000 );
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    check_member( state, expect_state_abs[1], "%d", lX );
+    check_member( state, expect_state_abs[1], "%d", lY );
+    check_member( state, expect_state_abs[1], "%d", lZ );
+    check_member( state, expect_state_abs[1], "%d", lRx );
+    check_member( state, expect_state_abs[1], "%d", rgdwPOV[0] );
+    check_member( state, expect_state_abs[1], "%d", rgdwPOV[1] );
+    check_member( state, expect_state_abs[1], "%#x", rgbButtons[0] );
+    check_member( state, expect_state_abs[1], "%#x", rgbButtons[1] );
+    check_member( state, expect_state_abs[1], "%#x", rgbButtons[2] );
+
+    hr = IDirectInputDevice8_SetProperty( device, NULL, NULL );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, &GUID_NULL, NULL );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_VIDPID, NULL );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_VIDPID, &prop_string.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty returned %#x\n", hr );
+
+    prop_dword.diph.dwHow = DIPH_DEVICE;
+    prop_dword.diph.dwObj = 0;
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_VIDPID, &prop_dword.diph );
+    ok( hr == DIERR_READONLY, "SetProperty DIPROP_VIDPID returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
+    ok( hr == DIERR_READONLY, "SetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
+
+    prop_string.diph.dwHow = DIPH_DEVICE;
+    prop_string.diph.dwObj = 0;
+    wcscpy( prop_string.wsz, L"instance name" );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_INSTANCENAME, &prop_string.diph );
+    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_INSTANCENAME returned %#x\n", hr );
+
+    wcscpy( prop_string.wsz, L"product name" );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_PRODUCTNAME, &prop_string.diph );
+    todo_wine
+    ok( hr == DI_OK, "SetProperty DIPROP_PRODUCTNAME returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_PRODUCTNAME, &prop_string.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_PRODUCTNAME returned %#x\n", hr );
+    todo_wine
+    ok( !wcscmp( prop_string.wsz, expect_devinst.tszProductName ), "got product %s\n",
+        debugstr_w(prop_string.wsz) );
+
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_TYPENAME, &prop_string.diph );
+    ok( hr == DIERR_READONLY, "SetProperty DIPROP_TYPENAME returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_USERNAME, &prop_string.diph );
+    ok( hr == DIERR_READONLY, "SetProperty DIPROP_USERNAME returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFLOAD, &prop_dword.diph );
+    ok( hr == DIERR_READONLY, "SetProperty DIPROP_FFLOAD returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_GRANULARITY, &prop_dword.diph );
+    ok( hr == DIERR_READONLY, "SetProperty DIPROP_GRANULARITY returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_JOYSTICKID, &prop_dword.diph );
+    todo_wine
+    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_JOYSTICKID returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
+    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_AXISMODE returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
+    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
+    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
+    prop_pointer.diph.dwHow = DIPH_BYUSAGE;
+    prop_pointer.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_APPDATA, &prop_pointer.diph );
+    todo_wine
+    ok( hr == DIERR_ACQUIRED, "SetProperty DIPROP_APPDATA returned %#x\n", hr );
+
+    prop_dword.diph.dwHow = DIPH_DEVICE;
+    prop_dword.diph.dwObj = 0;
+    prop_dword.dwData = 10001;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_SATURATION returned %#x\n", hr );
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
+    prop_dword.dwData = 1000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    prop_dword.dwData = 6000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_SATURATION returned %#x\n", hr );
+    prop_dword.dwData = DIPROPCALIBRATIONMODE_COOKED;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
+
+    prop_dword.diph.dwHow = DIPH_BYUSAGE;
+    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_dword.dwData = 2000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    ok( prop_dword.dwData == 2000, "got %u expected %u\n", prop_dword.dwData, 2000 );
+    prop_dword.dwData = 7000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_SATURATION returned %#x\n", hr );
+
+    prop_dword.diph.dwHow = DIPH_BYUSAGE;
+    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    ok( prop_dword.dwData == 2000, "got %u expected %u\n", prop_dword.dwData, 2000 );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
+    ok( prop_dword.dwData == 7000, "got %u expected %u\n", prop_dword.dwData, 7000 );
+
+    prop_dword.diph.dwHow = DIPH_BYUSAGE;
+    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_Y, HID_USAGE_PAGE_GENERIC );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    ok( prop_dword.dwData == 1000, "got %u expected %u\n", prop_dword.dwData, 1000 );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_SATURATION returned %#x\n", hr );
+    ok( prop_dword.dwData == 6000, "got %u expected %u\n", prop_dword.dwData, 6000 );
+
+    for (i = 0; i < ARRAY_SIZE(injected_input); ++i)
+    {
+        winetest_push_context( "state[%d]", i );
+        hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+        ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+        if (broken( state.lX == -10750 )) win_skip( "Ignoring 32-bit rounding\n" );
+        else
+        {
+            check_member( state, expect_state_abs[i], "%d", lX );
+            check_member( state, expect_state_abs[i], "%d", lY );
+        }
+        check_member( state, expect_state_abs[i], "%d", lZ );
+        check_member( state, expect_state_abs[i], "%d", lRx );
+        check_member( state, expect_state_abs[i], "%d", rgdwPOV[0] );
+        check_member( state, expect_state_abs[i], "%d", rgdwPOV[1] );
+        check_member( state, expect_state_abs[i], "%#x", rgbButtons[0] );
+        check_member( state, expect_state_abs[i], "%#x", rgbButtons[1] );
+        check_member( state, expect_state_abs[i], "%#x", rgbButtons[2] );
+
+        send_hid_input( file, &injected_input[i], sizeof(*injected_input) );
+
+        res = WaitForSingleObject( event, 100 );
+        if (i == 0 || i == 3) ok( res == WAIT_TIMEOUT, "WaitForSingleObject succeeded\n" );
+        else ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+        ResetEvent( event );
+        winetest_pop_context();
+    }
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    winetest_push_context( "state[%d]", i );
+    check_member( state, expect_state_abs[i], "%d", lX );
+    check_member( state, expect_state_abs[i], "%d", lY );
+    check_member( state, expect_state_abs[i], "%d", lZ );
+    check_member( state, expect_state_abs[i], "%d", lRx );
+    check_member( state, expect_state_abs[i], "%d", rgdwPOV[0] );
+    check_member( state, expect_state_abs[i], "%d", rgdwPOV[1] );
+    check_member( state, expect_state_abs[i], "%#x", rgbButtons[0] );
+    check_member( state, expect_state_abs[i], "%#x", rgbButtons[1] );
+    check_member( state, expect_state_abs[i], "%#x", rgbButtons[2] );
+    winetest_pop_context();
+
+    prop_dword.diph.dwHow = DIPH_BYUSAGE;
+    prop_dword.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_dword.dwData = DIPROPCALIBRATIONMODE_RAW;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
+    ok( prop_dword.dwData == DIPROPCALIBRATIONMODE_RAW, "got %u expected %u\n", prop_dword.dwData,
+        DIPROPCALIBRATIONMODE_RAW );
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    winetest_push_context( "state[%d]", i );
+    todo_wine
+    ok( state.lX == 15, "got lX %d, expected %d\n", state.lX, 15 );
+    check_member( state, expect_state_abs[0], "%d", lY );
+    check_member( state, expect_state_abs[0], "%d", lZ );
+    check_member( state, expect_state_abs[0], "%d", lRx );
+    check_member( state, expect_state_abs[0], "%d", rgdwPOV[0] );
+    check_member( state, expect_state_abs[0], "%d", rgdwPOV[1] );
+    winetest_pop_context();
+
+    prop_dword.dwData = DIPROPCALIBRATIONMODE_COOKED;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATIONMODE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_CALIBRATIONMODE returned %#x\n", hr );
+
+    send_hid_input( file, &injected_input[ARRAY_SIZE(injected_input) - 1], sizeof(*injected_input) );
+    res = WaitForSingleObject( event, 100 );
+    ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    prop_dword.diph.dwHow = DIPH_DEVICE;
+    prop_dword.diph.dwObj = 0;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_JOYSTICKID, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_JOYSTICKID returned %#x\n", hr );
+    prop_dword.dwData = 0x1000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
+    prop_dword.dwData = DIPROPAUTOCENTER_ON;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AUTOCENTER, &prop_dword.diph );
+    ok( hr == DIERR_UNSUPPORTED, "SetProperty DIPROP_AUTOCENTER returned %#x\n", hr );
+    prop_pointer.diph.dwHow = DIPH_BYUSAGE;
+    prop_pointer.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    prop_pointer.uData = 0xfeedcafe;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_APPDATA, &prop_pointer.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_APPDATA returned %#x\n", hr );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_AXISMODE returned %#x\n", hr );
+    prop_dword.dwData = DIPROPAXISMODE_REL;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_AXISMODE returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_AXISMODE, &prop_dword.diph );
+    todo_wine
+    ok( hr == DI_OK, "GetProperty DIPROP_AXISMODE returned %#x\n", hr );
+    todo_wine
+    ok( prop_dword.dwData == DIPROPAXISMODE_REL, "got %u expected %u\n", prop_dword.dwData, DIPROPAXISMODE_REL );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_BUFFERSIZE, &prop_dword.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_BUFFERSIZE returned %#x\n", hr );
+    ok( prop_dword.dwData == 0x1000, "got %#x expected %#x\n", prop_dword.dwData, 0x1000 );
+
+    prop_pointer.diph.dwHow = DIPH_BYUSAGE;
+    prop_pointer.diph.dwObj = MAKELONG( HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC );
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_APPDATA, &prop_pointer.diph );
+    todo_wine
+    ok( hr == DI_OK, "GetProperty DIPROP_APPDATA returned %#x\n", hr );
+    ok( prop_pointer.uData == 0xfeedcafe, "got %p expected %p\n", (void *)prop_pointer.uData, (void *)0xfeedcafe );
+
+    prop_dword.diph.dwHow = DIPH_DEVICE;
+    prop_dword.diph.dwObj = 0;
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
+    prop_dword.dwData = 1000;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_FFGAIN, &prop_dword.diph );
+    ok( hr == DI_OK, "SetProperty DIPROP_FFGAIN returned %#x\n", hr );
+
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_CALIBRATION, &prop_dword.diph );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_CALIBRATION returned %#x\n", hr );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_DEADZONE, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_DEADZONE returned %#x\n", hr );
+    prop_dword.dwData = 0xdeadbeef;
+    hr = IDirectInputDevice8_SetProperty( device, DIPROP_SATURATION, &prop_dword.diph );
+    ok( hr == DIERR_INVALIDPARAM, "SetProperty DIPROP_SATURATION returned %#x\n", hr );
+
+    for (i = 0; i < ARRAY_SIZE(injected_input); ++i)
+    {
+        winetest_push_context( "state[%d]", i );
+        hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+        ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+        todo_wine
+        check_member( state, expect_state_rel[i], "%d", lX );
+        todo_wine
+        check_member( state, expect_state_rel[i], "%d", lY );
+        todo_wine
+        check_member( state, expect_state_rel[i], "%d", lZ );
+        check_member( state, expect_state_rel[i], "%d", lRx );
+        check_member( state, expect_state_rel[i], "%d", rgdwPOV[0] );
+        check_member( state, expect_state_rel[i], "%d", rgdwPOV[1] );
+        check_member( state, expect_state_rel[i], "%#x", rgbButtons[0] );
+        check_member( state, expect_state_rel[i], "%#x", rgbButtons[1] );
+        check_member( state, expect_state_rel[i], "%#x", rgbButtons[2] );
+
+        send_hid_input( file, &injected_input[i], sizeof(*injected_input) );
+
+        res = WaitForSingleObject( event, 100 );
+        if (i == 3) ok( res == WAIT_TIMEOUT, "WaitForSingleObject succeeded\n" );
+        else ok( res == WAIT_OBJECT_0, "WaitForSingleObject failed\n" );
+        ResetEvent( event );
+        winetest_pop_context();
+    }
+
+    hr = IDirectInputDevice8_GetDeviceState( device, sizeof(DIJOYSTATE2), &state );
+    ok( hr == DI_OK, "GetDeviceState returned: %#x\n", hr );
+    winetest_push_context( "state[%d]", i );
+    todo_wine
+    check_member( state, expect_state_rel[i], "%d", lX );
+    todo_wine
+    check_member( state, expect_state_rel[i], "%d", lY );
+    todo_wine
+    check_member( state, expect_state_rel[i], "%d", lZ );
+    check_member( state, expect_state_rel[i], "%d", lRx );
+    check_member( state, expect_state_rel[i], "%d", rgdwPOV[0] );
+    check_member( state, expect_state_rel[i], "%d", rgdwPOV[1] );
+    check_member( state, expect_state_rel[i], "%#x", rgbButtons[0] );
+    check_member( state, expect_state_rel[i], "%#x", rgbButtons[1] );
+    check_member( state, expect_state_rel[i], "%#x", rgbButtons[2] );
+    winetest_pop_context();
+
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, NULL );
+    ok( hr == E_POINTER, "GetForceFeedbackState returned %#x\n", hr );
+    res = 0xdeadbeef;
+    hr = IDirectInputDevice8_GetForceFeedbackState( device, &res );
+    ok( hr == DIERR_UNSUPPORTED, "GetForceFeedbackState returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, 0xdeadbeef );
+    ok( hr == DIERR_INVALIDPARAM, "SendForceFeedbackCommand returned %#x\n", hr );
+    hr = IDirectInputDevice8_SendForceFeedbackCommand( device, DISFFC_RESET );
+    ok( hr == DIERR_UNSUPPORTED, "SendForceFeedbackCommand returned %#x\n", hr );
+
+    objdata[0].dwOfs = 0xd;
+    objdata[0].dwData = 0x80;
+    res = 1;
+    hr = IDirectInputDevice8_SendDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0xdeadbeef );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
+    res = 1;
+    hr = IDirectInputDevice8_SendDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 1 /*DISDD_CONTINUE*/ );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
+    res = 1;
+    hr = IDirectInputDevice8_SendDeviceData( device, sizeof(DIDEVICEOBJECTDATA), objdata, &res, 0 );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, NULL, NULL );
+    ok( hr == E_POINTER, "CreateEffect returned %#x\n", hr );
+    hr = IDirectInputDevice8_CreateEffect( device, NULL, NULL, &effect, NULL );
+    ok( hr == DIERR_UNSUPPORTED, "CreateEffect returned %#x\n", hr );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_NULL, NULL, &effect, NULL );
+    ok( hr == DIERR_UNSUPPORTED, "CreateEffect returned %#x\n", hr );
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
+    ok( hr == DIERR_UNSUPPORTED, "CreateEffect returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_Unacquire( device );
+    ok( hr == DI_OK, "Unacquire returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_CreateEffect( device, &GUID_Sine, NULL, &effect, NULL );
+    ok( hr == DIERR_UNSUPPORTED, "CreateEffect returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, NULL, effect, 0 );
+    ok( hr == DIERR_INVALIDPARAM, "EnumCreatedEffectObjects returned %#x\n", hr );
+    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_no_created_effect_objects, effect, 0xdeadbeef );
+    ok( hr == DIERR_INVALIDPARAM, "EnumCreatedEffectObjects returned %#x\n", hr );
+    hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_no_created_effect_objects, (void *)0xdeadbeef, 0 );
+    ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#x\n", hr );
+
+    hr = IDirectInputDevice8_Escape( device, NULL );
+    todo_wine
+    ok( hr == E_POINTER, "Escape returned: %#x\n", hr );
+    hr = IDirectInputDevice8_Escape( device, &escape );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "Escape returned: %#x\n", hr );
+    escape.dwSize = sizeof(DIEFFESCAPE) + 1;
+    hr = IDirectInputDevice8_Escape( device, &escape );
+    todo_wine
+    ok( hr == DIERR_INVALIDPARAM, "Escape returned: %#x\n", hr );
+    escape.dwSize = sizeof(DIEFFESCAPE);
+    escape.dwCommand = 0;
+    escape.lpvInBuffer = buffer;
+    escape.cbInBuffer = 10;
+    escape.lpvOutBuffer = buffer + 10;
+    escape.cbOutBuffer = 10;
+    hr = IDirectInputDevice8_Escape( device, &escape );
+    todo_wine
+    ok( hr == DIERR_UNSUPPORTED, "Escape returned: %#x\n", hr );
+
+    ref = IDirectInputDevice8_Release( device );
+    ok( ref == 0, "Release returned %d\n", ref );
+
+    CloseHandle( event );
+    CloseHandle( file );
+
+done:
+    pnp_driver_stop();
+    cleanup_registry_keys();
+    SetCurrentDirectoryW( cwd );
+}
+
+struct device_desc
+{
+    const BYTE *report_desc_buf;
+    ULONG report_desc_len;
+    HIDP_CAPS hid_caps;
+};
+
+static BOOL test_device_types( DWORD version )
+{
+#include "psh_hid_macros.h"
+    static const unsigned char unknown_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,
+    };
+    static const unsigned char limited_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(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,
+    };
+    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,
+    };
+    static const unsigned char joystick_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(1, HID_USAGE_GENERIC_X),
+                USAGE(1, HID_USAGE_GENERIC_Y),
+                USAGE(1, HID_USAGE_GENERIC_Z),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 127),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 127),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 3),
+                INPUT(1, Data|Var|Abs),
+
+                USAGE(1, HID_USAGE_GENERIC_HATSWITCH),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 8),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 8),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                INPUT(1, Data|Var|Abs|Null),
+
+                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
+                USAGE_MINIMUM(1, 1),
+                USAGE_MAXIMUM(1, 5),
+                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 struct device_desc device_desc[] =
+    {
+        {
+            .report_desc_buf = unknown_desc,
+            .report_desc_len = sizeof(unknown_desc),
+            .hid_caps =
+            {
+                .InputReportByteLength = 1,
+            },
+        },
+        {
+            .report_desc_buf = limited_desc,
+            .report_desc_len = sizeof(limited_desc),
+            .hid_caps =
+            {
+                .InputReportByteLength = 3,
+            },
+        },
+        {
+            .report_desc_buf = gamepad_desc,
+            .report_desc_len = sizeof(gamepad_desc),
+            .hid_caps =
+            {
+                .InputReportByteLength = 3,
+            },
+        },
+        {
+            .report_desc_buf = joystick_desc,
+            .report_desc_len = sizeof(joystick_desc),
+            .hid_caps =
+            {
+                .InputReportByteLength = 5,
+            },
+        },
+    };
+    const DIDEVCAPS expect_caps[] =
+    {
+        {
+            .dwSize = sizeof(DIDEVCAPS),
+            .dwFlags = DIDC_ATTACHED|DIDC_EMULATED,
+            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPESUPPLEMENTAL_UNKNOWN << 8)|DI8DEVTYPE_SUPPLEMENTAL
+                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
+            .dwButtons = 6,
+        },
+        {
+            .dwSize = sizeof(DIDEVCAPS),
+            .dwFlags = DIDC_ATTACHED|DIDC_EMULATED,
+            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEJOYSTICK_LIMITED << 8)|DI8DEVTYPE_JOYSTICK
+                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
+            .dwAxes = 2,
+            .dwButtons = 6,
+        },
+        {
+            .dwSize = sizeof(DIDEVCAPS),
+            .dwFlags = DIDC_ATTACHED|DIDC_EMULATED,
+            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEGAMEPAD_STANDARD << 8)|DI8DEVTYPE_GAMEPAD
+                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_GAMEPAD << 8)|DIDEVTYPE_JOYSTICK,
+            .dwAxes = 2,
+            .dwButtons = 6,
+        },
+        {
+            .dwSize = sizeof(DIDEVCAPS),
+            .dwFlags = DIDC_ATTACHED|DIDC_EMULATED,
+            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEJOYSTICK_STANDARD << 8)|DI8DEVTYPE_JOYSTICK
+                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
+            .dwAxes = 3,
+            .dwPOVs = 1,
+            .dwButtons = 5,
+        },
+    };
+
+    const DIDEVICEINSTANCEW expect_devinst[] =
+    {
+        {
+            .dwSize = sizeof(DIDEVICEINSTANCEW),
+            .guidInstance = expect_guid_product,
+            .guidProduct = expect_guid_product,
+            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPESUPPLEMENTAL_UNKNOWN << 8)|DI8DEVTYPE_SUPPLEMENTAL
+                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
+            .tszInstanceName = L"Wine test root driver",
+            .tszProductName = L"Wine test root driver",
+            .guidFFDriver = GUID_NULL,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEINSTANCEW),
+            .guidInstance = expect_guid_product,
+            .guidProduct = expect_guid_product,
+            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEJOYSTICK_LIMITED << 8)|DI8DEVTYPE_JOYSTICK
+                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
+            .tszInstanceName = L"Wine test root driver",
+            .tszProductName = L"Wine test root driver",
+            .guidFFDriver = GUID_NULL,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEINSTANCEW),
+            .guidInstance = expect_guid_product,
+            .guidProduct = expect_guid_product,
+            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEGAMEPAD_STANDARD << 8)|DI8DEVTYPE_GAMEPAD
+                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_GAMEPAD << 8)|DIDEVTYPE_JOYSTICK,
+            .tszInstanceName = L"Wine test root driver",
+            .tszProductName = L"Wine test root driver",
+            .guidFFDriver = GUID_NULL,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_GAMEPAD,
+        },
+        {
+            .dwSize = sizeof(DIDEVICEINSTANCEW),
+            .guidInstance = expect_guid_product,
+            .guidProduct = expect_guid_product,
+            .dwDevType = version >= 0x800 ? DIDEVTYPE_HID|(DI8DEVTYPEJOYSTICK_STANDARD << 8)|DI8DEVTYPE_JOYSTICK
+                                          : DIDEVTYPE_HID|(DIDEVTYPEJOYSTICK_UNKNOWN << 8)|DIDEVTYPE_JOYSTICK,
+            .tszInstanceName = L"Wine test root driver",
+            .tszProductName = L"Wine test root driver",
+            .guidFFDriver = GUID_NULL,
+            .wUsagePage = HID_USAGE_PAGE_GENERIC,
+            .wUsage = HID_USAGE_GENERIC_JOYSTICK,
+        },
+    };
+
+    DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
+    DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
+    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
+    IDirectInputDevice8W *device;
+    BOOL success = TRUE;
+    ULONG i, ref;
+    HRESULT hr;
+
+    winetest_push_context( "%#x", version );
+
+    for (i = 0; i < ARRAY_SIZE(device_desc) && success; ++i)
+    {
+        winetest_push_context( "desc[%d]", i );
+        GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
+        GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
+        SetCurrentDirectoryW( tempdir );
+
+        cleanup_registry_keys();
+        if (!dinput_driver_start( device_desc[i].report_desc_buf, device_desc[i].report_desc_len,
+                                  &device_desc[i].hid_caps, NULL, 0 ))
+        {
+            success = FALSE;
+            goto done;
+        }
+
+        if (FAILED(hr = dinput_test_create_device( version, &devinst, &device )))
+        {
+            success = FALSE;
+            goto done;
+        }
+
+        hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst );
+        ok( hr == DI_OK, "GetDeviceInfo returned %#x\n", hr );
+        check_member( devinst, expect_devinst[i], "%d", dwSize );
+        todo_wine
+        check_member_guid( devinst, expect_devinst[i], guidInstance );
+        check_member_guid( devinst, expect_devinst[i], guidProduct );
+        todo_wine_if( version <= 0x700 && i == 3 )
+        check_member( devinst, expect_devinst[i], "%#x", dwDevType );
+        todo_wine
+        check_member_wstr( devinst, expect_devinst[i], tszInstanceName );
+        todo_wine
+        check_member_wstr( devinst, expect_devinst[i], tszProductName );
+        check_member_guid( devinst, expect_devinst[i], guidFFDriver );
+        check_member( devinst, expect_devinst[i], "%04x", wUsagePage );
+        check_member( devinst, expect_devinst[i], "%04x", wUsage );
+
+        hr = IDirectInputDevice8_GetCapabilities( device, &caps );
+        ok( hr == DI_OK, "GetCapabilities returned %#x\n", hr );
+        check_member( caps, expect_caps[i], "%d", dwSize );
+        check_member( caps, expect_caps[i], "%#x", dwFlags );
+        todo_wine_if( version <= 0x700 && i == 3 )
+        check_member( caps, expect_caps[i], "%#x", dwDevType );
+        check_member( caps, expect_caps[i], "%d", dwAxes );
+        check_member( caps, expect_caps[i], "%d", dwButtons );
+        check_member( caps, expect_caps[i], "%d", dwPOVs );
+        check_member( caps, expect_caps[i], "%d", dwFFSamplePeriod );
+        check_member( caps, expect_caps[i], "%d", dwFFMinTimeResolution );
+        check_member( caps, expect_caps[i], "%d", dwFirmwareRevision );
+        check_member( caps, expect_caps[i], "%d", dwHardwareRevision );
+        check_member( caps, expect_caps[i], "%d", dwFFDriverVersion );
+
+        ref = IDirectInputDevice8_Release( device );
+        ok( ref == 0, "Release returned %d\n", ref );
+
+    done:
+        pnp_driver_stop();
+        cleanup_registry_keys();
+        SetCurrentDirectoryW( cwd );
+        winetest_pop_context();
+    }
+
+    winetest_pop_context();
+
+    return success;
+}
+
+static BOOL test_winmm_joystick(void)
+{
+#include "psh_hid_macros.h"
+    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, Report),
+                REPORT_ID(1, 1),
+
+                USAGE(1, HID_USAGE_GENERIC_X),
+                USAGE(1, HID_USAGE_GENERIC_Y),
+                USAGE(1, HID_USAGE_GENERIC_Z),
+                USAGE(1, HID_USAGE_GENERIC_WHEEL),
+                USAGE(1, HID_USAGE_GENERIC_SLIDER),
+                USAGE(1, HID_USAGE_GENERIC_RX),
+                USAGE(1, HID_USAGE_GENERIC_RY),
+                USAGE(1, HID_USAGE_GENERIC_RZ),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(4, 0xffff),
+                PHYSICAL_MINIMUM(1, 1),
+                PHYSICAL_MAXIMUM(4, 0xffff),
+                REPORT_SIZE(1, 16),
+                REPORT_COUNT(1, 8),
+                INPUT(1, Data|Var|Abs),
+
+                USAGE(1, HID_USAGE_GENERIC_HATSWITCH),
+                LOGICAL_MINIMUM(1, 1),
+                LOGICAL_MAXIMUM(1, 8),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 8),
+                REPORT_SIZE(1, 4),
+                REPORT_COUNT(1, 1),
+                INPUT(1, Data|Var|Abs|Null),
+
+                USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
+                USAGE_MINIMUM(1, 1),
+                USAGE_MAXIMUM(1, 4),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                PHYSICAL_MINIMUM(1, 0),
+                PHYSICAL_MAXIMUM(1, 1),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 4),
+                INPUT(1, Data|Var|Abs),
+            END_COLLECTION,
+        END_COLLECTION,
+    };
+#include "pop_hid_macros.h"
+
+    static const HIDP_CAPS hid_caps =
+    {
+        .InputReportByteLength = 18,
+    };
+    static const JOYCAPS2W expect_regcaps =
+    {
+        .szRegKey = L"DINPUT.DLL",
+    };
+    static const JOYCAPS2W expect_caps =
+    {
+        .wMid = 0x1209,
+        .wPid = 0x0001,
+        .szPname = L"Microsoft PC-joystick driver",
+        .wXmax = 0xffff,
+        .wYmax = 0xffff,
+        .wZmax = 0xffff,
+        .wNumButtons = 4,
+        .wPeriodMin = 10,
+        .wPeriodMax = 1000,
+        .wRmax = 0xffff,
+        .wUmax = 0xffff,
+        .wVmax = 0xffff,
+        .wCaps = JOYCAPS_HASZ | JOYCAPS_HASR | JOYCAPS_HASU | JOYCAPS_HASV | JOYCAPS_HASPOV | JOYCAPS_POV4DIR,
+        .wMaxAxes = 6,
+        .wNumAxes = 6,
+        .wMaxButtons = 32,
+        .szRegKey = L"DINPUT.DLL",
+    };
+    struct hid_expect injected_input[] =
+    {
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_buf = {1,0x00,0x00,0x00,0x08,0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x38,0xf1},
+        },
+        {
+            .code = IOCTL_HID_READ_REPORT,
+            .report_buf = {1,0x00,0x38,0x00,0x30,0x00,0x28,0x00,0x20,0x00,0x18,0x00,0x10,0x00,0x08,0x00,0x00,0x63},
+        },
+    };
+    static const JOYINFOEX expect_infoex[] =
+    {
+        {
+            .dwSize = sizeof(JOYINFOEX), .dwFlags = 0xff,
+            .dwXpos = 0x7fff, .dwYpos = 0x7fff, .dwZpos = 0x7fff, .dwRpos = 0x7fff, .dwUpos = 0x7fff, .dwVpos = 0x7fff,
+            .dwButtons = 0, .dwButtonNumber = 0, .dwPOV = 0xffff,
+            .dwReserved1 = 0xcdcdcdcd, .dwReserved2 = 0xcdcdcdcd,
+        },
+        {
+            .dwSize = sizeof(JOYINFOEX), .dwFlags = 0xff,
+            .dwXpos = 0, .dwYpos = 0x07ff, .dwZpos = 0x17ff, .dwRpos = 0x37ff, .dwUpos = 0x1fff, .dwVpos = 0x27ff,
+            .dwButtons = 0xf, .dwButtonNumber = 0x4, .dwPOV = 0,
+            .dwReserved1 = 0xcdcdcdcd, .dwReserved2 = 0xcdcdcdcd,
+        },
+        {
+            .dwSize = sizeof(JOYINFOEX), .dwFlags = 0xff,
+            .dwXpos = 0x37ff, .dwYpos = 0x2fff, .dwZpos = 0x1fff, .dwRpos = 0, .dwUpos = 0x17ff, .dwVpos = 0x0fff,
+            .dwButtons = 0x6, .dwButtonNumber = 0x2, .dwPOV = 0x2328,
+            .dwReserved1 = 0xcdcdcdcd, .dwReserved2 = 0xcdcdcdcd,
+        },
+    };
+    static const JOYINFO expect_info =
+    {
+        .wXpos = 0x7fff,
+        .wYpos = 0x7fff,
+        .wZpos = 0x7fff,
+        .wButtons = 0,
+    };
+    JOYINFOEX infoex = {.dwSize = sizeof(JOYINFOEX)};
+    DIPROPGUIDANDPATH prop_guid_path =
+    {
+        .diph =
+        {
+            .dwSize = sizeof(DIPROPGUIDANDPATH),
+            .dwHeaderSize = sizeof(DIPROPHEADER),
+            .dwHow = DIPH_DEVICE,
+        },
+    };
+    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
+    IDirectInputDevice8W *device = NULL;
+    DIDEVICEINSTANCEW devinst = {0};
+    JOYCAPS2W caps = {0};
+    JOYINFO info = {0};
+    HANDLE event, file;
+    HRESULT hr;
+    UINT ret;
+
+    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
+    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
+    SetCurrentDirectoryW( tempdir );
+
+    cleanup_registry_keys();
+
+    ret = joyGetNumDevs();
+    ok( ret == 16, "joyGetNumDevs returned %u\n", ret );
+
+    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) );
+    ok( ret == JOYERR_PARMS, "joyGetDevCapsW returned %u\n", ret );
+
+    memset( &caps, 0xcd, sizeof(caps) );
+    ret = joyGetDevCapsW( -1, (JOYCAPSW *)&caps, sizeof(caps) );
+    ok( ret == 0, "joyGetDevCapsW returned %u\n", ret );
+    check_member( caps, expect_regcaps, "%#x", wMid );
+    check_member( caps, expect_regcaps, "%#x", wPid );
+    check_member_wstr( caps, expect_regcaps, szPname );
+    check_member( caps, expect_regcaps, "%#x", wXmin );
+    check_member( caps, expect_regcaps, "%#x", wXmax );
+    check_member( caps, expect_regcaps, "%#x", wYmin );
+    check_member( caps, expect_regcaps, "%#x", wYmax );
+    check_member( caps, expect_regcaps, "%#x", wZmin );
+    check_member( caps, expect_regcaps, "%#x", wZmax );
+    check_member( caps, expect_regcaps, "%#x", wNumButtons );
+    check_member( caps, expect_regcaps, "%#x", wPeriodMin );
+    check_member( caps, expect_regcaps, "%#x", wPeriodMax );
+    check_member( caps, expect_regcaps, "%#x", wRmin );
+    check_member( caps, expect_regcaps, "%#x", wRmax );
+    check_member( caps, expect_regcaps, "%#x", wUmin );
+    check_member( caps, expect_regcaps, "%#x", wUmax );
+    check_member( caps, expect_regcaps, "%#x", wVmin );
+    check_member( caps, expect_regcaps, "%#x", wVmax );
+    check_member( caps, expect_regcaps, "%#x", wCaps );
+    check_member( caps, expect_regcaps, "%#x", wMaxAxes );
+    check_member( caps, expect_regcaps, "%#x", wNumAxes );
+    check_member( caps, expect_regcaps, "%#x", wMaxButtons );
+    check_member_wstr( caps, expect_regcaps, szRegKey );
+    check_member_wstr( caps, expect_regcaps, szOEMVxD );
+    check_member_guid( caps, expect_regcaps, ManufacturerGuid );
+    check_member_guid( caps, expect_regcaps, ProductGuid );
+    check_member_guid( caps, expect_regcaps, NameGuid );
+
+    if (!dinput_driver_start( report_desc, sizeof(report_desc), &hid_caps, NULL, 0 )) goto done;
+
+    ret = joyGetNumDevs();
+    ok( ret == 16, "joyGetNumDevs returned %u\n", ret );
+
+    ret = joyGetPosEx( 1, &infoex );
+    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
+    ret = joyGetPosEx( 0, &infoex );
+    /* first call for an index sometimes fail */
+    if (ret == JOYERR_PARMS) ret = joyGetPosEx( 0, &infoex );
+    ok( ret == 0, "joyGetPosEx returned %u\n", ret );
+
+    ret = joyGetDevCapsW( 1, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) );
+    ok( ret == JOYERR_PARMS, "joyGetDevCapsW returned %u\n", ret );
+
+    memset( &caps, 0xcd, sizeof(caps) );
+    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(caps) );
+    ok( ret == 0, "joyGetDevCapsW returned %u\n", ret );
+    check_member( caps, expect_caps, "%#x", wMid );
+    check_member( caps, expect_caps, "%#x", wPid );
+    todo_wine
+    check_member_wstr( caps, expect_caps, szPname );
+    check_member( caps, expect_caps, "%#x", wXmin );
+    check_member( caps, expect_caps, "%#x", wXmax );
+    check_member( caps, expect_caps, "%#x", wYmin );
+    check_member( caps, expect_caps, "%#x", wYmax );
+    check_member( caps, expect_caps, "%#x", wZmin );
+    check_member( caps, expect_caps, "%#x", wZmax );
+    check_member( caps, expect_caps, "%#x", wNumButtons );
+    check_member( caps, expect_caps, "%#x", wPeriodMin );
+    check_member( caps, expect_caps, "%#x", wPeriodMax );
+    check_member( caps, expect_caps, "%#x", wRmin );
+    check_member( caps, expect_caps, "%#x", wRmax );
+    check_member( caps, expect_caps, "%#x", wUmin );
+    check_member( caps, expect_caps, "%#x", wUmax );
+    check_member( caps, expect_caps, "%#x", wVmin );
+    check_member( caps, expect_caps, "%#x", wVmax );
+    check_member( caps, expect_caps, "%#x", wCaps );
+    check_member( caps, expect_caps, "%#x", wMaxAxes );
+    check_member( caps, expect_caps, "%#x", wNumAxes );
+    check_member( caps, expect_caps, "%#x", wMaxButtons );
+    check_member_wstr( caps, expect_caps, szRegKey );
+    check_member_wstr( caps, expect_caps, szOEMVxD );
+    check_member_guid( caps, expect_caps, ManufacturerGuid );
+    check_member_guid( caps, expect_caps, ProductGuid );
+    check_member_guid( caps, expect_caps, NameGuid );
+
+    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) );
+    ok( ret == 0, "joyGetDevCapsW returned %u\n", ret );
+    ret = joyGetDevCapsW( 0, NULL, sizeof(JOYCAPSW) );
+    ok( ret == MMSYSERR_INVALPARAM, "joyGetDevCapsW returned %u\n", ret );
+    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) + 4 );
+    ok( ret == JOYERR_PARMS, "joyGetDevCapsW returned %u\n", ret );
+    ret = joyGetDevCapsW( 0, (JOYCAPSW *)&caps, sizeof(JOYCAPSW) - 4 );
+    ok( ret == JOYERR_PARMS, "joyGetDevCapsW returned %u\n", ret );
+
+    infoex.dwSize = sizeof(JOYINFOEX);
+    infoex.dwFlags = JOY_RETURNALL;
+    ret = joyGetPosEx( -1, &infoex );
+    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
+    ret = joyGetPosEx( 1, &infoex );
+    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
+    ret = joyGetPosEx( 16, &infoex );
+    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
+
+    memset( &infoex, 0xcd, sizeof(infoex) );
+    infoex.dwSize = sizeof(JOYINFOEX);
+    infoex.dwFlags = JOY_RETURNALL;
+    ret = joyGetPosEx( 0, &infoex );
+    ok( ret == 0, "joyGetPosEx returned %u\n", ret );
+    check_member( infoex, expect_infoex[0], "%#x", dwSize );
+    check_member( infoex, expect_infoex[0], "%#x", dwFlags );
+    check_member( infoex, expect_infoex[0], "%#x", dwXpos );
+    check_member( infoex, expect_infoex[0], "%#x", dwYpos );
+    check_member( infoex, expect_infoex[0], "%#x", dwZpos );
+    check_member( infoex, expect_infoex[0], "%#x", dwRpos );
+    check_member( infoex, expect_infoex[0], "%#x", dwUpos );
+    check_member( infoex, expect_infoex[0], "%#x", dwVpos );
+    check_member( infoex, expect_infoex[0], "%#x", dwButtons );
+    check_member( infoex, expect_infoex[0], "%#x", dwButtonNumber );
+    check_member( infoex, expect_infoex[0], "%#x", dwPOV );
+    check_member( infoex, expect_infoex[0], "%#x", dwReserved1 );
+    check_member( infoex, expect_infoex[0], "%#x", dwReserved2 );
+
+    infoex.dwSize = sizeof(JOYINFOEX) - 4;
+    ret = joyGetPosEx( 0, &infoex );
+    ok( ret == JOYERR_PARMS, "joyGetPosEx returned %u\n", ret );
+
+    ret = joyGetPos( -1, &info );
+    ok( ret == JOYERR_PARMS, "joyGetPos returned %u\n", ret );
+    ret = joyGetPos( 1, &info );
+    ok( ret == JOYERR_PARMS, "joyGetPos returned %u\n", ret );
+    memset( &info, 0xcd, sizeof(info) );
+    ret = joyGetPos( 0, &info );
+    ok( ret == 0, "joyGetPos returned %u\n", ret );
+    check_member( info, expect_info, "%#x", wXpos );
+    check_member( info, expect_info, "%#x", wYpos );
+    check_member( info, expect_info, "%#x", wZpos );
+    check_member( info, expect_info, "%#x", wButtons );
+
+    if (FAILED(hr = dinput_test_create_device( DIRECTINPUT_VERSION, &devinst, &device ))) goto done;
+
+    event = CreateEventW( NULL, FALSE, FALSE, NULL );
+    ok( event != NULL, "CreateEventW failed, last error %u\n", GetLastError() );
+    hr = IDirectInputDevice8_SetEventNotification( device, event );
+    ok( hr == DI_OK, "SetEventNotification returned: %#x\n", hr );
+
+    hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph );
+    ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr );
+    file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
+    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
+
+    hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 );
+    ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr );
+    hr = IDirectInputDevice8_SetCooperativeLevel( device, NULL, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE );
+    ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr );
+    hr = IDirectInputDevice8_Acquire( device );
+    ok( hr == DI_OK, "Acquire returned: %#x\n", hr );
+
+    send_hid_input( file, &injected_input[0], sizeof(struct hid_expect) );
+    ret = WaitForSingleObject( event, 100 );
+    ok( ret != WAIT_TIMEOUT, "WaitForSingleObject returned %#x\n", ret );
+    Sleep( 50 ); /* leave some time for winmm to keep up */
+
+    memset( &infoex, 0xcd, sizeof(infoex) );
+    infoex.dwSize = sizeof(JOYINFOEX);
+    infoex.dwFlags = JOY_RETURNALL;
+    ret = joyGetPosEx( 0, &infoex );
+    ok( ret == 0, "joyGetPosEx returned %u\n", ret );
+    check_member( infoex, expect_infoex[1], "%#x", dwSize );
+    check_member( infoex, expect_infoex[1], "%#x", dwFlags );
+    check_member( infoex, expect_infoex[1], "%#x", dwXpos );
+    check_member( infoex, expect_infoex[1], "%#x", dwYpos );
+    check_member( infoex, expect_infoex[1], "%#x", dwZpos );
+    check_member( infoex, expect_infoex[1], "%#x", dwRpos );
+    check_member( infoex, expect_infoex[1], "%#x", dwUpos );
+    check_member( infoex, expect_infoex[1], "%#x", dwVpos );
+    check_member( infoex, expect_infoex[1], "%#x", dwButtons );
+    check_member( infoex, expect_infoex[1], "%#x", dwButtonNumber );
+    check_member( infoex, expect_infoex[1], "%#x", dwPOV );
+
+    send_hid_input( file, &injected_input[1], sizeof(struct hid_expect) );
+    ret = WaitForSingleObject( event, 100 );
+    ok( ret != WAIT_TIMEOUT, "WaitForSingleObject returned %#x\n", ret );
+    Sleep( 50 ); /* leave some time for winmm to keep up */
+
+    memset( &infoex, 0xcd, sizeof(infoex) );
+    infoex.dwSize = sizeof(JOYINFOEX);
+    infoex.dwFlags = JOY_RETURNALL;
+    ret = joyGetPosEx( 0, &infoex );
+    ok( ret == 0, "joyGetPosEx returned %u\n", ret );
+    check_member( infoex, expect_infoex[2], "%#x", dwSize );
+    check_member( infoex, expect_infoex[2], "%#x", dwFlags );
+    check_member( infoex, expect_infoex[2], "%#x", dwXpos );
+    check_member( infoex, expect_infoex[2], "%#x", dwYpos );
+    check_member( infoex, expect_infoex[2], "%#x", dwZpos );
+    check_member( infoex, expect_infoex[2], "%#x", dwRpos );
+    check_member( infoex, expect_infoex[2], "%#x", dwUpos );
+    check_member( infoex, expect_infoex[2], "%#x", dwVpos );
+    check_member( infoex, expect_infoex[2], "%#x", dwButtons );
+    check_member( infoex, expect_infoex[2], "%#x", dwButtonNumber );
+    check_member( infoex, expect_infoex[2], "%#x", dwPOV );
+
+    ret = IDirectInputDevice8_Release( device );
+    ok( ret == 0, "Release returned %d\n", ret );
+
+    CloseHandle( event );
+    CloseHandle( file );
+
+done:
+    pnp_driver_stop();
+    cleanup_registry_keys();
+    SetCurrentDirectoryW( cwd );
+
+    return device != NULL;
+}
+
+START_TEST( joystick8 )
+{
+    if (!dinput_test_init()) return;
+
+    CoInitialize( NULL );
+    if (test_device_types( 0x800 ))
+    {
+        /* This needs to be done before doing anything involving dinput.dll
+         * on Windows, or the tests will fail, dinput8.dll is fine though. */
+        test_winmm_joystick();
+
+        test_device_types( 0x500 );
+        test_device_types( 0x700 );
+
+        test_simple_joystick();
+    }
+    CoUninitialize();
+
+    dinput_test_exit();
+}
-- 
2.34.1




More information about the wine-devel mailing list