text: /*
   * Devinst tests
   *
   * Copyright 2006 Christian Gmeiner
   *
   * 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
   */
  
  #include <stdarg.h>
  #include <stdio.h>
  
  #include "windef.h"
  #include "winbase.h"
  #include "wingdi.h"
  #include "winuser.h"
  #include "winreg.h"
  #include "initguid.h"
  #include "devguid.h"
  #include "devpkey.h"
  #include "setupapi.h"
  #include "cfgmgr32.h"
  #include "cguid.h"
  
  #include "wine/heap.h"
  #include "wine/test.h"
  
  /* This is a unique guid for testing purposes */
  static GUID guid = {0x6a55b5a4, 0x3f65, 0x11db, {0xb7,0x04,0x00,0x11,0x95,0x5c,0x2b,0xdb}};
  static GUID guid2 = {0x6a55b5a5, 0x3f65, 0x11db, {0xb7,0x04,0x00,0x11,0x95,0x5c,0x2b,0xdb}};
  static GUID iface_guid = {0xdeadbeef, 0x3f65, 0x11db, {0xb7,0x04,0x00,0x11,0x95,0x5c,0x2b,0xdb}};
  static GUID iface_guid2 = {0xdeadf00d, 0x3f65, 0x11db, {0xb7,0x04,0x00,0x11,0x95,0x5c,0x2b,0xdb}};
  
  BOOL (WINAPI *pSetupDiSetDevicePropertyW)(HDEVINFO, PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE, const BYTE *, DWORD, DWORD);
  BOOL (WINAPI *pSetupDiGetDevicePropertyW)(HDEVINFO, PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, BYTE *, DWORD, DWORD *, DWORD);
  
  static BOOL wow64;
  
  static void create_file(const char *name, const char *data)
  {
      HANDLE file;
      DWORD size;
      BOOL ret;
  
      file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
      ok(file != INVALID_HANDLE_VALUE, "Failed to create %s, error %u.\n", name, GetLastError());
      ret = WriteFile(file, data, strlen(data), &size, NULL);
      ok(ret && size == strlen(data), "Failed to write %s, error %u.\n", name, GetLastError());
      CloseHandle(file);
  }
  
  static void load_resource(const char *name, const char *filename)
  {
      DWORD written;
      HANDLE file;
      HRSRC res;
      void *ptr;
  
      file = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
      ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", filename, GetLastError());
  
      res = FindResourceA(NULL, name, "TESTDLL");
      ok( res != 0, "couldn't find resource\n" );
      ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res ));
      WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL );
      ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" );
      CloseHandle( file );
  }
  
  static void test_create_device_list_ex(void)
  {
      static const WCHAR machine[] = { 'd','u','m','m','y',0 };
      static const WCHAR empty[] = { 0 };
      static char notnull[] = "NotNull";
      HDEVINFO set;
      BOOL ret;
  
      SetLastError(0xdeadbeef);
      set = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, notnull);
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      set = SetupDiCreateDeviceInfoListExW(NULL, NULL, machine, NULL);
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_MACHINENAME
              || GetLastError() == ERROR_MACHINE_UNAVAILABLE
              || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
              "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiCreateDeviceInfoListExW(NULL, NULL, empty, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  }
  
  static void test_open_class_key(void)
  {
      static const char guidstr[] = "{6a55b5a4-3f65-11db-b704-0011955c2bdb}";
      HKEY root_key, class_key;
      LONG res;
  
      SetLastError(0xdeadbeef);
      class_key = SetupDiOpenClassRegKeyExA(&guid, KEY_ALL_ACCESS, DIOCR_INSTALLER, NULL, NULL);
      ok(class_key == INVALID_HANDLE_VALUE, "Expected failure.\n");
  todo_wine
      ok(GetLastError() == ERROR_INVALID_CLASS, "Got unexpected error %#x.\n", GetLastError());
  
      root_key = SetupDiOpenClassRegKey(NULL, KEY_ALL_ACCESS);
      ok(root_key != INVALID_HANDLE_VALUE, "Failed to open root key, error %#x.\n", GetLastError());
  
      res = RegCreateKeyA(root_key, guidstr, &class_key);
      ok(!res, "Failed to create class key, error %#x.\n", GetLastError());
      RegCloseKey(class_key);
  
      SetLastError(0xdeadbeef);
      class_key = SetupDiOpenClassRegKeyExA(&guid, KEY_ALL_ACCESS, DIOCR_INSTALLER, NULL, NULL);
      ok(class_key != INVALID_HANDLE_VALUE, "Failed to open class key, error %#x.\n", GetLastError());
      RegCloseKey(class_key);
  
      RegDeleteKeyA(root_key, guidstr);
      RegCloseKey(root_key);
  }
  
  static void get_temp_filename(LPSTR path)
  {
      static char curr[MAX_PATH] = { 0 };
      char temp[MAX_PATH];
      LPSTR ptr;
  
      if (!*curr)
          GetCurrentDirectoryA(MAX_PATH, curr);
      GetTempFileNameA(curr, "set", 0, temp);
      ptr = strrchr(temp, '\\');
  
      lstrcpyA(path, ptr + 1);
  }
  
  static void test_install_class(void)
  {
      static const WCHAR classKey[] = {'S','y','s','t','e','m','\\',
       'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
       'C','o','n','t','r','o','l','\\','C','l','a','s','s','\\',
       '{','6','a','5','5','b','5','a','4','-','3','f','6','5','-',
       '1','1','d','b','-','b','7','0','4','-',
       '0','0','1','1','9','5','5','c','2','b','d','b','}',0};
      char tmpfile[MAX_PATH];
      BOOL ret;
  
      static const char inf_data[] =
              "[Version]\n"
              "Signature=\"$Chicago$\"\n"
              "Class=Bogus\n"
              "ClassGUID={6a55b5a4-3f65-11db-b704-0011955c2bdb}\n"
              "[ClassInstall32]\n"
              "AddReg=BogusClass.NT.AddReg\n"
              "[BogusClass.NT.AddReg]\n"
              "HKR,,,,\"Wine test devices\"\n";
  
      tmpfile[0] = '.';
      tmpfile[1] = '\\';
      get_temp_filename(tmpfile + 2);
      create_file(tmpfile + 2, inf_data);
  
      ret = SetupDiInstallClassA(NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiInstallClassA(NULL, NULL, DI_NOVCP, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiInstallClassA(NULL, tmpfile + 2, DI_NOVCP, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiInstallClassA(NULL, tmpfile + 2, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Got unexpected error %#x.\n", GetLastError());
  
      /* The next call will succeed. Information is put into the registry but the
       * location(s) is/are depending on the Windows version.
       */
      ret = SetupDiInstallClassA(NULL, tmpfile, 0, NULL);
      ok(ret, "Failed to install class, error %#x.\n", GetLastError());
  
      ret = RegDeleteKeyW(HKEY_LOCAL_MACHINE, classKey);
      ok(!ret, "Failed to delete class key, error %u.\n", GetLastError());
      DeleteFileA(tmpfile);
  }
  
  static void check_device_info_(int line, HDEVINFO set, int index, const GUID *class, const char *expect_id)
  {
      SP_DEVINFO_DATA device = {sizeof(device)};
      char id[50];
      BOOL ret;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiEnumDeviceInfo(set, index, &device);
      if (expect_id)
      {
          ok_(__FILE__, line)(ret, "Got unexpected error %#x.\n", GetLastError());
          ok_(__FILE__, line)(IsEqualGUID(&device.ClassGuid, class),
                  "Got unexpected class %s.\n", wine_dbgstr_guid(&device.ClassGuid));
          ret = SetupDiGetDeviceInstanceIdA(set, &device, id, sizeof(id), NULL);
          ok_(__FILE__, line)(ret, "Got unexpected error %#x.\n", GetLastError());
          ok_(__FILE__, line)(!strcasecmp(id, expect_id), "Got unexpected id %s.\n", id);
      }
      else
      {
          ok_(__FILE__, line)(!ret, "Expected failure.\n");
          ok_(__FILE__, line)(GetLastError() == ERROR_NO_MORE_ITEMS,
                  "Got unexpected error %#x.\n", GetLastError());
      }
  }
  #define check_device_info(a,b,c,d) check_device_info_(__LINE__,a,b,c,d)
  
  static void test_device_info(void)
  {
      static const GUID deadbeef = {0xdeadbeef,0xdead,0xbeef,{0xde,0xad,0xbe,0xef,0xde,0xad,0xbe,0xef}};
      SP_DEVINFO_DATA device = {0}, ret_device = {sizeof(ret_device)};
      char id[MAX_DEVICE_ID_LEN + 2];
      HDEVINFO set;
      BOOL ret;
      INT i = 0;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(NULL, NULL, NULL, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DEVINST_NAME, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(NULL, "Root\\LEGACY_BOGUS\\0000", NULL, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", NULL, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &deadbeef, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_CLASS_MISMATCH, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &GUID_NULL, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_CLASS_MISMATCH, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, NULL);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\0000");
      check_device_info(set, 1, &guid, NULL);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_DEVINST_ALREADY_EXISTS, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0001", &guid, NULL, NULL, 0, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\0000");
      check_device_info(set, 1, &guid, "ROOT\\LEGACY_BOGUS\\0001");
      check_device_info(set, 2, &guid, NULL);
  
      device.cbSize = sizeof(device);
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0002", &guid, NULL, NULL, 0, &device);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
      ok(IsEqualGUID(&device.ClassGuid, &guid), "Got unexpected class %s.\n",
              wine_dbgstr_guid(&device.ClassGuid));
      ret = SetupDiGetDeviceInstanceIdA(set, &device, id, sizeof(id), NULL);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
      ok(!strcmp(id, "ROOT\\LEGACY_BOGUS\\0002"), "Got unexpected id %s.\n", id);
  
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\0000");
      check_device_info(set, 1, &guid, "ROOT\\LEGACY_BOGUS\\0001");
      check_device_info(set, 2, &guid, "ROOT\\LEGACY_BOGUS\\0002");
      check_device_info(set, 3, &guid, NULL);
  
      ret = SetupDiEnumDeviceInfo(set, 0, &ret_device);
      ok(ret, "Failed to enumerate devices, error %#x.\n", GetLastError());
      ret = SetupDiDeleteDeviceInfo(set, &ret_device);
      ok(ret, "Failed to delete device, error %#x.\n", GetLastError());
  
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\0001");
      check_device_info(set, 1, &guid, "ROOT\\LEGACY_BOGUS\\0002");
      check_device_info(set, 2, &guid, NULL);
  
      ret = SetupDiRemoveDevice(set, &device);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
  
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\0001");
  
      ret = SetupDiEnumDeviceInfo(set, 1, &ret_device);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
      ok(IsEqualGUID(&ret_device.ClassGuid, &guid), "Got unexpected class %s.\n",
              wine_dbgstr_guid(&ret_device.ClassGuid));
      ret = SetupDiGetDeviceInstanceIdA(set, &ret_device, id, sizeof(id), NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_NO_SUCH_DEVINST, "Got unexpected error %#x.\n", GetLastError());
      ok(ret_device.DevInst == device.DevInst, "Expected device node %#x, got %#x.\n",
              device.DevInst, ret_device.DevInst);
  
      check_device_info(set, 2, &guid, NULL);
  
      SetupDiDestroyDeviceInfoList(set);
  
      set = SetupDiCreateDeviceInfoList(NULL, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", NULL, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\deadbeef", &deadbeef, NULL, NULL, 0, NULL);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\null", &GUID_NULL, NULL, NULL, 0, NULL);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\testguid", &guid, NULL, NULL, 0, NULL);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      check_device_info(set, 0, &deadbeef, "ROOT\\LEGACY_BOGUS\\deadbeef");
      check_device_info(set, 1, &GUID_NULL, "ROOT\\LEGACY_BOGUS\\null");
      check_device_info(set, 2, &guid, "ROOT\\LEGACY_BOGUS\\testguid");
      check_device_info(set, 3, NULL, NULL);
  
      memset(id, 'x', sizeof(id));
      memcpy(id, "Root\\LEGACY_BOGUS\\", strlen("Root\\LEGACY_BOGUS\\"));
      id[MAX_DEVICE_ID_LEN + 1] = 0;
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(set, id, &guid, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DEVINST_NAME, "Got unexpected error %#x.\n", GetLastError());
  
      id[MAX_DEVICE_ID_LEN] = 0;
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInfoA(set, id, &guid, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DEVINST_NAME, "Got unexpected error %#x.\n", GetLastError());
  
      id[MAX_DEVICE_ID_LEN - 1] = 0;
      ret = SetupDiCreateDeviceInfoA(set, id, &guid, NULL, NULL, 0, NULL);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      SetupDiDestroyDeviceInfoList(set);
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiRegisterDeviceInfo(set , &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\0000");
      check_device_info(set, 1, NULL, NULL);
      SetupDiDestroyDeviceInfoList(set);
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(!ret, "Expect failure\n");
      ok(GetLastError() == ERROR_DEVINST_ALREADY_EXISTS, "Got error %#x\n", GetLastError());
      check_device_info(set, 0, NULL, NULL);
      SetupDiDestroyDeviceInfoList(set);
  
      set = SetupDiGetClassDevsA(&guid, NULL, NULL, 0);
      while (SetupDiEnumDeviceInfo(set, i++, &device))
      {
          ret = SetupDiRemoveDevice(set, &device);
          ok(ret, "Failed to remove device, error %#x.\n", GetLastError());
      }
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_device_property(void)
  {
      static const WCHAR valueW[] = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f', 0};
      SP_DEVINFO_DATA device_data = {sizeof(device_data)};
      HMODULE hmod;
      HDEVINFO set;
      DEVPROPTYPE type;
      DWORD size;
      BYTE buffer[256];
      DWORD err;
      BOOL ret;
  
      hmod = LoadLibraryA("setupapi.dll");
      pSetupDiSetDevicePropertyW = (void *)GetProcAddress(hmod, "SetupDiSetDevicePropertyW");
      pSetupDiGetDevicePropertyW = (void *)GetProcAddress(hmod, "SetupDiGetDevicePropertyW");
  
      if (!pSetupDiSetDevicePropertyW || !pSetupDiGetDevicePropertyW)
      {
          win_skip("SetupDi{Set,Get}DevicePropertyW() are only available on vista+, skipping tests.\n");
          FreeLibrary(hmod);
          return;
      }
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device_data);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      /* SetupDiSetDevicePropertyW */
      /* #1 Null device info list */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(NULL, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_HANDLE, "Expect last error %#x, got %#x\n", ERROR_INVALID_HANDLE, err);
  
      /* #2 Null device */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, NULL, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_PARAMETER, "Expect last error %#x, got %#x\n", ERROR_INVALID_PARAMETER, err);
  
      /* #3 Null property key pointer */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, NULL, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_DATA, "Expect last error %#x, got %#x\n", ERROR_INVALID_DATA, err);
  
      /* #4 Invalid property key type */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, 0xffff, (const BYTE *)valueW, sizeof(valueW), 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_DATA, "Expect last error %#x, got %#x\n", ERROR_INVALID_DATA, err);
  
      /* #5 Null buffer pointer */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, NULL, sizeof(valueW), 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_USER_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INVALID_USER_BUFFER, err);
  
      /* #6 Zero buffer size */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, 0, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_DATA, "Expect last error %#x, got %#x\n", ERROR_INVALID_DATA, err);
  
      /* #7 Flags not zero */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 1);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_FLAGS, "Expect last error %#x, got %#x\n", ERROR_INVALID_FLAGS, err);
  
      /* #8 Normal */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      err = GetLastError();
      ok(ret, "Expect success\n");
      ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
  
      /* #9 Delete property with buffer not null */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_EMPTY, (const BYTE *)valueW, 0, 0);
      err = GetLastError();
      ok(ret, "Expect success\n");
      ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
  
      /* #10 Delete property with size not zero */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_EMPTY, NULL, sizeof(valueW), 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_USER_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INVALID_USER_BUFFER, err);
  
      /* #11 Delete property */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_EMPTY, NULL, 0, 0);
      err = GetLastError();
      ok(ret, "Expect success\n");
      ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
  
      /* #12 Delete non-existent property */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_EMPTY, NULL, 0, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_NOT_FOUND, "Expect last error %#x, got %#x\n", ERROR_NOT_FOUND, err);
  
      /* #13 Delete property value with buffer not null */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, (const BYTE *)valueW, 0, 0);
      err = GetLastError();
      ok(ret, "Expect success\n");
      ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
  
      /* #14 Delete property value with size not zero */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, NULL, sizeof(valueW), 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_USER_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INVALID_USER_BUFFER, err);
  
      /* #15 Delete property value */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, NULL, 0, 0);
      err = GetLastError();
      ok(ret, "Expect success\n");
      ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
  
      /* #16 Delete non-existent property value */
      SetLastError(0xdeadbeef);
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, NULL, 0, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_NOT_FOUND, "Expect last error %#x, got %#x\n", ERROR_NOT_FOUND, err);
  
  
      /* SetupDiGetDevicePropertyW */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
  
      /* #1 Null device info list */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(NULL, &device_data, &DEVPKEY_Device_FriendlyName, &type, buffer, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_HANDLE, "Expect last error %#x, got %#x\n", ERROR_INVALID_HANDLE, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == 0, "Expect size %d, got %d\n", 0, size);
  
      /* #2 Null device */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, NULL, &DEVPKEY_Device_FriendlyName, &type, buffer, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_PARAMETER, "Expect last error %#x, got %#x\n", ERROR_INVALID_PARAMETER, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == 0, "Expect size %d, got %d\n", 0, size);
  
      /* #3 Null property key */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, NULL, &type, buffer, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_DATA, "Expect last error %#x, got %#x\n", ERROR_INVALID_DATA, err);
      ok(size == 0, "Expect size %d, got %d\n", 0, size);
  
      /* #4 Null property type */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, NULL, buffer, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_USER_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INVALID_USER_BUFFER, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == 0, "Expect size %d, got %d\n", 0, size);
  
      /* #5 Null buffer */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, NULL, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_USER_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INVALID_USER_BUFFER, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == 0, "Expect size %d, got %d\n", 0, size);
  
      /* #6 Null buffer with zero size and wrong type */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_UINT64;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, NULL, 0, &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INSUFFICIENT_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INSUFFICIENT_BUFFER, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == sizeof(valueW), "Got size %d\n", size);
  
      /* #7 Zero buffer size */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, buffer, 0, &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INSUFFICIENT_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INSUFFICIENT_BUFFER, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == sizeof(valueW), "Got size %d\n", size);
  
      /* #8 Null required size */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, buffer, sizeof(buffer), NULL, 0);
      err = GetLastError();
      ok(ret, "Expect success\n");
      ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
  
      /* #9 Flags not zero */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, buffer, sizeof(buffer), &size, 1);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INVALID_FLAGS, "Expect last error %#x, got %#x\n", ERROR_INVALID_FLAGS, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == 0, "Expect size %d, got %d\n", 0, size);
  
      /* #10 Non-existent property key */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_HardwareIds, &type, buffer, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_NOT_FOUND, "Expect last error %#x, got %#x\n", ERROR_NOT_FOUND, err);
      ok(size == 0, "Expect size %d, got %d\n", 0, size);
  
      /* #11 Wrong property key type */
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_UINT64;
      size = 0;
      memset(buffer, 0, sizeof(buffer));
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, buffer, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(ret, "Expect success\n");
      ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == sizeof(valueW), "Got size %d\n", size);
      ok(!lstrcmpW((WCHAR *)buffer, valueW), "Expect buffer %s, got %s\n", wine_dbgstr_w(valueW), wine_dbgstr_w((WCHAR *)buffer));
  
      /* #12 Get null property value */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, NULL, 0, 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, buffer, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_NOT_FOUND, "Expect last error %#x, got %#x\n", ERROR_NOT_FOUND, err);
      ok(size == 0, "Expect size %d, got %d\n", 0, size);
  
      /* #13 Insufficient buffer size */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, buffer, sizeof(valueW) - 1, &size, 0);
      err = GetLastError();
      ok(!ret, "Expect failure\n");
      ok(err == ERROR_INSUFFICIENT_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INSUFFICIENT_BUFFER, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == sizeof(valueW), "Got size %d\n", size);
  
      /* #14 Normal */
      ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
      ok(ret, "Expect success\n");
      SetLastError(0xdeadbeef);
      type = DEVPROP_TYPE_STRING;
      size = 0;
      memset(buffer, 0, sizeof(buffer));
      ret = pSetupDiGetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, &type, buffer, sizeof(buffer), &size, 0);
      err = GetLastError();
      ok(ret, "Expect success\n");
      ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
      ok(type == DEVPROP_TYPE_STRING, "Expect type %#x, got %#x\n", DEVPROP_TYPE_STRING, type);
      ok(size == sizeof(valueW), "Got size %d\n", size);
      ok(!lstrcmpW((WCHAR *)buffer, valueW), "Expect buffer %s, got %s\n", wine_dbgstr_w(valueW), wine_dbgstr_w((WCHAR *)buffer));
  
      ret = SetupDiRemoveDevice(set, &device_data);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
  
      SetupDiDestroyDeviceInfoList(set);
      FreeLibrary(hmod);
  }
  
  static void test_get_device_instance_id(void)
  {
      BOOL ret;
      HDEVINFO set;
      SP_DEVINFO_DATA device = {0};
      char id[200];
      DWORD size;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstanceIdA(NULL, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstanceIdA(NULL, &device, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstanceIdA(set, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstanceIdA(set, &device, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstanceIdA(set, &device, NULL, 0, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      device.cbSize = sizeof(device);
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstanceIdA(set, &device, NULL, 0, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstanceIdA(set, &device, NULL, 0, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiGetDeviceInstanceIdA(set, &device, id, sizeof(id), NULL);
      ok(ret, "Failed to get device id, error %#x.\n", GetLastError());
      ok(!strcmp(id, "ROOT\\LEGACY_BOGUS\\0000"), "Got unexpected id %s.\n", id);
  
      ret = SetupDiCreateDeviceInfoA(set, "LEGACY_BOGUS", &guid, NULL, NULL, DICD_GENERATE_ID, &device);
      ok(ret, "SetupDiCreateDeviceInfoA failed: %08x\n", GetLastError());
  
      ret = SetupDiGetDeviceInstanceIdA(set, &device, id, sizeof(id), NULL);
      ok(ret, "Failed to get device id, error %#x.\n", GetLastError());
      ok(!strcmp(id, "ROOT\\LEGACY_BOGUS\\0001"), "Got unexpected id %s.\n", id);
  
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_open_device_info(void)
  {
      SP_DEVINFO_DATA device = {sizeof(device)};
      CHAR id[MAX_DEVICE_ID_LEN + 2];
      HDEVINFO set;
      DWORD i = 0;
      BOOL ret;
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info, error %#x.\n", GetLastError());
  
      /* Open non-existent device */
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\FFFF", NULL, 0, &device);
      ok(GetLastError() == ERROR_NO_SUCH_DEVINST, "Got unexpected error %#x.\n", GetLastError());
      ok(!ret, "Expected failure.\n");
      check_device_info(set, 0, NULL, NULL);
  
      /* Open unregistered device */
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1000", NULL, 0, &device);
      ok(GetLastError() == ERROR_NO_SUCH_DEVINST, "Got unexpected error %#x.\n", GetLastError());
      ok(!ret, "Expected failure.\n");
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\1000");
      check_device_info(set, 1, NULL, NULL);
  
      /* Open registered device */
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1001", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\1000");
      check_device_info(set, 1, &guid, "Root\\LEGACY_BOGUS\\1001");
      check_device_info(set, 2, NULL, NULL);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1001", NULL, 0, &device);
      ok(GetLastError() == NO_ERROR, "Got unexpected error %#x.\n", GetLastError());
      ok(ret, "Failed to open device info\n");
      ret = SetupDiGetDeviceInstanceIdA(set, &device, id, sizeof(id), NULL);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
      ok(!strcasecmp(id, "Root\\LEGACY_BOGUS\\1001"), "Got unexpected id %s.\n", id);
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\1000");
      check_device_info(set, 1, &guid, "Root\\LEGACY_BOGUS\\1001");
      check_device_info(set, 2, NULL, NULL);
  
      /* Open registered device in an empty device info set */
      SetupDiDestroyDeviceInfoList(set);
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1001", NULL, 0, &device);
      ok(GetLastError() == NO_ERROR, "Got unexpected error %#x.\n", GetLastError());
      ok(ret, "Failed to open device info\n");
      ret = SetupDiGetDeviceInstanceIdA(set, &device, id, sizeof(id), NULL);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
      ok(!strcasecmp(id, "Root\\LEGACY_BOGUS\\1001"), "Got unexpected id %s.\n", id);
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\1001");
      check_device_info(set, 1, NULL, NULL);
  
      /* Open registered device again */
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1001", NULL, 0, &device);
      ok(GetLastError() == NO_ERROR, "Got unexpected error %#x.\n", GetLastError());
      ok(ret, "Failed to open device info\n");
      ret = SetupDiGetDeviceInstanceIdA(set, &device, id, sizeof(id), NULL);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
      ok(!strcasecmp(id, "Root\\LEGACY_BOGUS\\1001"), "Got unexpected id %s.\n", id);
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\1001");
      check_device_info(set, 1, NULL, NULL);
  
      /* Open registered device in a new device info set with wrong GUID */
      SetupDiDestroyDeviceInfoList(set);
      set = SetupDiCreateDeviceInfoList(&guid2, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1001", NULL, 0, &device);
      ok(GetLastError() == ERROR_CLASS_MISMATCH, "Got unexpected error %#x.\n", GetLastError());
      ok(!ret, "Expected failure.\n");
      check_device_info(set, 0, NULL, NULL);
  
      /* Open registered device in a new device info set with NULL GUID */
      SetupDiDestroyDeviceInfoList(set);
      set = SetupDiCreateDeviceInfoList(NULL, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1001", NULL, 0, &device);
      ok(GetLastError() == NO_ERROR, "Got unexpected error %#x.\n", GetLastError());
      ok(ret, "Failed to open device info\n");
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\1001");
      check_device_info(set, 1, NULL, NULL);
  
      SetupDiDestroyDeviceInfoList(set);
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info list, error %#x.\n", GetLastError());
  
      /* NULL set */
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(NULL, "Root\\LEGACY_BOGUS\\1001", NULL, 0, &device);
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
      ok(!ret, "Expected failure.\n");
  
      /* NULL instance id */
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, NULL, NULL, 0, &device);
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
      ok(!ret, "Expected failure.\n");
      check_device_info(set, 0, NULL, NULL);
  
      /* Invalid SP_DEVINFO_DATA cbSize, device will be added despite failure */
      SetLastError(0xdeadbeef);
      device.cbSize = 0;
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1001", NULL, 0, &device);
      device.cbSize = sizeof(device);
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(!ret, "Expected failure.\n");
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\1001");
      check_device_info(set, 1, NULL, NULL);
  
      SetupDiDestroyDeviceInfoList(set);
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device info list, error %#x.\n", GetLastError());
  
      /* NULL device */
      SetLastError(0xdeadbeef);
      ret = SetupDiOpenDeviceInfoA(set, "Root\\LEGACY_BOGUS\\1001", NULL, 0, NULL);
      ok(GetLastError() == NO_ERROR, "Got unexpected error %#x.\n", GetLastError());
      ok(ret, "Failed to open device info\n");
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\1001");
      check_device_info(set, 1, NULL, NULL);
  
      /* Clean up */
      SetupDiDestroyDeviceInfoList(set);
      set = SetupDiGetClassDevsA(&guid, NULL, NULL, 0);
      while (SetupDiEnumDeviceInfo(set, i++, &device))
      {
          ret = SetupDiRemoveDevice(set, &device);
          ok(ret, "Failed to remove device, error %#x.\n", GetLastError());
      }
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_register_device_info(void)
  {
      SP_DEVINFO_DATA device = {0};
      BOOL ret;
      HDEVINFO set;
      HKEY hkey;
      LSTATUS ls;
      DWORD type = 0;
      DWORD phantom = 0;
      DWORD size;
      int i = 0;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiRegisterDeviceInfo(NULL, NULL, 0, NULL, NULL, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiRegisterDeviceInfo(set, NULL, 0, NULL, NULL, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      device.cbSize = sizeof(device);
      SetLastError(0xdeadbeef);
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      RegOpenKeyA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\ROOT\\LEGACY_BOGUS\\0000", &hkey);
      size = sizeof(phantom);
      ls = RegQueryValueExA(hkey, "Phantom", NULL, &type, (BYTE *)&phantom, &size);
      ok(ls == ERROR_SUCCESS, "Got wrong error code %#x\n", ls);
      ok(phantom == 1, "Got wrong phantom value %d\n", phantom);
      ok(type == REG_DWORD, "Got wrong phantom type %#x\n", type);
      ok(size == sizeof(phantom), "Got wrong phantom size %d\n", size);
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
      size = sizeof(phantom);
      ls = RegQueryValueExA(hkey, "Phantom", NULL, NULL, (BYTE *)&phantom, &size);
      ok(ls == ERROR_FILE_NOT_FOUND, "Got wrong error code %#x\n", ls);
      RegCloseKey(hkey);
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0001", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
      ret = SetupDiRemoveDevice(set, &device);
      ok(ret, "Failed to remove device, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0002", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
      ret = SetupDiDeleteDeviceInfo(set, &device);
      ok(ret, "Failed to remove device, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0003", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      SetupDiDestroyDeviceInfoList(set);
  
      set = SetupDiGetClassDevsA(&guid, NULL, NULL, 0);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      check_device_info(set, 0, &guid, "Root\\LEGACY_BOGUS\\0000");
      check_device_info(set, 1, &guid, "Root\\LEGACY_BOGUS\\0002");
      check_device_info(set, 2, &guid, NULL);
  
      while (SetupDiEnumDeviceInfo(set, i++, &device))
      {
          ret = SetupDiRemoveDevice(set, &device);
          ok(ret, "Failed to remove device, error %#x.\n", GetLastError());
      }
  
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void check_all_lower_case(int line, const char* str)
  {
      const char *cur;
  
      for (cur = str; *cur; cur++)
      {
          BOOL is_lower = (tolower(*cur) == *cur);
          ok_(__FILE__, line)(is_lower, "Expected device path to be all lowercase but got %s.\n", str);
          if (!is_lower) break;
      }
  }
  
  static void check_device_iface_(int line, HDEVINFO set, SP_DEVINFO_DATA *device,
          const GUID *class, int index, DWORD flags, const char *path)
  {
      char buffer[200];
      SP_DEVICE_INTERFACE_DETAIL_DATA_A *detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_A *)buffer;
      SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
      BOOL ret;
  
      detail->cbSize = sizeof(*detail);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiEnumDeviceInterfaces(set, device, class, index, &iface);
      if (path)
      {
          ok_(__FILE__, line)(ret, "Failed to enumerate interfaces, error %#x.\n", GetLastError());
          ok_(__FILE__, line)(IsEqualGUID(&iface.InterfaceClassGuid, class),
                  "Got unexpected class %s.\n", wine_dbgstr_guid(&iface.InterfaceClassGuid));
          ok_(__FILE__, line)(iface.Flags == flags, "Got unexpected flags %#x.\n", iface.Flags);
          ret = SetupDiGetDeviceInterfaceDetailA(set, &iface, detail, sizeof(buffer), NULL, NULL);
          ok_(__FILE__, line)(ret, "Failed to get interface detail, error %#x.\n", GetLastError());
          ok_(__FILE__, line)(!strcasecmp(detail->DevicePath, path), "Got unexpected path %s.\n", detail->DevicePath);
          check_all_lower_case(line, detail->DevicePath);
      }
      else
      {
          ok_(__FILE__, line)(!ret, "Expected failure.\n");
          ok_(__FILE__, line)(GetLastError() == ERROR_NO_MORE_ITEMS,
                  "Got unexpected error %#x.\n", GetLastError());
      }
  }
  #define check_device_iface(a,b,c,d,e,f) check_device_iface_(__LINE__,a,b,c,d,e,f)
  
  static void test_device_iface(void)
  {
      char buffer[200];
      SP_DEVICE_INTERFACE_DETAIL_DATA_A *detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_A *)buffer;
      SP_DEVINFO_DATA device = {0}, device2 = {sizeof(device2)};
      SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
      BOOL ret;
      HDEVINFO set;
  
      detail->cbSize = sizeof(*detail);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInterfaceA(NULL, NULL, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInterfaceA(NULL, NULL, &guid, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInterfaceA(set, NULL, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInterfaceA(set, &device, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      device.cbSize = sizeof(device);
      ret = SetupDiCreateDeviceInfoA(set, "ROOT\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      check_device_iface(set, &device, &guid, 0, 0, NULL);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInterfaceA(set, &device, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, NULL, 0, NULL);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
  
      check_device_iface(set, &device, &guid, 0, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}");
      check_device_iface(set, &device, &guid, 1, 0, NULL);
  
      /* Creating the same interface a second time succeeds */
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, NULL, 0, NULL);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
  
      check_device_iface(set, &device, &guid, 0, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}");
      check_device_iface(set, &device, &guid, 1, 0, NULL);
  
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, "Oogah", 0, NULL);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
  
      check_device_iface(set, &device, &guid, 0, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}");
      check_device_iface(set, &device, &guid, 1, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}\\Oogah");
      check_device_iface(set, &device, &guid, 2, 0, NULL);
  
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, "test", 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
      ok(IsEqualGUID(&iface.InterfaceClassGuid, &guid), "Got unexpected class %s.\n",
              wine_dbgstr_guid(&iface.InterfaceClassGuid));
      ok(iface.Flags == 0, "Got unexpected flags %#x.\n", iface.Flags);
      ret = SetupDiGetDeviceInterfaceDetailA(set, &iface, detail, sizeof(buffer), NULL, NULL);
      ok(ret, "Failed to get interface detail, error %#x.\n", GetLastError());
      ok(!strcasecmp(detail->DevicePath, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}\\test"),
              "Got unexpected path %s.\n", detail->DevicePath);
  
      check_device_iface(set, &device, &guid, 0, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}");
      check_device_iface(set, &device, &guid, 1, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}\\Oogah");
      check_device_iface(set, &device, &guid, 2, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}\\test");
      check_device_iface(set, &device, &guid, 3, 0, NULL);
  
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid2, NULL, 0, NULL);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
  
      check_device_iface(set, &device, &guid2, 0, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A5-3F65-11DB-B704-0011955C2BDB}");
      check_device_iface(set, &device, &guid2, 1, 0, NULL);
  
      ret = SetupDiEnumDeviceInterfaces(set, &device, &guid2, 0, &iface);
      ok(ret, "Failed to enumerate interfaces, error %#x.\n", GetLastError());
      ret = SetupDiRemoveDeviceInterface(set, &iface);
      ok(ret, "Failed to remove interface, error %#x.\n", GetLastError());
  
      check_device_iface(set, &device, &guid2, 0, SPINT_REMOVED, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A5-3F65-11DB-B704-0011955C2BDB}");
      check_device_iface(set, &device, &guid2, 1, 0, NULL);
  
      ret = SetupDiEnumDeviceInterfaces(set, &device, &guid, 0, &iface);
      ok(ret, "Failed to enumerate interfaces, error %#x.\n", GetLastError());
      ret = SetupDiDeleteDeviceInterfaceData(set, &iface);
      ok(ret, "Failed to delete interface, error %#x.\n", GetLastError());
  
      check_device_iface(set, &device, &guid, 0, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}\\Oogah");
      check_device_iface(set, &device, &guid, 1, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}\\test");
      check_device_iface(set, &device, &guid, 2, 0, NULL);
  
      ret = SetupDiCreateDeviceInfoA(set, "ROOT\\LEGACY_BOGUS\\0001", &guid, NULL, NULL, 0, &device2);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInterfaceA(set, &device2, &guid, NULL, 0, NULL);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
  
      check_device_iface(set, NULL, &guid, 0, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}\\Oogah");
      check_device_iface(set, NULL, &guid, 1, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0000#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}\\test");
      check_device_iface(set, NULL, &guid, 2, 0, "\\\\?\\ROOT#LEGACY_BOGUS#0001#{6A55B5A4-3F65-11DB-B704-0011955C2BDB}");
      check_device_iface(set, NULL, &guid, 3, 0, NULL);
  
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  }
  
  static void test_device_iface_detail(void)
  {
      static const char path[] = "\\\\?\\root#legacy_bogus#0000#{6a55b5a4-3f65-11db-b704-0011955c2bdb}";
      SP_DEVICE_INTERFACE_DETAIL_DATA_A *detail;
      SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
      SP_DEVINFO_DATA device = {sizeof(device)};
      DWORD size = 0, expectedsize;
      HDEVINFO set;
      BOOL ret;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInterfaceDetailA(NULL, NULL, NULL, 0, NULL, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInterfaceDetailA(set, NULL, NULL, 0, NULL, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "ROOT\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, NULL, 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInterfaceDetailA(set, &iface, NULL, 0, NULL, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInterfaceDetailA(set, &iface, NULL, 100, NULL, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInterfaceDetailA(set, &iface, NULL, 0, &size, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      detail = heap_alloc(size);
      expectedsize = FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath[strlen(path) + 1]);
  
      detail->cbSize = 0;
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInterfaceDetailA(set, &iface, detail, size, &size, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      detail->cbSize = size;
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInterfaceDetailA(set, &iface, detail, size, &size, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInterfaceDetailA(set, &iface, detail, size, &size, NULL);
      ok(ret, "Failed to get interface detail, error %#x.\n", GetLastError());
      ok(!strcasecmp(path, detail->DevicePath), "Got unexpected path %s.\n", detail->DevicePath);
  
      ret = SetupDiGetDeviceInterfaceDetailW(set, &iface, NULL, 0, &size, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(size == expectedsize, "Got unexpected size %d.\n", size);
  
      memset(&device, 0, sizeof(device));
      device.cbSize = sizeof(device);
      ret = SetupDiGetDeviceInterfaceDetailW(set, &iface, NULL, 0, &size, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(IsEqualGUID(&device.ClassGuid, &guid), "Got unexpected class %s.\n", wine_dbgstr_guid(&device.ClassGuid));
  
      heap_free(detail);
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_device_key(void)
  {
      static const char params_key_path[] = "System\\CurrentControlSet\\Enum\\Root"
              "\\LEGACY_BOGUS\\0000\\Device Parameters";
      static const char class_key_path[] = "System\\CurrentControlSet\\Control\\Class"
              "\\{6a55b5a4-3f65-11db-b704-0011955c2bdb}";
      static const WCHAR bogus[] = {'S','y','s','t','e','m','\\',
       'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
       'E','n','u','m','\\','R','o','o','t','\\',
       'L','E','G','A','C','Y','_','B','O','G','U','S',0};
      SP_DEVINFO_DATA device = {sizeof(device)};
      char driver_path[50], data[4];
      HKEY class_key, key;
      DWORD size;
      BOOL ret;
      HDEVINFO set;
      LONG res;
  
      SetLastError(0xdeadbeef);
      key = SetupDiCreateDevRegKeyW(NULL, NULL, 0, 0, 0, NULL, NULL);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      res = RegOpenKeyW(HKEY_LOCAL_MACHINE, bogus, &key);
      ok(res != ERROR_SUCCESS, "Key should not exist.\n");
      RegCloseKey(key);
  
      ret = SetupDiCreateDeviceInfoA(set, "ROOT\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ok(!RegOpenKeyW(HKEY_LOCAL_MACHINE, bogus, &key), "Key should exist.\n");
      RegCloseKey(key);
  
      SetLastError(0xdeadbeef);
      key = SetupDiOpenDevRegKey(NULL, NULL, 0, 0, 0, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      key = SetupDiOpenDevRegKey(set, NULL, 0, 0, 0, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      key = SetupDiOpenDevRegKey(set, &device, 0, 0, 0, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_FLAGS, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, 0, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_FLAGS, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_BOTH, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_FLAGS, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_DEVINFO_NOT_REGISTERED, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_KEY_DOES_NOT_EXIST, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_DRIVER, NULL,
              (BYTE *)driver_path, sizeof(driver_path), NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      res = RegOpenKeyA(HKEY_LOCAL_MACHINE, class_key_path, &key);
      ok(res == ERROR_FILE_NOT_FOUND, "Key should not exist.\n");
      RegCloseKey(key);
  
      /* Vista+ will fail the following call to SetupDiCreateDevKeyW() if the
       * class key doesn't exist. */
      res = RegCreateKeyA(HKEY_LOCAL_MACHINE, class_key_path, &key);
      ok(!res, "Failed to create class key, error %u.\n", res);
      RegCloseKey(key);
  
      key = SetupDiCreateDevRegKeyW(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
      ok(key != INVALID_HANDLE_VALUE, "Failed to create device key, error %#x.\n", GetLastError());
      RegCloseKey(key);
  
      ok(!RegOpenKeyA(HKEY_LOCAL_MACHINE, class_key_path, &key), "Key should exist.\n");
      RegCloseKey(key);
  
      res = RegOpenKeyA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Class", &class_key);
      ok(!res, "Failed to open class key, error %u.\n", res);
  
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_DRIVER, NULL,
              (BYTE *)driver_path, sizeof(driver_path), NULL);
      ok(ret, "Failed to get driver property, error %#x.\n", GetLastError());
      res = RegOpenKeyA(class_key, driver_path, &key);
      ok(!res, "Failed to open driver key, error %u.\n", res);
      RegSetValueExA(key, "foo", 0, REG_SZ, (BYTE *)"bar", sizeof("bar"));
      RegCloseKey(key);
  
      SetLastError(0xdeadbeef);
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, 0);
  todo_wine {
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA || GetLastError() == ERROR_ACCESS_DENIED, /* win2k3 */
              "Got unexpected error %#x.\n", GetLastError());
  }
  
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ);
      ok(key != INVALID_HANDLE_VALUE, "Failed to open device key, error %#x.\n", GetLastError());
      size = sizeof(data);
      res = RegQueryValueExA(key, "foo", NULL, NULL, (BYTE *)data, &size);
      ok(!res, "Failed to get value, error %u.\n", res);
      ok(!strcmp(data, "bar"), "Got wrong data %s.\n", data);
      RegCloseKey(key);
  
      ret = SetupDiDeleteDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DRV);
      ok(ret, "Failed to delete device key, error %#x.\n", GetLastError());
  
      res = RegOpenKeyA(class_key, driver_path, &key);
      ok(res == ERROR_FILE_NOT_FOUND, "Key should not exist.\n");
  
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_KEY_DOES_NOT_EXIST, "Got unexpected error %#x.\n", GetLastError());
  
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_KEY_DOES_NOT_EXIST, "Got unexpected error %#x.\n", GetLastError());
  
      res = RegOpenKeyA(HKEY_LOCAL_MACHINE, params_key_path, &key);
      ok(res == ERROR_FILE_NOT_FOUND, "Key should not exist.\n");
  
      key = SetupDiCreateDevRegKeyA(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL);
      ok(key != INVALID_HANDLE_VALUE, "Got unexpected error %#x.\n", GetLastError());
      RegCloseKey(key);
  
      res = RegOpenKeyA(HKEY_LOCAL_MACHINE, params_key_path, &key);
      ok(!res, "Failed to open device key, error %u.\n", res);
      res = RegSetValueExA(key, "foo", 0, REG_SZ, (BYTE *)"bar", sizeof("bar"));
      ok(!res, "Failed to set value, error %u.\n", res);
      RegCloseKey(key);
  
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
      ok(key != INVALID_HANDLE_VALUE, "Got unexpected error %#x.\n", GetLastError());
      size = sizeof(data);
      res = RegQueryValueExA(key, "foo", NULL, NULL, (BYTE *)data, &size);
      ok(!res, "Failed to get value, error %u.\n", res);
      ok(!strcmp(data, "bar"), "Got wrong data %s.\n", data);
      RegCloseKey(key);
  
      ret = SetupDiDeleteDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DEV);
      ok(ret, "Got unexpected error %#x.\n", GetLastError());
  
      key = SetupDiOpenDevRegKey(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_KEY_DOES_NOT_EXIST, "Got unexpected error %#x.\n", GetLastError());
  
      res = RegOpenKeyA(HKEY_LOCAL_MACHINE, params_key_path, &key);
      ok(res == ERROR_FILE_NOT_FOUND, "Key should not exist.\n");
  
      key = SetupDiCreateDevRegKeyW(set, &device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
      ok(key != INVALID_HANDLE_VALUE, "Failed to create device key, error %#x.\n", GetLastError());
      RegCloseKey(key);
  
      ret = SetupDiRemoveDevice(set, &device);
      ok(ret, "Failed to remove device, error %#x.\n", GetLastError());
      SetupDiDestroyDeviceInfoList(set);
  
      res = RegOpenKeyA(class_key, driver_path, &key);
      ok(res == ERROR_FILE_NOT_FOUND, "Key should not exist.\n");
  
      /* Vista+ deletes the key automatically. */
      res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, class_key_path);
      ok(!res || res == ERROR_FILE_NOT_FOUND, "Failed to delete class key, error %u.\n", res);
  
      RegCloseKey(class_key);
  }
  
  static void test_register_device_iface(void)
  {
      static const WCHAR bogus[] = {'S','y','s','t','e','m','\\',
       'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
       'E','n','u','m','\\','R','o','o','t','\\',
       'L','E','G','A','C','Y','_','B','O','G','U','S',0};
      SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
      SP_DEVINFO_DATA device2 = {sizeof(device2)};
      SP_DEVINFO_DATA device = {sizeof(device)};
      HDEVINFO set, set2;
      BOOL ret;
      HKEY key;
      LONG res;
  
      set = SetupDiGetClassDevsA(&guid, NULL, 0, DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      res = RegOpenKeyW(HKEY_LOCAL_MACHINE, bogus, &key);
      ok(res == ERROR_FILE_NOT_FOUND, "Key should not exist.\n");
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, NULL, 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, "removed", 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, "deleted", 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
  
      ret = SetupDiEnumDeviceInterfaces(set, &device, &guid, 1, &iface);
      ok(ret, "Failed to enumerate interfaces, error %#x.\n", GetLastError());
      ret = SetupDiRemoveDeviceInterface(set, &iface);
      ok(ret, "Failed to delete interface, error %#x.\n", GetLastError());
      ret = SetupDiEnumDeviceInterfaces(set, &device, &guid, 2, &iface);
      ok(ret, "Failed to enumerate interfaces, error %#x.\n", GetLastError());
      ret = SetupDiDeleteDeviceInterfaceData(set, &iface);
      ok(ret, "Failed to delete interface, error %#x.\n", GetLastError());
  
      set2 = SetupDiGetClassDevsA(&guid, NULL, 0, DIGCF_DEVICEINTERFACE);
      ok(set2 != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      check_device_iface(set2, NULL, &guid, 0, 0, "\\\\?\\root#legacy_bogus#0000#{6a55b5a4-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set2, NULL, &guid, 1, 0, "\\\\?\\root#legacy_bogus#0000#{6a55b5a4-3f65-11db-b704-0011955c2bdb}\\deleted");
      check_device_iface(set2, NULL, &guid, 2, 0, NULL);
  
      ret = SetupDiEnumDeviceInfo(set2, 0, &device2);
      ok(ret, "Failed to enumerate devices, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInterfaceA(set2, &device2, &guid, "second", 0, NULL);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
  
      ret = SetupDiRemoveDevice(set, &device);
      ok(ret, "Failed to remove device, error %#x.\n", GetLastError());
  
      check_device_iface(set, NULL, &guid, 0, SPINT_REMOVED, "\\\\?\\root#legacy_bogus#0000#{6a55b5a4-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &guid, 1, SPINT_REMOVED, "\\\\?\\root#legacy_bogus#0000#{6a55b5a4-3f65-11db-b704-0011955c2bdb}\\removed");
      check_device_iface(set, NULL, &guid, 2, 0, NULL);
  
      check_device_iface(set2, NULL, &guid, 0, 0, "\\\\?\\root#legacy_bogus#0000#{6a55b5a4-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set2, NULL, &guid, 1, 0, "\\\\?\\root#legacy_bogus#0000#{6a55b5a4-3f65-11db-b704-0011955c2bdb}\\deleted");
      check_device_iface(set2, NULL, &guid, 2, 0, "\\\\?\\root#legacy_bogus#0000#{6a55b5a4-3f65-11db-b704-0011955c2bdb}\\second");
      check_device_iface(set2, NULL, &guid, 3, 0, NULL);
  
      SetupDiDestroyDeviceInfoList(set);
      SetupDiDestroyDeviceInfoList(set2);
  
      /* make sure all interface keys are deleted when a device is removed */
  
      set = SetupDiGetClassDevsA(&guid, NULL, 0, DIGCF_DEVICEINTERFACE);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      set2 = SetupDiGetClassDevsA(&guid, NULL, 0, DIGCF_DEVICEINTERFACE);
      ok(set2 != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_iface(set2, NULL, &guid, 0, 0, NULL);
      SetupDiDestroyDeviceInfoList(set2);
  
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_registry_property_a(void)
  {
      static const CHAR bogus[] = "System\\CurrentControlSet\\Enum\\Root\\LEGACY_BOGUS";
      SP_DEVINFO_DATA device = {sizeof(device)};
      CHAR buf[64] = "";
      DWORD size, type;
      HDEVINFO set;
      BOOL ret;
      LONG res;
      HKEY key;
  
      set = SetupDiGetClassDevsA(&guid, NULL, 0, DIGCF_DEVICEINTERFACE);
      ok(set != INVALID_HANDLE_VALUE, "Failed to get device list, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "LEGACY_BOGUS", &guid, NULL, NULL, DICD_GENERATE_ID, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyA(NULL, NULL, -1, NULL, 0);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyA(set, NULL, -1, NULL, 0);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyA(set, &device, -1, NULL, 0);
      ok(!ret, "Expected failure.\n");
  todo_wine
      ok(GetLastError() == ERROR_INVALID_REG_PROPERTY, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiSetDeviceRegistryPropertyA(set, &device, SPDRP_FRIENDLYNAME, NULL, 0);
      todo_wine
      ok(!ret, "Expected failure.\n");
      /* GetLastError() returns nonsense in win2k3 */
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyA(set, &device, SPDRP_FRIENDLYNAME, (BYTE *)"Bogus", sizeof("Bogus"));
      ok(ret, "Failed to set property, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(NULL, NULL, -1, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(set, NULL, -1, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, -1, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
  todo_wine
      ok(GetLastError() == ERROR_INVALID_REG_PROPERTY, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_FRIENDLYNAME, NULL, NULL, sizeof("Bogus"), NULL);
      ok(!ret, "Expected failure, got %d\n", ret);
      /* GetLastError() returns nonsense in win2k3 */
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_FRIENDLYNAME, NULL, NULL, 0, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(size == sizeof("Bogus"), "Got unexpected size %d.\n", size);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_FRIENDLYNAME, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!strcmp(buf, "Bogus"), "Got unexpected property %s.\n", buf);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_FRIENDLYNAME, &type, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!strcmp(buf, "Bogus"), "Got unexpected property %s.\n", buf);
      ok(type == REG_SZ, "Got unexpected type %d.\n", type);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyA(set, &device, SPDRP_FRIENDLYNAME, NULL, 0);
      ok(ret, "Failed to set property, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_FRIENDLYNAME, NULL, (BYTE *)buf, sizeof(buf), &size);
  todo_wine {
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  }
  
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_HARDWAREID, NULL, NULL, 0, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  
      SetupDiDestroyDeviceInfoList(set);
  
      res = RegOpenKeyA(HKEY_LOCAL_MACHINE, bogus, &key);
      ok(res == ERROR_FILE_NOT_FOUND, "Key should not exist.\n");
  
      /* Test existing registry properties right after device creation */
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      /* Create device from a not registered class without device name */
      ret = SetupDiCreateDeviceInfoA(set, "LEGACY_BOGUS", &guid, NULL, NULL, DICD_GENERATE_ID, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      /* No SPDRP_DEVICEDESC property */
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_DEVICEDESC, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  
      /* No SPDRP_CLASS property */
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_CLASS, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  
      /* Have SPDRP_CLASSGUID property */
      memset(buf, 0, sizeof(buf));
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_CLASSGUID, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!lstrcmpiA(buf, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}"), "Got unexpected value %s.\n", buf);
  
      ret = SetupDiDeleteDeviceInfo(set, &device);
      ok(ret, "Failed to delete device, error %#x.\n", GetLastError());
  
      /* Create device from a not registered class with a device name */
      ret = SetupDiCreateDeviceInfoA(set, "LEGACY_BOGUS", &guid, "device_name", NULL, DICD_GENERATE_ID, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      /* Have SPDRP_DEVICEDESC property */
      memset(buf, 0, sizeof(buf));
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_DEVICEDESC, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!lstrcmpA(buf, "device_name"), "Got unexpected value %s.\n", buf);
  
      SetupDiDestroyDeviceInfoList(set);
  
      /* Create device from a registered class */
      set = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "display", &GUID_DEVCLASS_DISPLAY, NULL, NULL, DICD_GENERATE_ID, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      /* Have SPDRP_CLASS property */
      memset(buf, 0, sizeof(buf));
      ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_CLASS, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!lstrcmpA(buf, "Display"), "Got unexpected value %s.\n", buf);
  
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_registry_property_w(void)
  {
      WCHAR friendly_name[] = {'B','o','g','u','s',0};
      SP_DEVINFO_DATA device = {sizeof(device)};
      WCHAR buf[64] = {0};
      DWORD size, type;
      HDEVINFO set;
      BOOL ret;
      LONG res;
      HKEY key;
      static const WCHAR bogus[] = {'S','y','s','t','e','m','\\',
       'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
       'E','n','u','m','\\','R','o','o','t','\\',
       'L','E','G','A','C','Y','_','B','O','G','U','S',0};
  
      set = SetupDiGetClassDevsA(&guid, NULL, 0, DIGCF_DEVICEINTERFACE);
      ok(set != INVALID_HANDLE_VALUE, "Failed to get device list, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "LEGACY_BOGUS", &guid, NULL, NULL, DICD_GENERATE_ID, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyW(NULL, NULL, -1, NULL, 0);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyW(set, NULL, -1, NULL, 0);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyW(set, &device, -1, NULL, 0);
      ok(!ret, "Expected failure.\n");
  todo_wine
      ok(GetLastError() == ERROR_INVALID_REG_PROPERTY, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiSetDeviceRegistryPropertyW(set, &device, SPDRP_FRIENDLYNAME, NULL, 0);
      todo_wine
      ok(!ret, "Expected failure.\n");
      /* GetLastError() returns nonsense in win2k3 */
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyW(set, &device, SPDRP_FRIENDLYNAME, (BYTE *)friendly_name, sizeof(friendly_name));
      ok(ret, "Failed to set property, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(NULL, NULL, -1, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(set, NULL, -1, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, -1, NULL, NULL, 0, NULL);
      ok(!ret, "Expected failure.\n");
  todo_wine
      ok(GetLastError() == ERROR_INVALID_REG_PROPERTY, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_FRIENDLYNAME, NULL, NULL, sizeof(buf), NULL);
      ok(!ret, "Expected failure.\n");
      /* GetLastError() returns nonsense in win2k3 */
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_FRIENDLYNAME, NULL, NULL, 0, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(size == sizeof(friendly_name), "Got unexpected size %d.\n", size);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_FRIENDLYNAME, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!lstrcmpW(buf, friendly_name), "Got unexpected property %s.\n", wine_dbgstr_w(buf));
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_FRIENDLYNAME, &type, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!lstrcmpW(buf, friendly_name), "Got unexpected property %s.\n", wine_dbgstr_w(buf));
      ok(type == REG_SZ, "Got unexpected type %d.\n", type);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceRegistryPropertyW(set, &device, SPDRP_FRIENDLYNAME, NULL, 0);
      ok(ret, "Failed to set property, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_FRIENDLYNAME, NULL, (BYTE *)buf, sizeof(buf), &size);
  todo_wine {
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  }
  
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_HARDWAREID, NULL, NULL, 0, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  
      SetupDiDestroyDeviceInfoList(set);
  
      res = RegOpenKeyW(HKEY_LOCAL_MACHINE, bogus, &key);
      ok(res == ERROR_FILE_NOT_FOUND, "Key should not exist.\n");
  
      /* Test existing registry properties right after device creation */
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      /* Create device from a not registered class without device name */
      ret = SetupDiCreateDeviceInfoW(set, L"LEGACY_BOGUS", &guid, NULL, NULL, DICD_GENERATE_ID, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      /* No SPDRP_DEVICEDESC property */
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_DEVICEDESC, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  
      /* No SPDRP_CLASS property */
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_CLASS, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  
      /* Have SPDRP_CLASSGUID property */
      memset(buf, 0, sizeof(buf));
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_CLASSGUID, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!lstrcmpiW(buf, L"{6a55b5a4-3f65-11db-b704-0011955c2bdb}"), "Got unexpected value %s.\n", wine_dbgstr_w(buf));
  
      ret = SetupDiDeleteDeviceInfo(set, &device);
      ok(ret, "Failed to delete device, error %#x.\n", GetLastError());
  
      /* Create device from a not registered class with a device name */
      ret = SetupDiCreateDeviceInfoW(set, L"LEGACY_BOGUS", &guid, L"device_name", NULL, DICD_GENERATE_ID, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      /* Have SPDRP_DEVICEDESC property */
      memset(buf, 0, sizeof(buf));
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_DEVICEDESC, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!lstrcmpW(buf, L"device_name"), "Got unexpected value %s.\n", wine_dbgstr_w(buf));
  
      SetupDiDestroyDeviceInfoList(set);
  
      /* Create device from a registered class */
      set = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoW(set, L"display", &GUID_DEVCLASS_DISPLAY, NULL, NULL, DICD_GENERATE_ID, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      /* Have SPDRP_CLASS property */
      memset(buf, 0, sizeof(buf));
      ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_CLASS, NULL, (BYTE *)buf, sizeof(buf), NULL);
      ok(ret, "Failed to get property, error %#x.\n", GetLastError());
      ok(!lstrcmpW(buf, L"Display"), "Got unexpected value %s.\n", wine_dbgstr_w(buf));
  
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_get_inf_class(void)
  {
      static const char inffile[] = "winetest.inf";
      static const char content[] = "[Version]\r\n\r\n";
      static const char* signatures[] = {"\"$CHICAGO$\"", "\"$Windows NT$\""};
  
      char cn[MAX_PATH];
      char filename[MAX_PATH];
      DWORD count;
      BOOL retval;
      GUID guid;
      HANDLE h;
      int i;
  
      GetTempPathA(MAX_PATH, filename);
      strcat(filename, inffile);
      DeleteFileA(filename);
  
      /* not existing file */
      SetLastError(0xdeadbeef);
      retval = SetupDiGetINFClassA(filename, &guid, cn, MAX_PATH, &count);
      ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
      ok(ERROR_FILE_NOT_FOUND == GetLastError(),
          "expected error ERROR_FILE_NOT_FOUND, got %u\n", GetLastError());
  
      /* missing file wins against other invalid parameter */
      SetLastError(0xdeadbeef);
      retval = SetupDiGetINFClassA(filename, NULL, cn, MAX_PATH, &count);
      ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
      ok(ERROR_FILE_NOT_FOUND == GetLastError(),
          "expected error ERROR_FILE_NOT_FOUND, got %u\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      retval = SetupDiGetINFClassA(filename, &guid, NULL, MAX_PATH, &count);
      ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
      ok(ERROR_FILE_NOT_FOUND == GetLastError(),
          "expected error ERROR_FILE_NOT_FOUND, got %u\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      retval = SetupDiGetINFClassA(filename, &guid, cn, 0, &count);
      ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
      ok(ERROR_FILE_NOT_FOUND == GetLastError(),
          "expected error ERROR_FILE_NOT_FOUND, got %u\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      retval = SetupDiGetINFClassA(filename, &guid, cn, MAX_PATH, NULL);
      ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
      ok(ERROR_FILE_NOT_FOUND == GetLastError(),
          "expected error ERROR_FILE_NOT_FOUND, got %u\n", GetLastError());
  
      /* test file content */
      h = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                      FILE_ATTRIBUTE_NORMAL, NULL);
      ok(h != INVALID_HANDLE_VALUE, "Failed to create file, error %#x.\n", GetLastError());
      CloseHandle(h);
  
      retval = SetupDiGetINFClassA(filename, &guid, cn, MAX_PATH, &count);
      ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
  
      for(i=0; i < ARRAY_SIZE(signatures); i++)
      {
          trace("testing signature %s\n", signatures[i]);
          h = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                          FILE_ATTRIBUTE_NORMAL, NULL);
          ok(h != INVALID_HANDLE_VALUE, "Failed to create file, error %#x.\n", GetLastError());
          WriteFile(h, content, sizeof(content), &count, NULL);
          CloseHandle(h);
  
          retval = SetupDiGetINFClassA(filename, &guid, cn, MAX_PATH, &count);
          ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
  
          WritePrivateProfileStringA("Version", "Signature", signatures[i], filename);
  
          retval = SetupDiGetINFClassA(filename, &guid, cn, MAX_PATH, &count);
          ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
  
          WritePrivateProfileStringA("Version", "Class", "WINE", filename);
  
          count = 0xdeadbeef;
          retval = SetupDiGetINFClassA(filename, &guid, cn, MAX_PATH, &count);
          ok(retval, "expected SetupDiGetINFClassA to succeed! error %u\n", GetLastError());
          ok(count == 5, "expected count==5, got %u\n", count);
  
          count = 0xdeadbeef;
          retval = SetupDiGetINFClassA(filename, &guid, cn, 5, &count);
          ok(retval, "expected SetupDiGetINFClassA to succeed! error %u\n", GetLastError());
          ok(count == 5, "expected count==5, got %u\n", count);
  
          count = 0xdeadbeef;
          SetLastError(0xdeadbeef);
          retval = SetupDiGetINFClassA(filename, &guid, cn, 4, &count);
          ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
          ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
              "expected error ERROR_INSUFFICIENT_BUFFER, got %u\n", GetLastError());
          ok(count == 5, "expected count==5, got %u\n", count);
  
          /* invalid parameter */
          SetLastError(0xdeadbeef);
          retval = SetupDiGetINFClassA(NULL, &guid, cn, MAX_PATH, &count);
          ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
          ok(ERROR_INVALID_PARAMETER == GetLastError(),
              "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
  
          SetLastError(0xdeadbeef);
          retval = SetupDiGetINFClassA(filename, NULL, cn, MAX_PATH, &count);
          ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
          ok(ERROR_INVALID_PARAMETER == GetLastError(),
              "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
  
          SetLastError(0xdeadbeef);
          retval = SetupDiGetINFClassA(filename, &guid, NULL, MAX_PATH, &count);
          ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
          ok(ERROR_INVALID_PARAMETER == GetLastError(),
              "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
  
          SetLastError(0xdeadbeef);
          retval = SetupDiGetINFClassA(filename, &guid, cn, 0, &count);
          ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
          ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER || GetLastError() == ERROR_INVALID_PARAMETER /* 2k3+ */,
                  "Got unexpected error %#x.\n", GetLastError());
  
          DeleteFileA(filename);
  
          WritePrivateProfileStringA("Version", "Signature", signatures[i], filename);
          WritePrivateProfileStringA("Version", "ClassGUID", "WINE", filename);
  
          SetLastError(0xdeadbeef);
          retval = SetupDiGetINFClassA(filename, &guid, cn, MAX_PATH, &count);
          ok(!retval, "expected SetupDiGetINFClassA to fail!\n");
          ok(GetLastError() == RPC_S_INVALID_STRING_UUID || GetLastError() == ERROR_INVALID_PARAMETER /* 7+ */,
                  "Got unexpected error %#x.\n", GetLastError());
  
          /* network adapter guid */
          WritePrivateProfileStringA("Version", "ClassGUID",
                                     "{4d36e972-e325-11ce-bfc1-08002be10318}", filename);
  
          /* this test succeeds only if the guid is known to the system */
          count = 0xdeadbeef;
          retval = SetupDiGetINFClassA(filename, &guid, cn, MAX_PATH, &count);
          ok(retval, "expected SetupDiGetINFClassA to succeed! error %u\n", GetLastError());
          todo_wine
          ok(count == 4, "expected count==4, got %u(%s)\n", count, cn);
  
          DeleteFileA(filename);
      }
  }
  
  static void test_devnode(void)
  {
      HDEVINFO set;
      SP_DEVINFO_DATA device = { sizeof(SP_DEVINFO_DATA) };
      char buffer[50];
      DWORD ret;
  
      set = SetupDiGetClassDevsA(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE);
      ok(set != INVALID_HANDLE_VALUE, "SetupDiGetClassDevs failed: %#x\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL,
          NULL, 0, &device);
      ok(ret, "SetupDiCreateDeviceInfo failed: %#x\n", GetLastError());
  
      ret = CM_Get_Device_IDA(device.DevInst, buffer, sizeof(buffer), 0);
      ok(!ret, "got %#x\n", ret);
      ok(!strcmp(buffer, "ROOT\\LEGACY_BOGUS\\0000"), "got %s\n", buffer);
  
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_device_interface_key(void)
  {
      const char keypath[] = "System\\CurrentControlSet\\Control\\DeviceClasses\\"
          "{6a55b5a4-3f65-11db-b704-0011955c2bdb}\\"
          "##?#ROOT#LEGACY_BOGUS#0001#{6a55b5a4-3f65-11db-b704-0011955c2bdb}";
      SP_DEVICE_INTERFACE_DATA iface = { sizeof(iface) };
      SP_DEVINFO_DATA devinfo = { sizeof(devinfo) };
      HKEY parent, key, dikey;
      char buffer[5];
      HDEVINFO set;
      LONG sz, ret;
  
      set = SetupDiGetClassDevsA(NULL, NULL, 0, DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "SetupDiGetClassDevs failed: %#x\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "ROOT\\LEGACY_BOGUS\\0001", &guid, NULL, NULL, 0, &devinfo);
      ok(ret, "SetupDiCreateDeviceInfo failed: %#x\n", GetLastError());
  
      ret = SetupDiCreateDeviceInterfaceA(set, &devinfo, &guid, NULL, 0, &iface);
      ok(ret, "SetupDiCreateDeviceInterface failed: %#x\n", GetLastError());
  
      ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &parent);
      ok(!ret, "failed to open device parent key: %u\n", ret);
  
      ret = RegOpenKeyA(parent, "#\\Device Parameters", &key);
      ok(ret == ERROR_FILE_NOT_FOUND, "key shouldn't exist\n");
  
      dikey = SetupDiCreateDeviceInterfaceRegKeyA(set, &iface, 0, KEY_ALL_ACCESS, NULL, NULL);
      ok(dikey != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError());
  
      ret = RegOpenKeyA(parent, "#\\Device Parameters", &key);
      ok(!ret, "key should exist: %u\n", ret);
  
      ret = RegSetValueA(key, NULL, REG_SZ, "test", 5);
      ok(!ret, "RegSetValue failed: %u\n", ret);
      sz = sizeof(buffer);
      ret = RegQueryValueA(dikey, NULL, buffer, &sz);
      ok(!ret, "RegQueryValue failed: %u\n", ret);
      ok(!strcmp(buffer, "test"), "got wrong data %s\n", buffer);
  
      RegCloseKey(dikey);
      RegCloseKey(key);
  
      ret = SetupDiDeleteDeviceInterfaceRegKey(set, &iface, 0);
      ok(ret, "got error %u\n", GetLastError());
  
      ret = RegOpenKeyA(parent, "#\\Device Parameters", &key);
      ok(ret == ERROR_FILE_NOT_FOUND, "key shouldn't exist\n");
  
      RegCloseKey(parent);
      SetupDiRemoveDeviceInterface(set, &iface);
      SetupDiRemoveDevice(set, &devinfo);
      SetupDiDestroyDeviceInfoList(set);
  }
  
  static void test_open_device_interface_key(void)
  {
      SP_DEVICE_INTERFACE_DATA iface;
      SP_DEVINFO_DATA device;
      CHAR buffer[5];
      HDEVINFO set;
      LSTATUS lr;
      LONG size;
      HKEY key;
      BOOL ret;
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x\n", GetLastError());
  
      device.cbSize = sizeof(device);
      ret = SetupDiCreateDeviceInfoA(set, "ROOT\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      iface.cbSize = sizeof(iface);
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &guid, NULL, 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
  
      /* Test open before creation */
      key = SetupDiOpenDeviceInterfaceRegKey(set, &iface, 0, KEY_ALL_ACCESS);
      ok(key == INVALID_HANDLE_VALUE, "Expect open interface registry key failure\n");
  
      /* Test opened key is from SetupDiCreateDeviceInterfaceRegKey */
      key = SetupDiCreateDeviceInterfaceRegKeyW(set, &iface, 0, KEY_ALL_ACCESS, NULL, NULL);
      ok(key != INVALID_HANDLE_VALUE, "Failed to create interface registry key, error %#x\n", GetLastError());
  
      lr = RegSetValueA(key, NULL, REG_SZ, "test", 5);
      ok(!lr, "RegSetValue failed, error %#x\n", lr);
  
      RegCloseKey(key);
  
      key = SetupDiOpenDeviceInterfaceRegKey(set, &iface, 0, KEY_ALL_ACCESS);
      ok(key != INVALID_HANDLE_VALUE, "Failed to open interface registry key, error %#x\n", GetLastError());
  
      size = sizeof(buffer);
      lr = RegQueryValueA(key, NULL, buffer, &size);
      ok(!lr, "RegQueryValue failed, error %#x\n", lr);
      ok(!strcmp(buffer, "test"), "got wrong data %s\n", buffer);
  
      RegCloseKey(key);
  
      /* Test open after removal */
      ret = SetupDiRemoveDeviceInterface(set, &iface);
      ok(ret, "Failed to remove device interface, error %#x.\n", GetLastError());
  
      key = SetupDiOpenDeviceInterfaceRegKey(set, &iface, 0, KEY_ALL_ACCESS);
      ok(key == INVALID_HANDLE_VALUE, "Expect open interface registry key failure\n");
  
      ret = SetupDiRemoveDevice(set, &device);
      ok(ret, "Failed to remove device, error %#x.\n", GetLastError());
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  }
  
  static void test_device_install_params(void)
  {
      SP_DEVINFO_DATA device = {sizeof(device)};
      SP_DEVINSTALL_PARAMS_A params;
      HDEVINFO set;
      BOOL ret;
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      params.cbSize = sizeof(params) - 1;
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstallParamsA(set, &device, &params);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      params.cbSize = sizeof(params) + 1;
      SetLastError(0xdeadbeef);
      ret = SetupDiGetDeviceInstallParamsA(set, &device, &params);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      params.cbSize = sizeof(params) - 1;
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceInstallParamsA(set, &device, &params);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      params.cbSize = sizeof(params) + 1;
      SetLastError(0xdeadbeef);
      ret = SetupDiSetDeviceInstallParamsA(set, &device, &params);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
  
      memset(&params, 0xcc, sizeof(params));
      params.cbSize = sizeof(params);
      ret = SetupDiGetDeviceInstallParamsA(set, &device, &params);
      ok(ret, "Failed to get device install params, error %#x.\n", GetLastError());
      ok(!params.Flags, "Got flags %#x.\n", params.Flags);
      ok(!params.FlagsEx, "Got extended flags %#x.\n", params.FlagsEx);
      ok(!params.hwndParent, "Got parent %p.\n", params.hwndParent);
      ok(!params.InstallMsgHandler, "Got callback %p.\n", params.InstallMsgHandler);
      ok(!params.InstallMsgHandlerContext, "Got callback context %p.\n", params.InstallMsgHandlerContext);
      ok(!params.FileQueue, "Got queue %p.\n", params.FileQueue);
      ok(!params.ClassInstallReserved, "Got class installer data %#lx.\n", params.ClassInstallReserved);
      ok(!params.DriverPath[0], "Got driver path %s.\n", params.DriverPath);
  
      params.Flags = DI_INF_IS_SORTED;
      params.FlagsEx = DI_FLAGSEX_ALLOWEXCLUDEDDRVS;
      strcpy(params.DriverPath, "C:\\windows");
      ret = SetupDiSetDeviceInstallParamsA(set, &device, &params);
      ok(ret, "Failed to set device install params, error %#x.\n", GetLastError());
  
      memset(&params, 0xcc, sizeof(params));
      params.cbSize = sizeof(params);
      ret = SetupDiGetDeviceInstallParamsA(set, &device, &params);
      ok(ret, "Failed to get device install params, error %#x.\n", GetLastError());
      ok(params.Flags == DI_INF_IS_SORTED, "Got flags %#x.\n", params.Flags);
      ok(params.FlagsEx == DI_FLAGSEX_ALLOWEXCLUDEDDRVS, "Got extended flags %#x.\n", params.FlagsEx);
      ok(!params.hwndParent, "Got parent %p.\n", params.hwndParent);
      ok(!params.InstallMsgHandler, "Got callback %p.\n", params.InstallMsgHandler);
      ok(!params.InstallMsgHandlerContext, "Got callback context %p.\n", params.InstallMsgHandlerContext);
      ok(!params.FileQueue, "Got queue %p.\n", params.FileQueue);
      ok(!params.ClassInstallReserved, "Got class installer data %#lx.\n", params.ClassInstallReserved);
      ok(!strcasecmp(params.DriverPath, "C:\\windows"), "Got driver path %s.\n", params.DriverPath);
  
      SetupDiDestroyDeviceInfoList(set);
  }
  
  #ifdef __i386__
  #define MYEXT "x86"
  #define WOWEXT "AMD64"
  #define WRONGEXT "ARM"
  #elif defined(__x86_64__)
  #define MYEXT "AMD64"
  #define WOWEXT "x86"
  #define WRONGEXT "ARM64"
  #elif defined(__arm__)
  #define MYEXT "ARM"
  #define WOWEXT "ARM64"
  #define WRONGEXT "x86"
  #elif defined(__aarch64__)
  #define MYEXT "ARM64"
  #define WOWEXT "ARM"
  #define WRONGEXT "AMD64"
  #else
  #define MYEXT
  #define WOWEXT
  #define WRONGEXT
  #endif
  
  static void test_get_actual_section(void)
  {
      static const char inf_data[] = "[Version]\n"
              "Signature=\"$Chicago$\"\n"
              "[section1]\n"
              "[section2]\n"
              "[section2.nt]\n"
              "[section3]\n"
              "[section3.nt" MYEXT "]\n"
              "[section4]\n"
              "[section4.nt]\n"
              "[section4.nt" MYEXT "]\n"
              "[section5.nt]\n"
              "[section6.nt" MYEXT "]\n"
              "[section7]\n"
              "[section7.nt" WRONGEXT "]\n"
              "[section8.nt" WRONGEXT "]\n"
              "[section9.nt" MYEXT "]\n"
              "[section9.nt" WOWEXT "]\n"
              "[section10.nt" WOWEXT "]\n";
  
      char inf_path[MAX_PATH], section[LINE_LEN], *extptr;
      DWORD size;
      HINF hinf;
      BOOL ret;
  
      GetTempPathA(sizeof(inf_path), inf_path);
      strcat(inf_path, "setupapi_test.inf");
      create_file(inf_path, inf_data);
  
      hinf = SetupOpenInfFileA(inf_path, NULL, INF_STYLE_WIN4, NULL);
      ok(hinf != INVALID_HANDLE_VALUE, "Failed to open INF file, error %#x.\n", GetLastError());
  
      ret = SetupDiGetActualSectionToInstallA(hinf, "section1", section, ARRAY_SIZE(section), NULL, NULL);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(!strcmp(section, "section1"), "Got unexpected section %s.\n", section);
  
      size = 0xdeadbeef;
      ret = SetupDiGetActualSectionToInstallA(hinf, "section1", NULL, 5, &size, NULL);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(size == 9, "Got size %u.\n", size);
  
      SetLastError(0xdeadbeef);
      size = 0xdeadbeef;
      ret = SetupDiGetActualSectionToInstallA(hinf, "section1", section, 5, &size, NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(size == 9, "Got size %u.\n", size);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiGetActualSectionToInstallA(hinf, "section1", section, ARRAY_SIZE(section), &size, NULL);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(!strcasecmp(section, "section1"), "Got unexpected section %s.\n", section);
      ok(size == 9, "Got size %u.\n", size);
  
      extptr = section;
      ret = SetupDiGetActualSectionToInstallA(hinf, "section2", section, ARRAY_SIZE(section), NULL, &extptr);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(!strcasecmp(section, "section2.NT"), "Got unexpected section %s.\n", section);
      ok(extptr == section + 8, "Got extension %s.\n", extptr);
  
      extptr = section;
      ret = SetupDiGetActualSectionToInstallA(hinf, "section3", section, ARRAY_SIZE(section), NULL, &extptr);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(!strcasecmp(section, "section3.NT" MYEXT), "Got unexpected section %s.\n", section);
      ok(extptr == section + 8, "Got extension %s.\n", extptr);
  
      extptr = section;
      ret = SetupDiGetActualSectionToInstallA(hinf, "section4", section, ARRAY_SIZE(section), NULL, &extptr);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(!strcasecmp(section, "section4.NT" MYEXT), "Got unexpected section %s.\n", section);
      ok(extptr == section + 8, "Got extension %s.\n", extptr);
  
      extptr = section;
      ret = SetupDiGetActualSectionToInstallA(hinf, "section5", section, ARRAY_SIZE(section), NULL, &extptr);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(!strcasecmp(section, "section5.NT"), "Got unexpected section %s.\n", section);
      ok(extptr == section + 8, "Got extension %s.\n", extptr);
  
      extptr = section;
      ret = SetupDiGetActualSectionToInstallA(hinf, "section6", section, ARRAY_SIZE(section), NULL, &extptr);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(!strcasecmp(section, "section6.NT" MYEXT), "Got unexpected section %s.\n", section);
      ok(extptr == section + 8, "Got extension %s.\n", extptr);
  
      extptr = section;
      ret = SetupDiGetActualSectionToInstallA(hinf, "section9", section, ARRAY_SIZE(section), NULL, &extptr);
      ok(ret, "Failed to get section, error %#x.\n", GetLastError());
      ok(!strcasecmp(section, "section9.NT" MYEXT), "Got unexpected section %s.\n", section);
      ok(extptr == section + 8, "Got extension %s.\n", extptr);
  
      if (0)
      {
          /* For some reason, these calls hang on Windows 10 1809+. */
          extptr = section;
          ret = SetupDiGetActualSectionToInstallA(hinf, "section1", section, ARRAY_SIZE(section), NULL, &extptr);
          ok(ret, "Failed to get section, error %#x.\n", GetLastError());
          ok(!strcasecmp(section, "section1"), "Got unexpected section %s.\n", section);
          ok(!extptr || !*extptr /* Windows 10 1809 */, "Got extension %s.\n", extptr);
  
          extptr = section;
          ret = SetupDiGetActualSectionToInstallA(hinf, "section7", section, ARRAY_SIZE(section), NULL, &extptr);
          ok(ret, "Failed to get section, error %#x.\n", GetLastError());
          ok(!strcasecmp(section, "section7"), "Got unexpected section %s.\n", section);
          ok(!extptr || !*extptr /* Windows 10 1809 */, "Got extension %s.\n", extptr);
  
          extptr = section;
          ret = SetupDiGetActualSectionToInstallA(hinf, "section8", section, ARRAY_SIZE(section), NULL, &extptr);
          ok(ret, "Failed to get section, error %#x.\n", GetLastError());
          ok(!strcasecmp(section, "section8"), "Got unexpected section %s.\n", section);
          ok(!extptr || !*extptr /* Windows 10 1809 */, "Got extension %s.\n", extptr);
  
          extptr = section;
          ret = SetupDiGetActualSectionToInstallA(hinf, "nonexistent", section, ARRAY_SIZE(section), NULL, &extptr);
          ok(ret, "Failed to get section, error %#x.\n", GetLastError());
          ok(!strcasecmp(section, "nonexistent"), "Got unexpected section %s.\n", section);
          ok(!extptr || !*extptr /* Windows 10 1809 */, "Got extension %s.\n", extptr);
  
          extptr = section;
          ret = SetupDiGetActualSectionToInstallA(hinf, "section10", section, ARRAY_SIZE(section), NULL, &extptr);
          ok(ret, "Failed to get section, error %#x.\n", GetLastError());
          ok(!strcasecmp(section, "section10"), "Got unexpected section %s.\n", section);
          ok(!extptr, "Got extension %s.\n", extptr);
      }
  
      SetupCloseInfFile(hinf);
      ret = DeleteFileA(inf_path);
      ok(ret, "Failed to delete %s, error %u.\n", inf_path, GetLastError());
  }
  
  static void test_driver_list(void)
  {
      char detail_buffer[1000];
      SP_DRVINFO_DETAIL_DATA_A *detail = (SP_DRVINFO_DETAIL_DATA_A *)detail_buffer;
      char short_path[MAX_PATH], inf_dir[MAX_PATH], inf_path[MAX_PATH + 10], inf_path2[MAX_PATH + 10];
      static const char hardware_id[] = "bogus_hardware_id\0other_hardware_id\0";
      static const char compat_id[] = "bogus_compat_id\0";
      SP_DEVINSTALL_PARAMS_A params = {sizeof(params)};
      SP_DRVINFO_DATA_A driver = {sizeof(driver)};
      SP_DEVINFO_DATA device = {sizeof(device)};
      DWORD size, expect_size;
      FILETIME filetime;
      HDEVINFO set;
      HANDLE file;
      DWORD idx;
      BOOL ret;
  
      static const char inf_data[] = "[Version]\n"
              "Signature=\"$Chicago$\"\n"
              "ClassGuid={6a55b5a4-3f65-11db-b704-0011955c2bdb}\n"
              "[Manufacturer]\n"
              "mfg1=mfg1_key,NT" MYEXT "\n"
              "mfg2=mfg2_key,NT" MYEXT "\n"
              "mfg1_wow=mfg1_key,NT" WOWEXT "\n"
              "mfg2_wow=mfg2_key,NT" WOWEXT "\n"
              "mfg3=mfg3_key,NT" WRONGEXT "\n"
              "[mfg1_key.nt" MYEXT "]\n"
              "desc0=,other_hardware_id,bogus_compat_id\n"
              "desc1=install1,bogus_hardware_id\n"
              "desc2=,bogus_hardware_id\n"
              "desc3=,wrong_hardware_id\n"
              "desc4=,wrong_hardware_id,bogus_compat_id\n"
              "[mfg1_key.nt" WOWEXT "]\n"
              "desc0=,other_hardware_id,bogus_compat_id\n"
              "desc1=install1,bogus_hardware_id\n"
              "desc2=,bogus_hardware_id\n"
              "desc3=,wrong_hardware_id\n"
              "desc4=,wrong_hardware_id,bogus_compat_id\n"
              "[mfg2_key.nt" MYEXT "]\n"
              "desc5=,bogus_hardware_id\n"
              "[mfg2_key.nt" WOWEXT "]\n"
              "desc5=,bogus_hardware_id\n"
              "[mfg3_key.nt" WRONGEXT "]\n"
              "desc6=,bogus_hardware_id\n"
              "[install1.nt" MYEXT "]\n"
              "[install1.nt" WRONGEXT "]\n";
  
      static const char inf_data_file1[] = "[Version]\n"
              "Signature=\"$Chicago$\"\n"
              "ClassGuid={6a55b5a4-3f65-11db-b704-0011955c2bdb}\n"
              "[Manufacturer]\n"
              "mfg1=mfg1_key,NT" MYEXT ",NT" WOWEXT "\n"
              "[mfg1_key.nt" MYEXT "]\n"
              "desc1=,bogus_hardware_id\n"
              "[mfg1_key.nt" WOWEXT "]\n"
              "desc1=,bogus_hardware_id\n";
  
      static const char inf_data_file2[] = "[Version]\n"
              "Signature=\"$Chicago$\"\n"
              "ClassGuid={6a55b5a5-3f65-11db-b704-0011955c2bdb}\n"
              "[Manufacturer]\n"
              "mfg1=mfg1_key,NT" MYEXT ",NT" WOWEXT "\n"
              "[mfg1_key.nt" MYEXT "]\n"
              "desc2=,bogus_hardware_id\n"
              "[mfg1_key.nt" WOWEXT "]\n"
              "desc2=,bogus_hardware_id\n";
  
      GetTempPathA(sizeof(inf_path), inf_path);
      GetShortPathNameA(inf_path, short_path, sizeof(short_path));
      strcat(inf_path, "setupapi_test.inf");
      strcat(short_path, "setupapi_test.inf");
      create_file(inf_path, inf_data);
      file = CreateFileA(inf_path, 0, 0, NULL, OPEN_EXISTING, 0, 0);
      GetFileTime(file, NULL, NULL, &filetime);
      CloseHandle(file);
  
      set = SetupDiCreateDeviceInfoList(NULL, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\BOGUS\\0000", &GUID_NULL, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiSetDeviceRegistryPropertyA(set, &device, SPDRP_HARDWAREID,
              (const BYTE *)hardware_id, sizeof(hardware_id));
      ok(ret, "Failed to set hardware ID, error %#x.\n", GetLastError());
  
      ret = SetupDiSetDeviceRegistryPropertyA(set, &device, SPDRP_COMPATIBLEIDS,
              (const BYTE *)compat_id, sizeof(compat_id));
      ok(ret, "Failed to set hardware ID, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, 0, &driver);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_NO_MORE_ITEMS, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiGetDeviceInstallParamsA(set, &device, &params);
      ok(ret, "Failed to get device install params, error %#x.\n", GetLastError());
      strcpy(params.DriverPath, inf_path);
      params.Flags = DI_ENUMSINGLEINF;
      ret = SetupDiSetDeviceInstallParamsA(set, &device, &params);
      ok(ret, "Failed to set device install params, error %#x.\n", GetLastError());
  
      ret = SetupDiBuildDriverInfoList(set, &device, SPDIT_COMPATDRIVER);
      ok(ret, "Failed to build driver list, error %#x.\n", GetLastError());
  
      idx = 0;
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, idx++, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc0"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, wow64 ? "mfg1_wow" : "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, idx++, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc1"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, wow64 ? "mfg1_wow" : "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      expect_size = FIELD_OFFSET(SP_DRVINFO_DETAIL_DATA_A, HardwareID[sizeof("bogus_hardware_id\0")]);
  
      ret = SetupDiGetDriverInfoDetailA(set, &device, &driver, NULL, 0, &size);
      ok(ret || GetLastError() == ERROR_INVALID_USER_BUFFER /* Win10 1809 */,
              "Failed to get driver details, error %#x.\n", GetLastError());
      ok(size == expect_size, "Got size %u.\n", size);
  
      ret = SetupDiGetDriverInfoDetailA(set, &device, &driver, NULL, sizeof(SP_DRVINFO_DETAIL_DATA_A) - 1, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(size == expect_size, "Got size %u.\n", size);
  
      size = 0xdeadbeef;
      ret = SetupDiGetDriverInfoDetailA(set, &device, &driver, detail, 0, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(size == 0xdeadbeef, "Got size %u.\n", size);
  
      size = 0xdeadbeef;
      detail->CompatIDsLength = 0xdeadbeef;
      ret = SetupDiGetDriverInfoDetailA(set, &device, &driver, detail, sizeof(SP_DRVINFO_DETAIL_DATA_A) - 1, &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(size == 0xdeadbeef, "Got size %u.\n", size);
      ok(detail->CompatIDsLength == 0xdeadbeef, "Got wrong compat IDs length %u.\n", detail->CompatIDsLength);
  
      memset(detail_buffer, 0xcc, sizeof(detail_buffer));
      detail->cbSize = sizeof(*detail);
      ret = SetupDiGetDriverInfoDetailA(set, &device, &driver, detail, sizeof(SP_DRVINFO_DETAIL_DATA_A), NULL);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(detail->InfDate.dwHighDateTime == filetime.dwHighDateTime
              && detail->InfDate.dwLowDateTime == filetime.dwLowDateTime,
              "Expected %#x%08x, got %#x%08x.\n", filetime.dwHighDateTime, filetime.dwLowDateTime,
              detail->InfDate.dwHighDateTime, detail->InfDate.dwLowDateTime);
      ok(!strcmp(detail->SectionName, "install1"), "Got section name %s.\n", debugstr_a(detail->SectionName));
      ok(!stricmp(detail->InfFileName, short_path), "Got INF file name %s.\n", debugstr_a(detail->InfFileName));
      ok(!strcmp(detail->DrvDescription, "desc1"), "Got description %s.\n", debugstr_a(detail->DrvDescription));
      ok(!detail->CompatIDsOffset || detail->CompatIDsOffset == sizeof("bogus_hardware_id") /* Win10 1809 */,
              "Got wrong compat IDs offset %u.\n", detail->CompatIDsOffset);
      ok(!detail->CompatIDsLength, "Got wrong compat IDs length %u.\n", detail->CompatIDsLength);
      ok(!detail->HardwareID[0], "Got wrong ID list.\n");
  
      size = 0xdeadbeef;
      ret = SetupDiGetDriverInfoDetailA(set, &device, &driver, detail, sizeof(SP_DRVINFO_DETAIL_DATA_A), &size);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#x.\n", GetLastError());
      ok(size == expect_size, "Got size %u.\n", size);
  
      size = 0xdeadbeef;
      ret = SetupDiGetDriverInfoDetailA(set, &device, &driver, detail, sizeof(detail_buffer), &size);
      ok(ret, "Failed to get driver details, error %#x.\n", GetLastError());
      ok(size == expect_size, "Got size %u.\n", size);
      ok(detail->InfDate.dwHighDateTime == filetime.dwHighDateTime
              && detail->InfDate.dwLowDateTime == filetime.dwLowDateTime,
              "Expected %#x%08x, got %#x%08x.\n", filetime.dwHighDateTime, filetime.dwLowDateTime,
              detail->InfDate.dwHighDateTime, detail->InfDate.dwLowDateTime);
      ok(!strcmp(detail->SectionName, "install1"), "Got section name %s.\n", debugstr_a(detail->SectionName));
      ok(!stricmp(detail->InfFileName, short_path), "Got INF file name %s.\n", debugstr_a(detail->InfFileName));
      ok(!strcmp(detail->DrvDescription, "desc1"), "Got description %s.\n", debugstr_a(detail->DrvDescription));
      ok(!detail->CompatIDsOffset, "Got wrong compat IDs offset %u.\n", detail->CompatIDsOffset);
      ok(!detail->CompatIDsLength, "Got wrong compat IDs length %u.\n", detail->CompatIDsLength);
      ok(!memcmp(detail->HardwareID, "bogus_hardware_id\0", sizeof("bogus_hardware_id\0")), "Got wrong ID list.\n");
  
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, idx++, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc2"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, wow64 ? "mfg1_wow" : "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, idx++, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc4"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, wow64 ? "mfg1_wow" : "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
      ret = SetupDiGetDriverInfoDetailA(set, &device, &driver, detail, sizeof(detail_buffer), NULL);
      ok(ret, "Failed to get driver details, error %#x.\n", GetLastError());
      ok(detail->InfDate.dwHighDateTime == filetime.dwHighDateTime
              && detail->InfDate.dwLowDateTime == filetime.dwLowDateTime,
              "Expected %#x%08x, got %#x%08x.\n", filetime.dwHighDateTime, filetime.dwLowDateTime,
              detail->InfDate.dwHighDateTime, detail->InfDate.dwLowDateTime);
      ok(!detail->SectionName[0], "Got section name %s.\n", debugstr_a(detail->SectionName));
      ok(!stricmp(detail->InfFileName, short_path), "Got INF file name %s.\n", debugstr_a(detail->InfFileName));
      ok(!strcmp(detail->DrvDescription, "desc4"), "Got description %s.\n", debugstr_a(detail->DrvDescription));
      ok(detail->CompatIDsOffset == sizeof("wrong_hardware_id"), "Got wrong compat IDs offset %u.\n", detail->CompatIDsOffset);
      ok(detail->CompatIDsLength == sizeof("bogus_compat_id\0"), "Got wrong compat IDs length %u.\n", detail->CompatIDsLength);
      ok(!memcmp(detail->HardwareID, "wrong_hardware_id\0bogus_compat_id\0",
              sizeof("wrong_hardware_id\0bogus_compat_id\0")), "Got wrong ID list.\n");
  
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, idx++, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc5"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, wow64 ? "mfg2_wow" : "mfg2"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, idx++, &driver);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_NO_MORE_ITEMS, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiGetSelectedDriverA(set, &device, &driver);
      ok(ret /* Win10 1809 */ || GetLastError() == ERROR_NO_DRIVER_SELECTED,
              "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiSelectBestCompatDrv(set, &device);
      ok(ret, "Failed to select driver, error %#x.\n", GetLastError());
  
      ret = SetupDiGetSelectedDriverA(set, &device, &driver);
      ok(ret, "Failed to get selected driver, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc1"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, wow64 ? "mfg1_wow" : "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      SetupDiDestroyDeviceInfoList(set);
      ret = DeleteFileA(inf_path);
      ok(ret, "Failed to delete %s, error %u.\n", inf_path, GetLastError());
  
      /* Test building from a path. */
  
      GetTempPathA(sizeof(inf_dir), inf_dir);
      strcat(inf_dir, "setupapi_test");
      ret = CreateDirectoryA(inf_dir, NULL);
      ok(ret, "Failed to create directory, error %u.\n", GetLastError());
      sprintf(inf_path, "%s/test1.inf", inf_dir);
      create_file(inf_path, inf_data_file1);
      sprintf(inf_path2, "%s/test2.inf", inf_dir);
      create_file(inf_path2, inf_data_file2);
  
      set = SetupDiCreateDeviceInfoList(NULL, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\BOGUS\\0000", &GUID_NULL, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiSetDeviceRegistryPropertyA(set, &device, SPDRP_HARDWAREID,
              (const BYTE *)hardware_id, sizeof(hardware_id));
      ok(ret, "Failed to set hardware ID, error %#x.\n", GetLastError());
  
      ret = SetupDiGetDeviceInstallParamsA(set, &device, &params);
      ok(ret, "Failed to get device install params, error %#x.\n", GetLastError());
      strcpy(params.DriverPath, inf_dir);
      ret = SetupDiSetDeviceInstallParamsA(set, &device, &params);
      ok(ret, "Failed to set device install params, error %#x.\n", GetLastError());
  
      ret = SetupDiBuildDriverInfoList(set, &device, SPDIT_COMPATDRIVER);
      ok(ret, "Failed to build driver list, error %#x.\n", GetLastError());
  
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, 0, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc1"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, 1, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc2"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, 2, &driver);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_NO_MORE_ITEMS, "Got unexpected error %#x.\n", GetLastError());
  
      SetupDiDestroyDeviceInfoList(set);
      ret = DeleteFileA(inf_path);
      ok(ret, "Failed to delete %s, error %u.\n", inf_path, GetLastError());
      ret = DeleteFileA(inf_path2);
      ok(ret, "Failed to delete %s, error %u.\n", inf_path2, GetLastError());
      ret = RemoveDirectoryA(inf_dir);
      ok(ret, "Failed to delete %s, error %u.\n", inf_dir, GetLastError());
  
      /* Test the default path. */
  
      create_file("C:/windows/inf/wine_test1.inf", inf_data_file1);
      create_file("C:/windows/inf/wine_test2.inf", inf_data_file2);
  
      set = SetupDiCreateDeviceInfoList(NULL, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\BOGUS\\0000", &GUID_NULL, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiSetDeviceRegistryPropertyA(set, &device, SPDRP_HARDWAREID,
              (const BYTE *)hardware_id, sizeof(hardware_id));
      ok(ret, "Failed to set hardware ID, error %#x.\n", GetLastError());
  
      ret = SetupDiBuildDriverInfoList(set, &device, SPDIT_COMPATDRIVER);
      ok(ret, "Failed to build driver list, error %#x.\n", GetLastError());
  
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, 0, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc1"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, 1, &driver);
      ok(ret, "Failed to enumerate drivers, error %#x.\n", GetLastError());
      ok(driver.DriverType == SPDIT_COMPATDRIVER, "Got wrong type %#x.\n", driver.DriverType);
      ok(!strcmp(driver.Description, "desc2"), "Got wrong description '%s'.\n", driver.Description);
      ok(!strcmp(driver.MfgName, "mfg1"), "Got wrong manufacturer '%s'.\n", driver.MfgName);
      ok(!strcmp(driver.ProviderName, ""), "Got wrong provider '%s'.\n", driver.ProviderName);
  
      SetLastError(0xdeadbeef);
      ret = SetupDiEnumDriverInfoA(set, &device, SPDIT_COMPATDRIVER, 2, &driver);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_NO_MORE_ITEMS, "Got unexpected error %#x.\n", GetLastError());
  
      SetupDiDestroyDeviceInfoList(set);
      ret = DeleteFileA("C:/windows/inf/wine_test1.inf");
      ok(ret, "Failed to delete %s, error %u.\n", inf_path, GetLastError());
      ret = DeleteFileA("C:/windows/inf/wine_test2.inf");
      ok(ret, "Failed to delete %s, error %u.\n", inf_path2, GetLastError());
      /* Windows "precompiles" INF files in this dir; try to avoid leaving them behind. */
      DeleteFileA("C:/windows/inf/wine_test1.pnf");
      DeleteFileA("C:/windows/inf/wine_test2.pnf");
  }
  
  static BOOL device_is_registered(HDEVINFO set, SP_DEVINFO_DATA *device)
  {
      HKEY key = SetupDiOpenDevRegKey(set, device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, 0);
      ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n");
      RegCloseKey(key);
      return GetLastError() == ERROR_KEY_DOES_NOT_EXIST;
  }
  
  static unsigned int *coinst_callback_count;
  static DI_FUNCTION *coinst_last_message;
  
  static void test_class_installer(void)
  {
      SP_DEVINFO_DATA device = {sizeof(device)};
      char regdata[200];
      HKEY class_key;
      HDEVINFO set;
      BOOL ret;
      LONG res;
  
      res = RegCreateKeyA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Class"
              "\\{6a55b5a4-3f65-11db-b704-0011955c2bdb}", &class_key);
      ok(!res, "Failed to create class key, error %u.\n", res);
  
      strcpy(regdata, "winetest_coinst.dll,class_success");
      res = RegSetValueExA(class_key, "Installer32", 0, REG_SZ, (BYTE *)regdata, strlen(regdata)+1);
      ok(!res, "Failed to set registry value, error %u.\n", res);
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_ALLOW_INSTALL, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      ret = SetupDiCallClassInstaller(0xdeadbeef, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == 0xdeadbeef, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
      ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_REGISTERDEVICE, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      ret = SetupDiCallClassInstaller(DIF_REMOVE, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_REMOVE, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list.\n");
      ok(!GetLastError(), "Got unexpected error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_DESTROYPRIVATEDATA, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      /* Test returning an error. */
  
      strcpy(regdata, "winetest_coinst.dll,class_error");
      res = RegSetValueExA(class_key, "Installer32", 0, REG_SZ, (BYTE *)regdata, strlen(regdata)+1);
      ok(!res, "Failed to set registry value, error %u.\n", res);
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == 0xdeadc0de, "Got unexpected error %#x.\n", GetLastError());
  
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
      ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == 0xdeadc0de, "Got unexpected error %#x.\n", GetLastError());
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
  
      ret = SetupDiCallClassInstaller(DIF_REMOVE, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == 0xdeadc0de, "Got unexpected error %#x.\n", GetLastError());
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
  
      SetLastError(0xdeadbeef);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list.\n");
      ok(!GetLastError(), "Got unexpected error %#x.\n", GetLastError());
  
      /* Test returning ERROR_DI_DO_DEFAULT. */
  
      strcpy(regdata, "winetest_coinst.dll,class_default");
      res = RegSetValueExA(class_key, "Installer32", 0, REG_SZ, (BYTE *)regdata, strlen(regdata)+1);
      ok(!res, "Failed to set registry value, error %u.\n", res);
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
  
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
      ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
      ok(device_is_registered(set, &device), "Expected device to be registered.\n");
  
      ret = SetupDiCallClassInstaller(DIF_REMOVE, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
  
      SetLastError(0xdeadbeef);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list.\n");
      ok(!GetLastError(), "Got unexpected error %#x.\n", GetLastError());
  
      /* The default entry point is ClassInstall(). */
  
      strcpy(regdata, "winetest_coinst.dll");
      res = RegSetValueExA(class_key, "Installer32", 0, REG_SZ, (BYTE *)regdata, strlen(regdata)+1);
      ok(!res, "Failed to set registry value, error %u.\n", res);
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_ALLOW_INSTALL, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list.\n");
      ok(!GetLastError(), "Got unexpected error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_DESTROYPRIVATEDATA, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      res = RegDeleteKeyA(class_key, "");
      ok(!res, "Failed to delete class key, error %u.\n", res);
      RegCloseKey(class_key);
  }
  
  static void test_class_coinstaller(void)
  {
      SP_DEVINFO_DATA device = {sizeof(device)};
      char regdata[200];
      HKEY coinst_key;
      HDEVINFO set;
      BOOL ret;
      LONG res;
  
      res = RegCreateKeyA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\CoDeviceInstallers", &coinst_key);
      ok(!res, "Failed to open CoDeviceInstallers key, error %u.\n", res);
      strcpy(regdata, "winetest_coinst.dll,co_success");
      regdata[strlen(regdata) + 1] = 0;
      res = RegSetValueExA(coinst_key, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}", 0,
              REG_MULTI_SZ, (BYTE *)regdata, strlen(regdata) + 2);
      ok(!res, "Failed to set registry value, error %u.\n", res);
  
      /* We must recreate the device list, or Windows will not recognize that the
       * class co-installer exists. */
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_ALLOW_INSTALL, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      ret = SetupDiCallClassInstaller(0xdeadbeef, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == 0xdeadbeef, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
      ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
      ok(device_is_registered(set, &device), "Expected device to be registered.\n");
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_REGISTERDEVICE, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      ret = SetupDiCallClassInstaller(DIF_REMOVE, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_REMOVE, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list.\n");
      ok(!GetLastError(), "Got unexpected error %#x.\n", GetLastError());
  
      todo_wine ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      todo_wine ok(*coinst_last_message == DIF_DESTROYPRIVATEDATA, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      /* Test returning an error from the co-installer. */
  
      strcpy(regdata, "winetest_coinst.dll,co_error");
      regdata[strlen(regdata) + 1] = 0;
      res = RegSetValueExA(coinst_key, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}", 0,
              REG_MULTI_SZ, (BYTE *)regdata, strlen(regdata) + 2);
      ok(!res, "Failed to set registry value, error %u.\n", res);
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == 0xdeadc0de, "Got unexpected error %#x.\n", GetLastError());
  
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
      ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == 0xdeadc0de, "Got unexpected error %#x.\n", GetLastError());
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
  
      SetLastError(0xdeadbeef);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list.\n");
      ok(!GetLastError(), "Got unexpected error %#x.\n", GetLastError());
  
      /* The default entry point is CoDeviceInstall(). */
  
      strcpy(regdata, "winetest_coinst.dll");
      regdata[strlen(regdata) + 1] = 0;
      res = RegSetValueExA(coinst_key, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}", 0,
              REG_MULTI_SZ, (BYTE *)regdata, strlen(regdata) + 2);
      ok(!res, "Failed to set registry value, error %u.\n", res);
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_ALLOW_INSTALL, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      SetLastError(0xdeadbeef);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list.\n");
      ok(!GetLastError(), "Got unexpected error %#x.\n", GetLastError());
  
      ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
      ok(*coinst_last_message == DIF_DESTROYPRIVATEDATA, "Got unexpected message %#x.\n", *coinst_last_message);
      *coinst_callback_count = 0;
  
      res = RegDeleteValueA(coinst_key, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}");
      ok(!res, "Failed to delete value, error %u.\n", res);
      RegCloseKey(coinst_key);
  }
  
  static void test_call_class_installer(void)
  {
      SP_DEVINFO_DATA device = {sizeof(device)};
      HMODULE coinst;
      HDEVINFO set;
      BOOL ret;
  
      if (wow64)
      {
          skip("SetupDiCallClassInstaller() does not work on WoW64.\n");
          return;
      }
  
      set = SetupDiCreateDeviceInfoList(&guid, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
  
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
      ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
      ok(device_is_registered(set, &device), "Expected device to be registered.\n");
  
      /* This is probably not failure per se, but rather an indication that no
       * class installer was called and no default handler exists. */
      ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(0xdeadbeef, set, &device);
      ok(!ret, "Expected failure.\n");
      ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
  
      ret = SetupDiCallClassInstaller(DIF_REMOVE, set, &device);
      ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
      ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
  
      SetLastError(0xdeadbeef);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list.\n");
      ok(!GetLastError(), "Got unexpected error %#x.\n", GetLastError());
  
      load_resource("coinst.dll", "C:\\windows\\system32\\winetest_coinst.dll");
  
      coinst = LoadLibraryA("winetest_coinst.dll");
      coinst_callback_count = (void *)GetProcAddress(coinst, "callback_count");
      coinst_last_message = (void *)GetProcAddress(coinst, "last_message");
  
      test_class_installer();
      test_class_coinstaller();
  
      FreeLibrary(coinst);
  
      ret = DeleteFileA("C:\\windows\\system32\\winetest_coinst.dll");
      ok(ret, "Failed to delete file, error %u.\n", GetLastError());
  }
  
  static void check_all_devices_enumerated_(int line, HDEVINFO set, BOOL expect_dev3)
  {
      SP_DEVINFO_DATA device = {sizeof(device)};
      BOOL ret, found_dev1 = 0, found_dev2 = 0, found_dev3 = 0;
      char id[50];
      DWORD i;
  
      for (i = 0; SetupDiEnumDeviceInfo(set, i, &device); ++i)
      {
          ret = SetupDiGetDeviceInstanceIdA(set, &device, id, sizeof(id), NULL);
          if (!ret) continue;
  
          if (!strcasecmp(id, "Root\\LEGACY_BOGUS\\foo"))
          {
              found_dev1 = 1;
              ok_(__FILE__, line)(IsEqualGUID(&device.ClassGuid, &guid),
                      "Got unexpected class %s.\n", wine_dbgstr_guid(&device.ClassGuid));
          }
          else if (!strcasecmp(id, "Root\\LEGACY_BOGUS\\qux"))
          {
              found_dev2 = 1;
              ok_(__FILE__, line)(IsEqualGUID(&device.ClassGuid, &guid),
                      "Got unexpected class %s.\n", wine_dbgstr_guid(&device.ClassGuid));
          }
          else if (!strcasecmp(id, "Root\\LEGACY_BOGUS\\bar"))
          {
              found_dev3 = 1;
              ok_(__FILE__, line)(IsEqualGUID(&device.ClassGuid, &guid2),
                      "Got unexpected class %s.\n", wine_dbgstr_guid(&device.ClassGuid));
          }
      }
      ok_(__FILE__, line)(found_dev1, "Expected device 1 to be enumerated.\n");
      ok_(__FILE__, line)(found_dev2, "Expected device 2 to be enumerated.\n");
      ok_(__FILE__, line)(found_dev3 == expect_dev3, "Expected device 2 %sto be enumerated.\n",
              expect_dev3 ? "" : "not ");
  }
  #define check_all_devices_enumerated(a,b) check_all_devices_enumerated_(__LINE__,a,b)
  
  static void check_device_list_(int line, HDEVINFO set, const GUID *expect)
  {
      SP_DEVINFO_LIST_DETAIL_DATA_A detail = {sizeof(detail)};
      BOOL ret = SetupDiGetDeviceInfoListDetailA(set, &detail);
      ok_(__FILE__, line)(ret, "Failed to get list detail, error %#x.\n", GetLastError());
      ok_(__FILE__, line)(IsEqualGUID(&detail.ClassGuid, expect), "Expected class %s, got %s\n",
              wine_dbgstr_guid(expect), wine_dbgstr_guid(&detail.ClassGuid));
  }
  #define check_device_list(a,b) check_device_list_(__LINE__,a,b)
  
  static void test_get_class_devs(void)
  {
      SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
      SP_DEVINFO_DATA device = {sizeof(device)};
      HDEVINFO set;
      BOOL ret;
  
      set = SetupDiCreateDeviceInfoList(NULL, NULL);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\foo", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &iface_guid, NULL, 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &iface_guid2, NULL, 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\qux", &guid, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiCreateDeviceInterfaceA(set, &device, &iface_guid, NULL, 0, &iface);
      ok(ret, "Failed to create interface, error %#x.\n", GetLastError());
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
  
      ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\bar", &guid2, NULL, NULL, 0, &device);
      ok(ret, "Failed to create device, error %#x.\n", GetLastError());
      ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL);
      ok(ret, "Failed to register device, error %#x.\n", GetLastError());
  
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      set = SetupDiGetClassDevsA(NULL, NULL, NULL, 0);
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(NULL, NULL, NULL, DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_all_devices_enumerated(set, TRUE);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&guid, NULL, NULL, 0);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &guid);
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\FOO");
      check_device_info(set, 1, &guid, "ROOT\\LEGACY_BOGUS\\QUX");
      check_device_info(set, 2, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&guid, NULL, NULL, DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_all_devices_enumerated(set, TRUE);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      set = SetupDiGetClassDevsA(NULL, "ROOT", NULL, 0);
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(NULL, "ROOT", NULL, DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_all_devices_enumerated(set, TRUE);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(NULL, "ROOT\\LEGACY_BOGUS", NULL, DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_device_info(set, 0, &guid2, "ROOT\\LEGACY_BOGUS\\BAR");
      check_device_info(set, 1, &guid, "ROOT\\LEGACY_BOGUS\\FOO");
      check_device_info(set, 2, &guid, "ROOT\\LEGACY_BOGUS\\QUX");
      check_device_info(set, 3, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&guid, "ROOT\\LEGACY_BOGUS", NULL, 0);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &guid);
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\FOO");
      check_device_info(set, 1, &guid, "ROOT\\LEGACY_BOGUS\\QUX");
      check_device_info(set, 2, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&guid, "ROOT\\LEGACY_BOGUS", NULL, DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_device_info(set, 0, &guid2, "ROOT\\LEGACY_BOGUS\\BAR");
      check_device_info(set, 1, &guid, "ROOT\\LEGACY_BOGUS\\FOO");
      check_device_info(set, 2, &guid, "ROOT\\LEGACY_BOGUS\\QUX");
      check_device_info(set, 3, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      /* test DIGCF_DEVICE_INTERFACE */
  
      SetLastError(0xdeadbeef);
      set = SetupDiGetClassDevsA(NULL, NULL, NULL, DIGCF_DEVICEINTERFACE);
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(NULL, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_all_devices_enumerated(set, FALSE);
      check_device_iface(set, NULL, &iface_guid, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 1, 0, "\\\\?\\root#legacy_bogus#qux#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 2, 0, NULL);
      check_device_iface(set, NULL, &iface_guid2, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadf00d-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid2, 1, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_device_info(set, 0, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      check_device_iface(set, NULL, &iface_guid2, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&iface_guid, NULL, NULL, DIGCF_DEVICEINTERFACE);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\FOO");
      check_device_info(set, 1, &guid, "ROOT\\LEGACY_BOGUS\\QUX");
      check_device_info(set, 2, &guid, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 1, 0, "\\\\?\\root#legacy_bogus#qux#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 2, 0, NULL);
      check_device_iface(set, NULL, &iface_guid2, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&iface_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_all_devices_enumerated(set, FALSE);
      check_device_iface(set, NULL, &iface_guid, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 1, 0, "\\\\?\\root#legacy_bogus#qux#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 2, 0, NULL);
      check_device_iface(set, NULL, &iface_guid2, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadf00d-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid2, 1, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      set = SetupDiGetClassDevsA(NULL, "ROOT", NULL, DIGCF_DEVICEINTERFACE);
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      set = SetupDiGetClassDevsA(NULL, "ROOT", NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
  todo_wine {
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  }
  
      SetLastError(0xdeadbeef);
      set = SetupDiGetClassDevsA(NULL, "ROOT\\LEGACY_BOGUS", NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
  todo_wine {
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  }
  
      set = SetupDiGetClassDevsA(NULL, "ROOT\\LEGACY_BOGUS\\foo", NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\FOO");
      check_device_info(set, 1, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 1, 0, NULL);
      check_device_iface(set, NULL, &iface_guid2, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadf00d-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid2, 1, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(NULL, "ROOT\\LEGACY_BOGUS\\bar", NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_device_info(set, 0, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, NULL);
      check_device_iface(set, NULL, &iface_guid2, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      SetLastError(0xdeadbeef);
      set = SetupDiGetClassDevsA(&iface_guid, "ROOT\\LEGACY_BOGUS", NULL, DIGCF_DEVICEINTERFACE);
  todo_wine {
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  }
  
      SetLastError(0xdeadbeef);
      set = SetupDiGetClassDevsA(&iface_guid, "ROOT\\LEGACY_BOGUS", NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
  todo_wine {
      ok(set == INVALID_HANDLE_VALUE, "Expected failure.\n");
      ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#x.\n", GetLastError());
  }
  
      set = SetupDiGetClassDevsA(&iface_guid, "ROOT\\LEGACY_BOGUS\\foo", NULL, DIGCF_DEVICEINTERFACE);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\FOO");
      check_device_info(set, 1, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 1, 0, NULL);
      check_device_iface(set, NULL, &iface_guid2, 0, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&iface_guid, "ROOT\\LEGACY_BOGUS\\foo", NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
      ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
      check_device_list(set, &GUID_NULL);
      check_device_info(set, 0, &guid, "ROOT\\LEGACY_BOGUS\\FOO");
      check_device_info(set, 1, NULL, NULL);
      check_device_iface(set, NULL, &iface_guid, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadbeef-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid, 1, 0, NULL);
      check_device_iface(set, NULL, &iface_guid2, 0, 0, "\\\\?\\root#legacy_bogus#foo#{deadf00d-3f65-11db-b704-0011955c2bdb}");
      check_device_iface(set, NULL, &iface_guid2, 1, 0, NULL);
      ret = SetupDiDestroyDeviceInfoList(set);
      ok(ret, "Failed to destroy device list, error %#x.\n", GetLastError());
  
      set = SetupDiGetClassDevsA(&guid, NULL, NULL, 0);
      SetupDiEnumDeviceInfo(set, 0, &device);
      SetupDiRemoveDevice(set, &device);
      SetupDiEnumDeviceInfo(set, 1, &device);
      SetupDiRemoveDevice(set, &device);
      SetupDiDestroyDeviceInfoList(set);
      set = SetupDiGetClassDevsA(&guid2, NULL, NULL, 0);
      SetupDiEnumDeviceInfo(set, 0, &device);
      SetupDiRemoveDevice(set, &device);
      SetupDiDestroyDeviceInfoList(set);
  }
  
  START_TEST(devinst)
  {
      static BOOL (WINAPI *pIsWow64Process)(HANDLE, BOOL *);
      HKEY hkey;
  
      test_get_actual_section();
  
      if ((hkey = SetupDiOpenClassRegKey(NULL, KEY_ALL_ACCESS)) == INVALID_HANDLE_VALUE)
      {
          skip("needs admin rights\n");
          return;
      }
      RegCloseKey(hkey);
  
      pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
      if (pIsWow64Process) pIsWow64Process(GetCurrentProcess(), &wow64);
  
      test_create_device_list_ex();
      test_open_class_key();
      test_install_class();
      test_device_info();
      test_device_property();
      test_get_device_instance_id();
      test_open_device_info();
      test_register_device_info();
      test_device_iface();
      test_device_iface_detail();
      test_device_key();
      test_register_device_iface();
      test_registry_property_a();
      test_registry_property_w();
      test_get_inf_class();
      test_devnode();
      test_device_interface_key();
      test_open_device_interface_key();
      test_device_install_params();
      test_driver_list();
      test_call_class_installer();
      test_get_class_devs();
  }
  