Replace some strings by constants
[reactos.git] / reactos / lib / setupapi / devinst.c
index 9c5b945..46eff4c 100644 (file)
@@ -2,7 +2,7 @@
  * SetupAPI device installer
  *
  * Copyright 2000 Andreas Mohr for CodeWeavers
- *           2005 HervĂ© Poussineau (hpoussin@reactos.com)
+ *           2005 HervĂ© Poussineau (hpoussin@reactos.org)
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include "config.h"
-#include "wine/port.h"
-
-#include <stdarg.h>
-
-#include <windows.h>
-#include "setupapi.h"
-#include "wine/debug.h"
-#include "wine/unicode.h"
-#include "cfgmgr32.h"
-#include "initguid.h"
-#define NTOS_MODE_USER
-#include <ndk/ntndk.h>
-
+#define INITGUID
 #include "setupapi_private.h"
 
-
 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
 
 /* Unicode constants */
@@ -43,31 +29,13 @@ static const WCHAR ClassGUID[]  = {'C','l','a','s','s','G','U','I','D',0};
 static const WCHAR Class[]  = {'C','l','a','s','s',0};
 static const WCHAR ClassInstall32[]  = {'C','l','a','s','s','I','n','s','t','a','l','l','3','2',0};
 static const WCHAR DeviceInstance[]  = {'D','e','v','i','c','e','I','n','s','t','a','n','c','e',0};
-static const WCHAR NoDisplayClass[]  = {'N','o','D','i','s','p','l','a','y','C','l','a','s','s',0};
-static const WCHAR NoInstallClass[]  = {'N','o','I','s','t','a','l','l','C','l','a','s','s',0};
-static const WCHAR NoUseClass[]  = {'N','o','U','s','e','C','l','a','s','s',0};
+static const WCHAR InterfaceInstall32[]  = {'I','n','t','e','r','f','a','c','e','I','n','s','t','a','l','l','3','2',0};
 static const WCHAR NtExtension[]  = {'.','N','T',0};
 static const WCHAR NtPlatformExtension[]  = {'.','N','T','x','8','6',0};
 static const WCHAR SymbolicLink[]  = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
 static const WCHAR Version[]  = {'V','e','r','s','i','o','n',0};
 static const WCHAR WinExtension[]  = {'.','W','i','n',0};
 
-/* Registry key and value names */
-static const WCHAR ControlClass[] = {'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',0};
-
-static const WCHAR DeviceClasses[] = {'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','\\',
-                                  'D','e','v','i','c','e','C','l','a','s','s','e','s',0};
-
-static const WCHAR EnumKeyName[] = {'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',0};
-
-
 /* FIXME: header mess */
 DEFINE_GUID(GUID_NULL,
   0x00000000L, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
@@ -80,14 +48,23 @@ typedef BOOL
 (WINAPI* DEFAULT_CLASS_INSTALL_PROC) (
     IN HDEVINFO DeviceInfoSet,
     IN OUT PSP_DEVINFO_DATA DeviceInfoData);
-typedef DWORD 
+typedef DWORD
 (CALLBACK* COINSTALLER_PROC) (
     IN DI_FUNCTION InstallFunction,
     IN HDEVINFO DeviceInfoSet,
     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
     IN OUT PCOINSTALLER_CONTEXT_DATA Context);
-
-#define SETUP_DEV_INFO_SET_MAGIC 0xd00ff057
+typedef BOOL
+(WINAPI* PROPERTY_PAGE_PROVIDER) (
+    IN PSP_PROPSHEETPAGE_REQUEST PropPageRequest,
+    IN LPFNADDPROPSHEETPAGE fAddFunc,
+    IN LPARAM lParam);
+typedef BOOL
+(*UPDATE_CLASS_PARAM_HANDLER) (
+    IN HDEVINFO DeviceInfoSet,
+    IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+    IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
+    IN DWORD ClassInstallParamsSize);
 
 struct CoInstallerElement
 {
@@ -99,103 +76,56 @@ struct CoInstallerElement
     PVOID PrivateData;
 };
 
-struct DeviceInterface /* Element of DeviceInfoElement.InterfaceListHead */
-{
-    LIST_ENTRY ListEntry;
-
-    struct DeviceInfoElement* DeviceInfo;
-    GUID InterfaceClassGuid;
-
-    
-    /* SPINT_ACTIVE : the interface is active/enabled
-     * SPINT_DEFAULT: the interface is the default interface for the device class FIXME???
-     * SPINT_REMOVED: the interface is removed
-     */
-    DWORD Flags;
-
-    WCHAR SymbolicLink[0]; /* \\?\ACPI#PNP0501#4&2658d0a0&0#{GUID} */
-};
-
-/* We don't want to open the .inf file to read only one information in it, so keep a handle to it once it
- * has been already loaded once. Keep also a reference counter */
-struct InfFileDetails
-{
-    HINF hInf;
-    LONG References;
-};
-
-struct DriverInfoElement /* Element of DeviceInfoSet.DriverListHead and DeviceInfoElement.DriverListHead */
-{
-    LIST_ENTRY ListEntry;
-
-    DWORD DriverRank;
-    SP_DRVINFO_DATA_V2_W Info;
-    SP_DRVINFO_DETAIL_DATA_W Details;
-    GUID ClassGuid;
-    LPWSTR MatchingId;
-    struct InfFileDetails *InfFileDetails;
-};
-
-struct DeviceInfoElement /* Element of DeviceInfoSet.ListHead */
-{
-    LIST_ENTRY ListEntry;
-
-    /* Reserved Field points to a struct DriverInfoElement */
-    SP_DEVINSTALL_PARAMS_W InstallParams;
-
-    /* Information about devnode:
-     * - DeviceName:
-     *       "Root\*PNP0501" for example.
-     *       It doesn't contain the unique ID for the device
-     *       (points into the Data field at the end of the structure)
-     *       WARNING: no NULL char exist between DeviceName and UniqueId
-     *       in Data field!
-     * - UniqueId
-     *       "5&1be2108e&0" or "0000"
-     *       If DICD_GENERATE_ID is specified in creation flags,
-     *       this unique ID is autogenerated using 4 digits, base 10
-     *       (points into the Data field at the end of the structure)
-     * - DeviceDescription
-     *       String which identifies the device. Can be NULL. If not NULL,
-     *       points into the Data field at the end of the structure
-     * - ClassGuid
-     *       Identifies the class of this device. FIXME: can it be GUID_NULL?
-     * - CreationFlags
-     *       Is a combination of:
-     *       - DICD_GENERATE_ID
-     *              the unique ID needs to be generated
-     *       - DICD_INHERIT_CLASSDRVS
-     *              inherit driver of the device info set (== same pointer)
-     */
-    PCWSTR DeviceName;
-    PCWSTR UniqueId;
-    PCWSTR DeviceDescription;
-    GUID ClassGuid;
-    DWORD CreationFlags;
-
-    /* If CreationFlags contains DICD_INHERIT_CLASSDRVS, this list is invalid */
-    /* If the driver is not searched/detected, this list is empty */
-    LIST_ENTRY DriverListHead; /* List of struct DriverInfoElement */
-
-    /* List of interfaces implemented by this device */
-    LIST_ENTRY InterfaceListHead; /* List of struct DeviceInterface */
-
-    WCHAR Data[0];
-};
-
-struct DeviceInfoSet /* HDEVINFO */
-{
-    DWORD magic; /* SETUP_DEV_INFO_SET_MAGIC */
-    GUID ClassGuid; /* If != GUID_NULL, only devices of this class can be in the device info set */
-    HKEY HKLM; /* Local or distant HKEY_LOCAL_MACHINE registry key */
-
-    /* Reserved Field points to a struct DriverInfoElement */
-    SP_DEVINSTALL_PARAMS_W InstallParams;
-
-    /* If the driver is not searched/detected, this list is empty */
-    LIST_ENTRY DriverListHead; /* List of struct DriverInfoElement */
-
-    LIST_ENTRY ListHead; /* List of struct DeviceInfoElement */
+static BOOL
+PropertyChangeHandler(
+    IN HDEVINFO DeviceInfoSet,
+    IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+    IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
+    IN DWORD ClassInstallParamsSize);
+
+static UPDATE_CLASS_PARAM_HANDLER UpdateClassInstallParamHandlers[] = {
+    NULL, /* DIF_SELECTDEVICE */
+    NULL, /* DIF_INSTALLDEVICE */
+    NULL, /* DIF_ASSIGNRESOURCES */
+    NULL, /* DIF_PROPERTIES */
+    NULL, /* DIF_REMOVE */
+    NULL, /* DIF_FIRSTTIMESETUP */
+    NULL, /* DIF_FOUNDDEVICE */
+    NULL, /* DIF_SELECTCLASSDRIVERS */
+    NULL, /* DIF_VALIDATECLASSDRIVERS */
+    NULL, /* DIF_INSTALLCLASSDRIVERS */
+    NULL, /* DIF_CALCDISKSPACE */
+    NULL, /* DIF_DESTROYPRIVATEDATA */
+    NULL, /* DIF_VALIDATEDRIVER */
+    NULL, /* DIF_MOVEDEVICE */
+    NULL, /* DIF_DETECT */
+    NULL, /* DIF_INSTALLWIZARD */
+    NULL, /* DIF_DESTROYWIZARDDATA */
+    PropertyChangeHandler, /* DIF_PROPERTYCHANGE */
+    NULL, /* DIF_ENABLECLASS */
+    NULL, /* DIF_DETECTVERIFY */
+    NULL, /* DIF_INSTALLDEVICEFILES */
+    NULL, /* DIF_UNREMOVE */
+    NULL, /* DIF_SELECTBESTCOMPATDRV */
+    NULL, /* DIF_ALLOW_INSTALL */
+    NULL, /* DIF_REGISTERDEVICE */
+    NULL, /* DIF_NEWDEVICEWIZARD_PRESELECT */
+    NULL, /* DIF_NEWDEVICEWIZARD_SELECT */
+    NULL, /* DIF_NEWDEVICEWIZARD_PREANALYZE */
+    NULL, /* DIF_NEWDEVICEWIZARD_POSTANALYZE */
+    NULL, /* DIF_NEWDEVICEWIZARD_FINISHINSTALL */
+    NULL, /* DIF_UNUSED1 */
+    NULL, /* DIF_INSTALLINTERFACES */
+    NULL, /* DIF_DETECTCANCEL */
+    NULL, /* DIF_REGISTER_COINSTALLERS */
+    NULL, /* DIF_ADDPROPERTYPAGE_ADVANCED */
+    NULL, /* DIF_ADDPROPERTYPAGE_BASIC */
+    NULL, /* DIF_RESERVED1 */
+    NULL, /* DIF_TROUBLESHOOTER */
+    NULL, /* DIF_POWERMESSAGEWAKE */
+    NULL, /* DIF_ADDREMOTEPROPERTYPAGE_ADVANCED */
+    NULL, /* DIF_UPDATEDRIVER_UI */
+    NULL  /* DIF_RESERVED2 */
 };
 
 /***********************************************************************
@@ -264,13 +194,14 @@ BOOL WINAPI SetupDiBuildClassInfoListExW(
     LONG lError;
     DWORD dwGuidListIndex = 0;
 
-    TRACE("\n");
+    TRACE("0x%lx %p %lu %p %s %p\n", Flags, ClassGuidList,
+        ClassGuidListSize, RequiredSize, debugstr_w(MachineName), Reserved);
 
     if (RequiredSize != NULL)
        *RequiredSize = 0;
 
     hClassesKey = SetupDiOpenClassRegKeyExW(NULL,
-                                            KEY_ALL_ACCESS,
+                                            KEY_ENUMERATE_SUB_KEYS,
                                             DIOCR_INSTALLER,
                                             MachineName,
                                             Reserved);
@@ -293,12 +224,12 @@ BOOL WINAPI SetupDiBuildClassInfoListExW(
        TRACE("RegEnumKeyExW() returns %ld\n", lError);
        if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA)
        {
-           TRACE("Key name: %p\n", szKeyName);
+           TRACE("Key name: %s\n", debugstr_w(szKeyName));
 
            if (RegOpenKeyExW(hClassesKey,
                              szKeyName,
                              0,
-                             KEY_ALL_ACCESS,
+                             KEY_QUERY_VALUE,
                              &hClassKey))
            {
                RegCloseKey(hClassesKey);
@@ -306,7 +237,7 @@ BOOL WINAPI SetupDiBuildClassInfoListExW(
            }
 
            if (!RegQueryValueExW(hClassKey,
-                                 NoUseClass,
+                                 REGSTR_VAL_NOUSECLASS,
                                  NULL,
                                  NULL,
                                  NULL,
@@ -319,7 +250,7 @@ BOOL WINAPI SetupDiBuildClassInfoListExW(
 
            if ((Flags & DIBCI_NOINSTALLCLASS) &&
                (!RegQueryValueExW(hClassKey,
-                                  NoInstallClass,
+                                  REGSTR_VAL_NOINSTALLCLASS,
                                   NULL,
                                   NULL,
                                   NULL,
@@ -332,7 +263,7 @@ BOOL WINAPI SetupDiBuildClassInfoListExW(
 
            if ((Flags & DIBCI_NODISPLAYCLASS) &&
                (!RegQueryValueExW(hClassKey,
-                                  NoDisplayClass,
+                                  REGSTR_VAL_NODISPLAYCLASS,
                                   NULL,
                                   NULL,
                                   NULL,
@@ -345,14 +276,14 @@ BOOL WINAPI SetupDiBuildClassInfoListExW(
 
            RegCloseKey(hClassKey);
 
-           TRACE("Guid: %p\n", szKeyName);
+           TRACE("Guid: %s\n", debugstr_w(szKeyName));
            if (dwGuidListIndex < ClassGuidListSize)
            {
                if (szKeyName[0] == L'{' && szKeyName[37] == L'}')
                {
                    szKeyName[37] = 0;
                }
-               TRACE("Guid: %p\n", &szKeyName[1]);
+               TRACE("Guid: %s\n", debugstr_w(&szKeyName[1]));
 
                UuidFromStringW(&szKeyName[1],
                                &ClassGuidList[dwGuidListIndex]);
@@ -470,6 +401,9 @@ BOOL WINAPI SetupDiClassGuidsFromNameExW(
     LONG lError;
     DWORD dwGuidListIndex = 0;
 
+    TRACE("%s %p %lu %p %s %p\n", debugstr_w(ClassName), ClassGuidList,
+        ClassGuidListSize, RequiredSize, debugstr_w(MachineName), Reserved);
+
     if (RequiredSize != NULL)
        *RequiredSize = 0;
 
@@ -497,7 +431,7 @@ BOOL WINAPI SetupDiClassGuidsFromNameExW(
        TRACE("RegEnumKeyExW() returns %ld\n", lError);
        if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA)
        {
-           TRACE("Key name: %p\n", szKeyName);
+           TRACE("Key name: %s\n", debugstr_w(szKeyName));
 
            if (RegOpenKeyExW(hClassesKey,
                              szKeyName,
@@ -517,20 +451,20 @@ BOOL WINAPI SetupDiClassGuidsFromNameExW(
                                  (LPBYTE)szClassName,
                                  &dwLength))
            {
-               TRACE("Class name: %p\n", szClassName);
+               TRACE("Class name: %s\n", debugstr_w(szClassName));
 
                if (strcmpiW(szClassName, ClassName) == 0)
                {
                    TRACE("Found matching class name\n");
 
-                   TRACE("Guid: %p\n", szKeyName);
+                   TRACE("Guid: %s\n", debugstr_w(szKeyName));
                    if (dwGuidListIndex < ClassGuidListSize)
                    {
                        if (szKeyName[0] == L'{' && szKeyName[37] == L'}')
                        {
                            szKeyName[37] = 0;
                        }
-                       TRACE("Guid: %p\n", &szKeyName[1]);
+                       TRACE("Guid: %s\n", debugstr_w(&szKeyName[1]));
 
                        UuidFromStringW(&szKeyName[1],
                                        &ClassGuidList[dwGuidListIndex]);
@@ -635,6 +569,9 @@ BOOL WINAPI SetupDiClassNameFromGuidExW(
     DWORD dwLength;
     LONG rc;
 
+    TRACE("%s %p %lu %p %s %p\n", debugstr_guid(ClassGuid), ClassName,
+        ClassNameSize, RequiredSize, debugstr_w(MachineName), Reserved);
+
     hKey = SetupDiOpenClassRegKeyExW(ClassGuid,
                                      KEY_QUERY_VALUE,
                                      DIOCR_INSTALLER,
@@ -705,7 +642,8 @@ SetupDiCreateDeviceInfoListExA(const GUID *ClassGuid,
     LPWSTR MachineNameW = NULL;
     HDEVINFO hDevInfo;
 
-    TRACE("%p %p %s %p\n", ClassGuid, hwndParent, MachineName, Reserved);
+    TRACE("%s %p %s %p\n", debugstr_guid(ClassGuid), hwndParent,
+      debugstr_a(MachineName), Reserved);
 
     if (MachineName)
     {
@@ -723,6 +661,22 @@ SetupDiCreateDeviceInfoListExA(const GUID *ClassGuid,
     return hDevInfo;
 }
 
+static DWORD
+GetErrorCodeFromCrCode(const IN CONFIGRET cr)
+{
+  switch (cr)
+  {
+    case CR_INVALID_MACHINENAME: return ERROR_INVALID_COMPUTERNAME;
+    case CR_OUT_OF_MEMORY: return ERROR_NOT_ENOUGH_MEMORY;
+    case CR_SUCCESS: return ERROR_SUCCESS;
+    default:
+      /* FIXME */
+      return ERROR_GEN_FAILURE;
+  }
+
+  /* Does not happen */
+}
+
 /***********************************************************************
  *             SetupDiCreateDeviceInfoListExW (SETUPAPI.@)
  */
@@ -733,15 +687,23 @@ SetupDiCreateDeviceInfoListExW(const GUID *ClassGuid,
                               PVOID Reserved)
 {
   struct DeviceInfoSet *list;
+  LPWSTR UNCServerName = NULL;
+  DWORD size;
   DWORD rc;
+  //CONFIGRET cr;
+  HDEVINFO ret = (HDEVINFO)INVALID_HANDLE_VALUE;;
 
-  TRACE("%p %p %S %p\n", ClassGuid, hwndParent, MachineName, Reserved);
+  TRACE("%s %p %s %p\n", debugstr_guid(ClassGuid), hwndParent,
+      debugstr_w(MachineName), Reserved);
 
-  list = HeapAlloc(GetProcessHeap(), 0, sizeof(struct DeviceInfoSet));
+  size = FIELD_OFFSET(struct DeviceInfoSet, szData);
+  if (MachineName)
+    size += (wcslen(MachineName) + 3) * sizeof(WCHAR);
+  list = HeapAlloc(GetProcessHeap(), 0, size);
   if (!list)
   {
     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-    return (HDEVINFO)INVALID_HANDLE_VALUE;
+    goto cleanup;
   }
   memset(list, 0, sizeof(struct DeviceInfoSet));
 
@@ -759,17 +721,57 @@ SetupDiCreateDeviceInfoListExW(const GUID *ClassGuid,
     if (rc != ERROR_SUCCESS)
     {
       SetLastError(rc);
-      HeapFree(GetProcessHeap(), 0, list);
-      return (HDEVINFO)INVALID_HANDLE_VALUE;
+      goto cleanup;
+    }
+    UNCServerName = HeapAlloc(GetProcessHeap(), 0, (strlenW(MachineName) + 3) * sizeof(WCHAR));
+    if (!UNCServerName)
+    {
+      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+      goto cleanup;
     }
+
+    strcpyW(UNCServerName + 2, MachineName);
+    list->szData[0] = list->szData[1] = '\\';
+    strcpyW(list->szData + 2, MachineName);
+    list->MachineName = list->szData;
   }
   else
   {
+    DWORD Size = MAX_PATH;
     list->HKLM = HKEY_LOCAL_MACHINE;
+    UNCServerName = HeapAlloc(GetProcessHeap(), 0, (MAX_PATH + 2) * sizeof(WCHAR));
+    if (!UNCServerName)
+    {
+      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+      goto cleanup;
+    }
+    if (!GetComputerNameW(UNCServerName + 2, &Size))
+      goto cleanup;
+    list->MachineName = NULL;
+  }
+#if 0
+  UNCServerName[0] = UNCServerName[1] = '\\';
+  cr = CM_Connect_MachineW(UNCServerName, &list->hMachine);
+  if (cr != CR_SUCCESS)
+  {
+    SetLastError(GetErrorCodeFromCrCode(cr));
+    goto cleanup;
   }
+#endif
   InitializeListHead(&list->DriverListHead);
   InitializeListHead(&list->ListHead);
-  return (HDEVINFO)list;
+
+  ret = (HDEVINFO)list;
+
+cleanup:
+  if (ret == INVALID_HANDLE_VALUE)
+  {
+    if (list && list->HKLM != 0 && list->HKLM != HKEY_LOCAL_MACHINE)
+      RegCloseKey(list->HKLM);
+    HeapFree(GetProcessHeap(), 0, list);
+  }
+  HeapFree(GetProcessHeap(), 0, UNCServerName);
+  return ret;
 }
 
 /***********************************************************************
@@ -788,7 +790,7 @@ BOOL WINAPI SetupDiEnumDeviceInfo(
     else if (DeviceInfoSet && DeviceInfoSet != (HDEVINFO)INVALID_HANDLE_VALUE)
     {
         struct DeviceInfoSet *list = (struct DeviceInfoSet *)DeviceInfoSet;
-        
+
         if (list->magic != SETUP_DEV_INFO_SET_MAGIC)
             SetLastError(ERROR_INVALID_HANDLE);
         else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
@@ -806,12 +808,7 @@ BOOL WINAPI SetupDiEnumDeviceInfo(
                 memcpy(&DeviceInfoData->ClassGuid,
                     &DevInfo->ClassGuid,
                     sizeof(GUID));
-                DeviceInfoData->DevInst = 0; /* FIXME */
-                /* Note: this appears to be dangerous, passing a private
-                 * pointer a heap-allocated datum to the caller.  However, the
-                 * expected lifetime of the device data is the same as the
-                 * HDEVINFO; once that is closed, the data are no longer valid.
-                 */
+                DeviceInfoData->DevInst = DevInfo->dnDevInst;
                 DeviceInfoData->Reserved = (ULONG_PTR)DevInfo;
                 ret = TRUE;
             }
@@ -892,6 +889,9 @@ BOOL WINAPI SetupDiGetActualSectionToInstallW(
     DWORD dwFullLength;
     LONG lLineCount = -1;
 
+    TRACE("%p %s %p %lu %p %p\n", InfHandle, debugstr_w(InfSectionName),
+        InfSectionWithExt, InfSectionWithExtSize, RequiredSize, Extension);
+
     lstrcpyW(szBuffer, InfSectionName);
     dwLength = lstrlenW(szBuffer);
 
@@ -1052,8 +1052,11 @@ BOOL WINAPI SetupDiGetClassDescriptionExW(
     HKEY hKey;
     DWORD dwLength;
 
+    TRACE("%s %p %lu %p %s %p\n", debugstr_guid(ClassGuid), ClassDescription,
+        ClassDescriptionSize, RequiredSize, debugstr_w(MachineName), Reserved);
+
     hKey = SetupDiOpenClassRegKeyExW(ClassGuid,
-                                     KEY_ALL_ACCESS,
+                                     KEY_QUERY_VALUE,
                                      DIOCR_INSTALLER,
                                      MachineName,
                                      Reserved);
@@ -1171,16 +1174,18 @@ end:
 
 static BOOL
 CreateDeviceInfoElement(
+    IN struct DeviceInfoSet *list,
     IN LPCWSTR InstancePath,
     IN LPCGUID pClassGuid,
     OUT struct DeviceInfoElement **pDeviceInfo)
 {
     DWORD size;
+    CONFIGRET cr;
     struct DeviceInfoElement *deviceInfo;
 
     *pDeviceInfo = NULL;
 
-    size = sizeof(struct DeviceInfoElement) + (wcslen(InstancePath) + 1) * sizeof(WCHAR);
+    size = FIELD_OFFSET(struct DeviceInfoElement, Data) + (wcslen(InstancePath) + 1) * sizeof(WCHAR);
     deviceInfo = HeapAlloc(GetProcessHeap(), 0, size);
     if (!deviceInfo)
     {
@@ -1188,6 +1193,14 @@ CreateDeviceInfoElement(
         return FALSE;
     }
     memset(deviceInfo, 0, size);
+
+    cr = CM_Locate_DevNode_ExW(&deviceInfo->dnDevInst, (DEVINSTID_W)InstancePath, CM_LOCATE_DEVNODE_PHANTOM, list->hMachine);
+    if (cr != CR_SUCCESS)
+    {
+        SetLastError(GetErrorCodeFromCrCode(cr));
+        return FALSE;
+    }
+
     deviceInfo->InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
     wcscpy(deviceInfo->Data, InstancePath);
     deviceInfo->DeviceName = deviceInfo->Data;
@@ -1213,7 +1226,8 @@ CreateDeviceInterface(
 
     *pDeviceInterface = NULL;
 
-    deviceInterface = HeapAlloc(GetProcessHeap(), 0, sizeof(struct DeviceInterface) + (wcslen(SymbolicLink) + 1) * sizeof(WCHAR));
+    deviceInterface = HeapAlloc(GetProcessHeap(), 0,
+        FIELD_OFFSET(struct DeviceInterface, SymbolicLink) + (wcslen(SymbolicLink) + 1) * sizeof(WCHAR));
     if (!deviceInterface)
     {
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -1242,7 +1256,7 @@ static LONG SETUP_CreateDevListFromEnumerator(
     DWORD i = 0, j;
     DWORD dwLength, dwRegType;
     DWORD rc;
-    
+
     /* Enumerate device IDs (subkeys of hEnumeratorKey) */
     while (TRUE)
     {
@@ -1300,6 +1314,8 @@ static LONG SETUP_CreateDevListFromEnumerator(
                 if (pClassGuid)
                     /* Skip this bad entry as we can't verify it */
                     continue;
+                /* Set a default GUID for this device */
+                memcpy(&KeyGuid, &GUID_NULL, sizeof(GUID));
             }
             else if (rc != ERROR_SUCCESS)
             {
@@ -1311,13 +1327,14 @@ static LONG SETUP_CreateDevListFromEnumerator(
                 RegCloseKey(hDeviceIdKey);
                 return ERROR_GEN_FAILURE;
             }
-
-            KeyBuffer[37] = '\0'; /* Replace the } by a NULL character */
-            if (UuidFromStringW(&KeyBuffer[1], &KeyGuid) != RPC_S_OK)
+            else
             {
-                RegCloseKey(hDeviceIdKey);
-                return GetLastError();
+                KeyBuffer[37] = '\0'; /* Replace the } by a NULL character */
+                if (UuidFromStringW(&KeyBuffer[1], &KeyGuid) != RPC_S_OK)
+                    /* Bad GUID, skip the entry */
+                    continue;
             }
+
             if (pClassGuid && !IsEqualIID(&KeyGuid, pClassGuid))
             {
                 /* Skip this entry as it is not the right device class */
@@ -1325,7 +1342,7 @@ static LONG SETUP_CreateDevListFromEnumerator(
             }
 
             /* Add the entry to the list */
-            if (!CreateDeviceInfoElement(InstancePath, &KeyGuid, &deviceInfo))
+            if (!CreateDeviceInfoElement(list, InstancePath, &KeyGuid, &deviceInfo))
             {
                 RegCloseKey(hDeviceIdKey);
                 return GetLastError();
@@ -1351,7 +1368,7 @@ static LONG SETUP_CreateDevList(
     DWORD dwLength;
     DWORD rc;
 
-    if (IsEqualIID(class, &GUID_NULL))
+    if (class && IsEqualIID(class, &GUID_NULL))
         class = NULL;
 
     /* Open Enum key */
@@ -1365,7 +1382,7 @@ static LONG SETUP_CreateDevList(
         HKLM = HKEY_LOCAL_MACHINE;
 
     rc = RegOpenKeyExW(HKLM,
-        EnumKeyName,
+        REGSTR_PATH_SYSTEMENUM,
         0,
         KEY_ENUMERATE_SUB_KEYS,
         &hEnumKey);
@@ -1374,7 +1391,7 @@ static LONG SETUP_CreateDevList(
         return rc;
 
     /* If enumerator is provided, call directly SETUP_CreateDevListFromEnumerator.
-     * Else, enumerate all enumerators all call SETUP_CreateDevListFromEnumerator
+     * Else, enumerate all enumerators and call SETUP_CreateDevListFromEnumerator
      * for each one.
      */
     if (Enumerator)
@@ -1484,7 +1501,7 @@ static LONG SETUP_CreateSerialDeviceList(
             struct DeviceInterface *interfaceInfo;
             TRACE("Adding %s to list\n", debugstr_w(ptr));
             /* Step 1. Create a device info element */
-            if (!CreateDeviceInfoElement(ptr, &GUID_SERENUM_BUS_ENUMERATOR, &deviceInfo))
+            if (!CreateDeviceInfoElement(list, ptr, &GUID_SERENUM_BUS_ENUMERATOR, &deviceInfo))
             {
                 if (devices != buf)
                     HeapFree(GetProcessHeap(), 0, devices);
@@ -1603,7 +1620,7 @@ static LONG SETUP_CreateInterfaceList(
         /* Find class GUID associated to the device instance */
         rc = RegOpenKeyExW(
             HKEY_LOCAL_MACHINE,
-            EnumKeyName,
+            REGSTR_PATH_SYSTEMENUM,
             0, /* Options */
             KEY_ENUMERATE_SUB_KEYS,
             &hEnumKey);
@@ -1656,7 +1673,7 @@ static LONG SETUP_CreateInterfaceList(
             RegCloseKey(hDeviceInstanceKey);
             continue;
         }
-        
+
         /* Enumerate subkeys of hDeviceInstanceKey (ie "#ReferenceString" in IoRegisterDeviceInterface). Skip entries that don't start with '#' */
         j = 0;
         while (TRUE)
@@ -1708,7 +1725,7 @@ static LONG SETUP_CreateInterfaceList(
 
             /* We have found a device */
             /* Step 1. Create a device info element */
-            if (!CreateDeviceInfoElement(InstancePath, &ClassGuid, &deviceInfo))
+            if (!CreateDeviceInfoElement(list, InstancePath, &ClassGuid, &deviceInfo))
             {
                 RegCloseKey(hReferenceKey);
                 RegCloseKey(hDeviceInstanceKey);
@@ -1866,140 +1883,447 @@ HDEVINFO WINAPI SetupDiGetClassDevsExW(
 }
 
 /***********************************************************************
- *             SetupDiEnumDeviceInterfaces (SETUPAPI.@)
+ *             SetupDiGetClassImageIndex (SETUPAPI.@)
  */
-BOOL WINAPI SetupDiEnumDeviceInterfaces(
-       HDEVINFO DeviceInfoSet,
-       PSP_DEVINFO_DATA DeviceInfoData,
-       CONST GUID * InterfaceClassGuid,
-       DWORD MemberIndex,
-       PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
+
+static BOOL GetIconIndex(
+       IN HKEY hClassKey,
+       OUT PINT ImageIndex)
 {
+    LPWSTR Buffer = NULL;
+    DWORD dwRegType, dwLength;
+    LONG rc;
     BOOL ret = FALSE;
 
-    TRACE("%p, %p, %s, %ld, %p\n", DeviceInfoSet, DeviceInfoData,
-     debugstr_guid(InterfaceClassGuid), MemberIndex, DeviceInterfaceData);
+    /* Read icon registry key */
+    rc = RegQueryValueExW(hClassKey, REGSTR_VAL_INSICON, NULL, &dwRegType, NULL, &dwLength);
+    if (rc != ERROR_SUCCESS)
+    {
+        SetLastError(rc);
+        goto cleanup;
+    } else if (dwRegType != REG_SZ)
+    {
+        SetLastError(ERROR_INVALID_INDEX);
+        goto cleanup;
+    }
+    Buffer = MyMalloc(dwLength + sizeof(WCHAR));
+    if (!Buffer)
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        goto cleanup;
+    }
+    rc = RegQueryValueExW(hClassKey, REGSTR_VAL_INSICON, NULL, NULL, (LPBYTE)Buffer, &dwLength);
+    if (rc != ERROR_SUCCESS)
+    {
+        SetLastError(rc);
+        goto cleanup;
+    }
+    /* make sure the returned buffer is NULL-terminated */
+    Buffer[dwLength / sizeof(WCHAR)] = 0;
 
-    if (!DeviceInterfaceData)
+    /* Transform icon value to a INT */
+    *ImageIndex = atoiW(Buffer);
+    ret = TRUE;
+
+cleanup:
+    MyFree(Buffer);
+    return ret;
+}
+
+BOOL WINAPI SetupDiGetClassImageIndex(
+       IN PSP_CLASSIMAGELIST_DATA ClassImageListData,
+       IN CONST GUID *ClassGuid,
+       OUT PINT ImageIndex)
+{
+    struct ClassImageList *list;
+    BOOL ret = FALSE;
+
+    TRACE("%p %s %p\n", ClassImageListData, debugstr_guid(ClassGuid), ImageIndex);
+
+    if (!ClassImageListData || !ClassGuid || !ImageIndex)
         SetLastError(ERROR_INVALID_PARAMETER);
-    else if (DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA))
+    else if (ClassImageListData->cbSize != sizeof(SP_CLASSIMAGELIST_DATA))
         SetLastError(ERROR_INVALID_USER_BUFFER);
-    else if (DeviceInfoSet && DeviceInfoSet != (HDEVINFO)INVALID_HANDLE_VALUE)
+    else if ((list = (struct ClassImageList *)ClassImageListData->Reserved) == NULL)
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (list->magic != SETUP_CLASS_IMAGE_LIST_MAGIC)
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (!ImageIndex)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
     {
-        struct DeviceInfoSet *list = (struct DeviceInfoSet *)DeviceInfoSet;
+        HKEY hKey = INVALID_HANDLE_VALUE;
+        INT iconIndex;
 
-        if (list->magic == SETUP_DEV_INFO_SET_MAGIC)
+        /* Read Icon registry entry into Buffer */
+        hKey = SetupDiOpenClassRegKeyExW(ClassGuid, KEY_QUERY_VALUE, DIOCR_INTERFACE, list->MachineName, NULL);
+        if (hKey == INVALID_HANDLE_VALUE)
+            goto cleanup;
+        if (!GetIconIndex(hKey, &iconIndex))
+            goto cleanup;
+
+        if (iconIndex >= 0)
         {
-            PLIST_ENTRY ItemList = list->ListHead.Flink;
-            BOOL Found = FALSE;
-            while (ItemList != &list->ListHead && !Found)
-            {
-                PLIST_ENTRY InterfaceListEntry;
-                struct DeviceInfoElement *DevInfo = (struct DeviceInfoElement *)ItemList;
-                if (DeviceInfoData && (struct DeviceInfoElement *)DeviceInfoData->Reserved != DevInfo)
-                {
-                    /* We are not searching for this element */
-                    ItemList = ItemList->Flink;
-                    continue;
-                }
-                InterfaceListEntry = DevInfo->InterfaceListHead.Flink;
-                while (InterfaceListEntry != &DevInfo->InterfaceListHead && !Found)
-                {
-                    struct DeviceInterface *DevItf = (struct DeviceInterface *)InterfaceListEntry;
-                    if (!IsEqualIID(&DevItf->InterfaceClassGuid, InterfaceClassGuid))
-                    {
-                        InterfaceListEntry = InterfaceListEntry->Flink;
-                        continue;
-                    }
-                    if (MemberIndex-- == 0)
-                    {
-                        /* return this item */
-                        memcpy(&DeviceInterfaceData->InterfaceClassGuid,
-                            &DevItf->InterfaceClassGuid,
-                            sizeof(GUID));
-                        DeviceInterfaceData->Flags = 0; /* FIXME */
-                        /* Note: this appears to be dangerous, passing a private
-                         * pointer a heap-allocated datum to the caller.  However, the
-                         * expected lifetime of the device data is the same as the
-                         * HDEVINFO; once that is closed, the data are no longer valid.
-                         */
-                        DeviceInterfaceData->Reserved = (ULONG_PTR)DevItf;
-                        Found = TRUE;
-                    }
-                    InterfaceListEntry = InterfaceListEntry->Flink;
-                }
-                ItemList = ItemList->Flink;
-            }
-            if (!Found)
-                SetLastError(ERROR_NO_MORE_ITEMS);
-            else
-                ret = TRUE;
+            SetLastError(ERROR_INVALID_INDEX);
+            goto cleanup;
         }
-        else
-            SetLastError(ERROR_INVALID_HANDLE);
-    }
-    else
-        SetLastError(ERROR_INVALID_HANDLE);
-    return ret;
-}
 
-static VOID ReferenceInfFile(struct InfFileDetails* infFile)
-{
-    InterlockedIncrement(&infFile->References);
-}
+        *ImageIndex = -iconIndex;
+        ret = TRUE;
 
-static VOID DereferenceInfFile(struct InfFileDetails* infFile)
-{
-    if (InterlockedDecrement(&infFile->References) == 0)
-    {
-        SetupCloseInfFile(infFile->hInf);
-        HeapFree(GetProcessHeap(), 0, infFile);
+cleanup:
+        if (hKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hKey);
     }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
 }
 
-static BOOL DestroyDriverInfoElement(struct DriverInfoElement* driverInfo)
+/***********************************************************************
+ *             SetupDiGetClassImageList(SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetClassImageList(
+       OUT PSP_CLASSIMAGELIST_DATA ClassImageListData)
 {
-    DereferenceInfFile(driverInfo->InfFileDetails);
-    HeapFree(GetProcessHeap(), 0, driverInfo->MatchingId);
-    HeapFree(GetProcessHeap(), 0, driverInfo);
-    return TRUE;
+    return SetupDiGetClassImageListExW(ClassImageListData, NULL, NULL);
 }
 
-static BOOL DestroyDeviceInfoElement(struct DeviceInfoElement* deviceInfo)
+/***********************************************************************
+ *             SetupDiGetClassImageListExA(SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetClassImageListExA(
+       OUT PSP_CLASSIMAGELIST_DATA ClassImageListData,
+       IN PCSTR MachineName OPTIONAL,
+       IN PVOID Reserved)
 {
-    PLIST_ENTRY ListEntry;
-    struct DriverInfoElement *driverInfo;
+    PWSTR MachineNameW = NULL;
+    BOOL ret;
 
-    while (!IsListEmpty(&deviceInfo->DriverListHead))
+    if (MachineName)
     {
-        ListEntry = RemoveHeadList(&deviceInfo->DriverListHead);
-        driverInfo = (struct DriverInfoElement *)ListEntry;
-        if (!DestroyDriverInfoElement(driverInfo))
+        MachineNameW = MultiByteToUnicode(MachineName, CP_ACP);
+        if (MachineNameW == NULL)
             return FALSE;
     }
-    while (!IsListEmpty(&deviceInfo->InterfaceListHead))
-    {
-        ListEntry = RemoveHeadList(&deviceInfo->InterfaceListHead);
-        HeapFree(GetProcessHeap(), 0, ListEntry);
-    }
-    HeapFree(GetProcessHeap(), 0, deviceInfo);
-    return TRUE;
+
+    ret = SetupDiGetClassImageListExW(ClassImageListData, MachineNameW, Reserved);
+
+    if (MachineNameW)
+        MyFree(MachineNameW);
+
+    return ret;
 }
 
-static BOOL DestroyDeviceInfoSet(struct DeviceInfoSet* list)
+/***********************************************************************
+ *             SetupDiGetClassImageListExW(SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetClassImageListExW(
+       OUT PSP_CLASSIMAGELIST_DATA ClassImageListData,
+       IN PCWSTR MachineName OPTIONAL,
+       IN PVOID Reserved)
 {
-    PLIST_ENTRY ListEntry;
-    struct DeviceInfoElement *deviceInfo;
+    BOOL ret = FALSE;
 
-    while (!IsListEmpty(&list->ListHead))
-    {
-        ListEntry = RemoveHeadList(&list->ListHead);
-        deviceInfo = (struct DeviceInfoElement *)ListEntry;
-        if (!DestroyDeviceInfoElement(deviceInfo))
+    TRACE("%p %p %p\n", ClassImageListData, debugstr_w(MachineName), Reserved);
+
+    if (!ClassImageListData)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (ClassImageListData->cbSize != sizeof(SP_CLASSIMAGELIST_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (Reserved)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
+    {
+        struct ClassImageList *list = NULL;
+        DWORD size;
+
+        size = FIELD_OFFSET(struct ClassImageList, szData);
+        if (MachineName)
+            size += (wcslen(MachineName) + 3) * sizeof(WCHAR);
+        list = HeapAlloc(GetProcessHeap(), 0, size);
+        if (!list)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            goto cleanup;
+        }
+        list->magic = SETUP_CLASS_IMAGE_LIST_MAGIC;
+        if (MachineName)
+        {
+            list->szData[0] = list->szData[1] = '\\';
+            strcpyW(list->szData + 2, MachineName);
+            list->MachineName = list->szData;
+        }
+        else
+        {
+            list->MachineName = NULL;
+        }
+
+        ClassImageListData->Reserved = (DWORD)list; /* FIXME: 64 bit portability issue */
+        ret = TRUE;
+
+cleanup:
+        if (!ret)
+            MyFree(list);
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiLoadClassIcon(SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiLoadClassIcon(
+       IN CONST GUID *ClassGuid,
+       OUT HICON *LargeIcon OPTIONAL,
+       OUT PINT MiniIconIndex OPTIONAL)
+{
+    BOOL ret = FALSE;
+
+    if (!ClassGuid)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
+    {
+        LPWSTR Buffer = NULL;
+        LPCWSTR DllName;
+        INT iconIndex;
+        HKEY hKey = INVALID_HANDLE_VALUE;
+
+        hKey = SetupDiOpenClassRegKey(ClassGuid, KEY_QUERY_VALUE);
+        if (hKey == INVALID_HANDLE_VALUE)
+            goto cleanup;
+
+        if (!GetIconIndex(hKey, &iconIndex))
+            goto cleanup;
+
+        if (iconIndex > 0)
+        {
+            /* Look up icon in dll specified by Installer32 or EnumPropPages32 key */
+            PWCHAR Comma;
+            LONG rc;
+            DWORD dwRegType, dwLength;
+            rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, &dwRegType, NULL, &dwLength);
+            if (rc == ERROR_SUCCESS && dwRegType == REG_SZ)
+            {
+                Buffer = MyMalloc(dwLength + sizeof(WCHAR));
+                if (Buffer == NULL)
+                {
+                    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                    goto cleanup;
+                }
+                rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, NULL, (LPBYTE)Buffer, &dwLength);
+                if (rc != ERROR_SUCCESS)
+                {
+                    SetLastError(rc);
+                    goto cleanup;
+                }
+                /* make sure the returned buffer is NULL-terminated */
+                Buffer[dwLength / sizeof(WCHAR)] = 0;
+            }
+            else if
+                (ERROR_SUCCESS == (rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, &dwRegType, NULL, &dwLength))
+                && dwRegType == REG_SZ)
+            {
+                Buffer = MyMalloc(dwLength + sizeof(WCHAR));
+                if (Buffer == NULL)
+                {
+                    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                    goto cleanup;
+                }
+                rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, NULL, (LPBYTE)Buffer, &dwLength);
+                if (rc != ERROR_SUCCESS)
+                {
+                    SetLastError(rc);
+                    goto cleanup;
+                }
+                /* make sure the returned buffer is NULL-terminated */
+                Buffer[dwLength / sizeof(WCHAR)] = 0;
+            }
+            else
+            {
+                /* Unable to find where to load the icon */
+                SetLastError(ERROR_FILE_NOT_FOUND);
+                goto cleanup;
+            }
+            Comma = strchrW(Buffer, ',');
+            if (!Comma)
+            {
+                SetLastError(ERROR_GEN_FAILURE);
+                goto cleanup;
+            }
+            *Comma = '\0';
+            DllName = Buffer;
+        }
+        else
+        {
+            /* Look up icon in setupapi.dll */
+            DllName = L"setupapi.dll";
+            iconIndex = -iconIndex;
+        }
+
+        TRACE("Icon index %d, dll name %s\n", iconIndex, debugstr_w(DllName));
+        if (LargeIcon)
+        {
+            if (1 != ExtractIconEx(DllName, iconIndex, LargeIcon, NULL, 1))
+            {
+                SetLastError(ERROR_INVALID_INDEX);
+                goto cleanup;
+            }
+        }
+        if (MiniIconIndex)
+            *MiniIconIndex = iconIndex;
+        ret = TRUE;
+
+cleanup:
+        if (hKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hKey);
+        MyFree(Buffer);
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiEnumDeviceInterfaces (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiEnumDeviceInterfaces(
+       HDEVINFO DeviceInfoSet,
+       PSP_DEVINFO_DATA DeviceInfoData,
+       CONST GUID * InterfaceClassGuid,
+       DWORD MemberIndex,
+       PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%p, %p, %s, %ld, %p\n", DeviceInfoSet, DeviceInfoData,
+     debugstr_guid(InterfaceClassGuid), MemberIndex, DeviceInterfaceData);
+
+    if (!DeviceInterfaceData)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (DeviceInfoSet && DeviceInfoSet != (HDEVINFO)INVALID_HANDLE_VALUE)
+    {
+        struct DeviceInfoSet *list = (struct DeviceInfoSet *)DeviceInfoSet;
+
+        if (list->magic == SETUP_DEV_INFO_SET_MAGIC)
+        {
+            PLIST_ENTRY ItemList = list->ListHead.Flink;
+            BOOL Found = FALSE;
+            while (ItemList != &list->ListHead && !Found)
+            {
+                PLIST_ENTRY InterfaceListEntry;
+                struct DeviceInfoElement *DevInfo = (struct DeviceInfoElement *)ItemList;
+                if (DeviceInfoData && (struct DeviceInfoElement *)DeviceInfoData->Reserved != DevInfo)
+                {
+                    /* We are not searching for this element */
+                    ItemList = ItemList->Flink;
+                    continue;
+                }
+                InterfaceListEntry = DevInfo->InterfaceListHead.Flink;
+                while (InterfaceListEntry != &DevInfo->InterfaceListHead && !Found)
+                {
+                    struct DeviceInterface *DevItf = (struct DeviceInterface *)InterfaceListEntry;
+                    if (!IsEqualIID(&DevItf->InterfaceClassGuid, InterfaceClassGuid))
+                    {
+                        InterfaceListEntry = InterfaceListEntry->Flink;
+                        continue;
+                    }
+                    if (MemberIndex-- == 0)
+                    {
+                        /* return this item */
+                        memcpy(&DeviceInterfaceData->InterfaceClassGuid,
+                            &DevItf->InterfaceClassGuid,
+                            sizeof(GUID));
+                        DeviceInterfaceData->Flags = 0; /* FIXME */
+                        DeviceInterfaceData->Reserved = (ULONG_PTR)DevItf;
+                        Found = TRUE;
+                    }
+                    InterfaceListEntry = InterfaceListEntry->Flink;
+                }
+                ItemList = ItemList->Flink;
+            }
+            if (!Found)
+                SetLastError(ERROR_NO_MORE_ITEMS);
+            else
+                ret = TRUE;
+        }
+        else
+            SetLastError(ERROR_INVALID_HANDLE);
+    }
+    else
+        SetLastError(ERROR_INVALID_HANDLE);
+    return ret;
+}
+
+static VOID ReferenceInfFile(struct InfFileDetails* infFile)
+{
+    InterlockedIncrement(&infFile->References);
+}
+
+static VOID DereferenceInfFile(struct InfFileDetails* infFile)
+{
+    if (InterlockedDecrement(&infFile->References) == 0)
+    {
+        SetupCloseInfFile(infFile->hInf);
+        HeapFree(GetProcessHeap(), 0, infFile);
+    }
+}
+
+static BOOL DestroyDriverInfoElement(struct DriverInfoElement* driverInfo)
+{
+    DereferenceInfFile(driverInfo->InfFileDetails);
+    HeapFree(GetProcessHeap(), 0, driverInfo->MatchingId);
+    HeapFree(GetProcessHeap(), 0, driverInfo);
+    return TRUE;
+}
+
+static BOOL DestroyClassInstallParams(struct ClassInstallParams* installParams)
+{
+    HeapFree(GetProcessHeap(), 0, installParams->PropChange);
+    return TRUE;
+}
+
+static BOOL DestroyDeviceInfoElement(struct DeviceInfoElement* deviceInfo)
+{
+    PLIST_ENTRY ListEntry;
+    struct DriverInfoElement *driverInfo;
+
+    while (!IsListEmpty(&deviceInfo->DriverListHead))
+    {
+        ListEntry = RemoveHeadList(&deviceInfo->DriverListHead);
+        driverInfo = (struct DriverInfoElement *)ListEntry;
+        if (!DestroyDriverInfoElement(driverInfo))
+            return FALSE;
+    }
+    while (!IsListEmpty(&deviceInfo->InterfaceListHead))
+    {
+        ListEntry = RemoveHeadList(&deviceInfo->InterfaceListHead);
+        HeapFree(GetProcessHeap(), 0, ListEntry);
+    }
+    DestroyClassInstallParams(&deviceInfo->ClassInstallParams);
+    HeapFree(GetProcessHeap(), 0, deviceInfo);
+    return TRUE;
+}
+
+static BOOL DestroyDeviceInfoSet(struct DeviceInfoSet* list)
+{
+    PLIST_ENTRY ListEntry;
+    struct DeviceInfoElement *deviceInfo;
+
+    while (!IsListEmpty(&list->ListHead))
+    {
+        ListEntry = RemoveHeadList(&list->ListHead);
+        deviceInfo = (struct DeviceInfoElement *)ListEntry;
+        if (!DestroyDeviceInfoElement(deviceInfo))
             return FALSE;
     }
     if (list->HKLM != HKEY_LOCAL_MACHINE)
         RegCloseKey(list->HKLM);
+    CM_Disconnect_Machine(list->hMachine);
+    DestroyClassInstallParams(&list->ClassInstallParams);
     HeapFree(GetProcessHeap(), 0, list);
     return TRUE;
 }
@@ -2043,7 +2367,7 @@ BOOL WINAPI SetupDiGetDeviceInterfaceDetailA(
     DWORD sizeW = 0, sizeA;
     BOOL ret = FALSE;
 
-    TRACE("(%p, %p, %p, %ld, %p, %p)\n", DeviceInfoSet,
+    TRACE("%p %p %p %lu %p %p\n", DeviceInfoSet,
         DeviceInterfaceData, DeviceInterfaceDetailData,
         DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData);
 
@@ -2111,7 +2435,7 @@ BOOL WINAPI SetupDiGetDeviceInterfaceDetailW(
 {
     BOOL ret = FALSE;
 
-    TRACE("(%p, %p, %p, %ld, %p, %p): stub\n", DeviceInfoSet,
+    TRACE("%p %p %p %lu %p %p\n", DeviceInfoSet,
         DeviceInterfaceData, DeviceInterfaceDetailData,
         DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData);
 
@@ -2153,12 +2477,7 @@ BOOL WINAPI SetupDiGetDeviceInterfaceDetailW(
                 memcpy(&DeviceInfoData->ClassGuid,
                     &deviceInterface->DeviceInfo->ClassGuid,
                     sizeof(GUID));
-                DeviceInfoData->DevInst = 0; /* FIXME */
-                /* Note: this appears to be dangerous, passing a private
-                 * pointer a heap-allocated datum to the caller.  However, the
-                 * expected lifetime of the device data is the same as the
-                 * HDEVINFO; once that is closed, the data are no longer valid.
-                 */
+                DeviceInfoData->DevInst = deviceInterface->DeviceInfo->dnDevInst;
                 DeviceInfoData->Reserved = (ULONG_PTR)deviceInterface->DeviceInfo;
             }
             ret = TRUE;
@@ -2304,43 +2623,43 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
             {
                 LPCWSTR RegistryPropertyName;
                 DWORD BufferSize;
-                
+
                 switch (Property)
                 {
                     case SPDRP_CAPABILITIES:
-                        RegistryPropertyName = L"Capabilities"; break;
+                        RegistryPropertyName = REGSTR_VAL_CAPABILITIES; break;
                     case SPDRP_CLASS:
-                        RegistryPropertyName = L"Class"; break;
+                        RegistryPropertyName = REGSTR_VAL_CLASS; break;
                     case SPDRP_CLASSGUID:
-                        RegistryPropertyName = L"ClassGUID"; break;
+                        RegistryPropertyName = REGSTR_VAL_CLASSGUID; break;
                     case SPDRP_COMPATIBLEIDS:
-                        RegistryPropertyName = L"CompatibleIDs"; break;
+                        RegistryPropertyName = REGSTR_VAL_COMPATIBLEIDS; break;
                     case SPDRP_CONFIGFLAGS:
-                        RegistryPropertyName = L"ConfigFlags"; break;
+                        RegistryPropertyName = REGSTR_VAL_CONFIGFLAGS; break;
                     case SPDRP_DEVICEDESC:
-                        RegistryPropertyName = L"DeviceDesc"; break;
+                        RegistryPropertyName = REGSTR_VAL_DEVDESC; break;
                     case SPDRP_DRIVER:
-                        RegistryPropertyName = L"Driver"; break;
+                        RegistryPropertyName = REGSTR_VAL_DRIVER; break;
                     case SPDRP_FRIENDLYNAME:
-                        RegistryPropertyName = L"FriendlyName"; break;
+                        RegistryPropertyName = REGSTR_VAL_FRIENDLYNAME; break;
                     case SPDRP_HARDWAREID:
-                        RegistryPropertyName = L"HardwareID"; break;
+                        RegistryPropertyName = REGSTR_VAL_HARDWAREID; break;
                     case SPDRP_LOCATION_INFORMATION:
-                        RegistryPropertyName = L"LocationInformation"; break;
+                        RegistryPropertyName = REGSTR_VAL_LOCATION_INFORMATION; break;
                     case SPDRP_LOWERFILTERS:
-                        RegistryPropertyName = L"LowerFilters"; break;
+                        RegistryPropertyName = REGSTR_VAL_LOWERFILTERS; break;
                     case SPDRP_MFG:
-                        RegistryPropertyName = L"Mfg"; break;
+                        RegistryPropertyName = REGSTR_VAL_MFG; break;
                     case SPDRP_SECURITY:
                         RegistryPropertyName = L"Security"; break;
                     case SPDRP_SERVICE:
-                        RegistryPropertyName = L"Service"; break;
+                        RegistryPropertyName = REGSTR_VAL_SERVICE; break;
                     case SPDRP_UI_NUMBER:
-                        RegistryPropertyName = L"UINumber"; break;
+                        RegistryPropertyName = REGSTR_VAL_UI_NUMBER; break;
                     case SPDRP_UI_NUMBER_DESC_FORMAT:
                         RegistryPropertyName = L"UINumberDescFormat"; break;
                     case SPDRP_UPPERFILTERS:
-                        RegistryPropertyName = L"UpperFilters"; break;
+                        RegistryPropertyName = REGSTR_VAL_UPPERFILTERS; break;
                     default:
                         /* Should not happen */
                         RegistryPropertyName = NULL; break;
@@ -2349,7 +2668,7 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
                 /* Open registry key name */
                 rc = RegOpenKeyExW(
                     list->HKLM,
-                    EnumKeyName,
+                    REGSTR_PATH_SYSTEMENUM,
                     0, /* Options */
                     KEY_ENUMERATE_SUB_KEYS,
                     &hEnumKey);
@@ -2435,7 +2754,7 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
 
             default:
             {
-                FIXME("Property 0x%lx not implemented\n", Property);
+                ERR("Property 0x%lx not implemented\n", Property);
                 SetLastError(ERROR_NOT_SUPPORTED);
             }
         }
@@ -2481,7 +2800,7 @@ BOOL WINAPI SetupDiSetDeviceRegistryPropertyW(
         SetLastError(ERROR_INVALID_HANDLE);
     else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
         SetLastError(ERROR_INVALID_HANDLE);
-    else if (DeviceInfoData)
+    else if (!DeviceInfoData)
         SetLastError(ERROR_INVALID_HANDLE);
     else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
         SetLastError(ERROR_INVALID_USER_BUFFER);
@@ -2508,27 +2827,27 @@ BOOL WINAPI SetupDiSetDeviceRegistryPropertyW(
                 switch (Property)
                 {
                     case SPDRP_COMPATIBLEIDS:
-                        RegistryPropertyName = L"CompatibleIDs";
+                        RegistryPropertyName = REGSTR_VAL_COMPATIBLEIDS;
                         RegistryDataType = REG_MULTI_SZ;
                         break;
                     case SPDRP_CONFIGFLAGS:
-                        RegistryPropertyName = L"ConfigFlags";
+                        RegistryPropertyName = REGSTR_VAL_CONFIGFLAGS;
                         RegistryDataType = REG_DWORD;
                         break;
                     case SPDRP_FRIENDLYNAME:
-                        RegistryPropertyName = L"FriendlyName";
+                        RegistryPropertyName = REGSTR_VAL_FRIENDLYNAME;
                         RegistryDataType = REG_SZ;
                         break;
                     case SPDRP_HARDWAREID:
-                        RegistryPropertyName = L"HardwareID";
+                        RegistryPropertyName = REGSTR_VAL_HARDWAREID;
                         RegistryDataType = REG_MULTI_SZ;
                         break;
                     case SPDRP_LOCATION_INFORMATION:
-                        RegistryPropertyName = L"LocationInformation";
+                        RegistryPropertyName = REGSTR_VAL_LOCATION_INFORMATION;
                         RegistryDataType = REG_SZ;
                         break;
                     case SPDRP_LOWERFILTERS:
-                        RegistryPropertyName = L"LowerFilters";
+                        RegistryPropertyName = REGSTR_VAL_LOWERFILTERS;
                         RegistryDataType = REG_MULTI_SZ;
                         break;
                     case SPDRP_SECURITY:
@@ -2536,7 +2855,7 @@ BOOL WINAPI SetupDiSetDeviceRegistryPropertyW(
                         RegistryDataType = REG_BINARY;
                         break;
                     case SPDRP_SERVICE:
-                        RegistryPropertyName = L"Service";
+                        RegistryPropertyName = REGSTR_VAL_SERVICE;
                         RegistryDataType = REG_SZ;
                         break;
                     case SPDRP_UI_NUMBER_DESC_FORMAT:
@@ -2544,7 +2863,7 @@ BOOL WINAPI SetupDiSetDeviceRegistryPropertyW(
                         RegistryDataType = REG_SZ;
                         break;
                     case SPDRP_UPPERFILTERS:
-                        RegistryPropertyName = L"UpperFilters";
+                        RegistryPropertyName = REGSTR_VAL_UPPERFILTERS;
                         RegistryDataType = REG_MULTI_SZ;
                         break;
                     default:
@@ -2584,7 +2903,7 @@ BOOL WINAPI SetupDiSetDeviceRegistryPropertyW(
 
             default:
             {
-                FIXME("Property 0x%lx not implemented\n", Property);
+                ERR("Property 0x%lx not implemented\n", Property);
                 SetLastError(ERROR_NOT_SUPPORTED);
             }
         }
@@ -2638,7 +2957,7 @@ static HKEY CreateClassKey(HINF hInf)
         return INVALID_HANDLE_VALUE;
     }
 
-    lstrcpyW(FullBuffer, ControlClass);
+    lstrcpyW(FullBuffer, REGSTR_PATH_CLASS_NT);
     lstrcatW(FullBuffer, Buffer);
 
 
@@ -2659,7 +2978,7 @@ static HKEY CreateClassKey(HINF hInf)
                            0,
                            NULL,
                            REG_OPTION_NON_VOLATILE,
-                           KEY_ALL_ACCESS,
+                           KEY_SET_VALUE,
                            NULL,
                            &hClassKey,
              NULL))
@@ -2698,6 +3017,9 @@ BOOL WINAPI SetupDiInstallClassW(
     BOOL bFileQueueCreated = FALSE;
     HKEY hClassKey;
 
+    TRACE("%p %s 0x%lx %p\n", hwndParent, debugstr_w(InfFileName),
+        Flags, FileQueue);
+
     FIXME("not fully implemented\n");
 
     if ((Flags & DI_NOVCP) && (FileQueue == NULL || FileQueue == INVALID_HANDLE_VALUE))
@@ -2768,7 +3090,7 @@ BOOL WINAPI SetupDiInstallClassW(
                                INVALID_HANDLE_VALUE,
                                NULL);
 
-    /* FIXME: More code! */
+    /* FIXME: Process InterfaceInstall32 section */
 
     if (bFileQueueCreated)
        SetupCloseFileQueue(FileQueue);
@@ -2796,10 +3118,10 @@ HKEY WINAPI SetupDiOpenClassRegKey(
  *             SetupDiOpenClassRegKeyExA  (SETUPAPI.@)
  */
 HKEY WINAPI SetupDiOpenClassRegKeyExA(
-        const GUID* ClassGuid,
+        const GUID* ClassGuid OPTIONAL,
         REGSAM samDesired,
         DWORD Flags,
-        PCSTR MachineName,
+        PCSTR MachineName OPTIONAL,
         PVOID Reserved)
 {
     PWSTR MachineNameW = NULL;
@@ -2828,10 +3150,10 @@ HKEY WINAPI SetupDiOpenClassRegKeyExA(
  *             SetupDiOpenClassRegKeyExW  (SETUPAPI.@)
  */
 HKEY WINAPI SetupDiOpenClassRegKeyExW(
-        const GUID* ClassGuid,
+        const GUID* ClassGuid OPTIONAL,
         REGSAM samDesired,
         DWORD Flags,
-        PCWSTR MachineName,
+        PCWSTR MachineName OPTIONAL,
         PVOID Reserved)
 {
     LPWSTR lpGuidString;
@@ -2843,18 +3165,21 @@ HKEY WINAPI SetupDiOpenClassRegKeyExW(
     DWORD rc;
     LPCWSTR lpKeyName;
 
+    TRACE("%s 0x%lx 0x%lx %s %p\n", debugstr_guid(ClassGuid), samDesired,
+        Flags, debugstr_w(MachineName), Reserved);
+
     if (Flags == DIOCR_INSTALLER)
     {
-        lpKeyName = ControlClass;
+        lpKeyName = REGSTR_PATH_CLASS_NT;
     }
     else if (Flags == DIOCR_INTERFACE)
     {
-        lpKeyName = DeviceClasses;
+        lpKeyName = REGSTR_PATH_DEVICE_CLASSES;
     }
     else
     {
         ERR("Invalid Flags parameter!\n");
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(ERROR_INVALID_FLAGS);
         return INVALID_HANDLE_VALUE;
     }
 
@@ -2873,7 +3198,7 @@ HKEY WINAPI SetupDiOpenClassRegKeyExW(
     rc = RegOpenKeyExW(HKLM,
                      lpKeyName,
                      0,
-                     KEY_ALL_ACCESS,
+                     ClassGuid ? KEY_ENUMERATE_SUB_KEYS : samDesired,
                      &hClassesKey);
     if (MachineName != NULL) RegCloseKey(HKLM);
     if (rc != ERROR_SUCCESS)
@@ -2909,7 +3234,7 @@ HKEY WINAPI SetupDiOpenClassRegKeyExW(
     rc = RegOpenKeyExW(hClassesKey,
                      lpFullGuidString,
                      0,
-                     KEY_ALL_ACCESS,
+                     samDesired,
                      &hClassKey);
     if (rc != ERROR_SUCCESS)
     {
@@ -2979,25 +3304,156 @@ BOOL WINAPI SetupDiSetClassInstallParamsA(
     return FALSE;
 }
 
-static DWORD
-GetFunctionPointer(
-        IN PWSTR InstallerName,
-        OUT HMODULE* ModulePointer,
-        OUT PVOID* FunctionPointer)
+/***********************************************************************
+ *             SetupDiSetClassInstallParamsW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiSetClassInstallParamsW(
+       IN HDEVINFO DeviceInfoSet,
+       IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+       IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
+       IN DWORD ClassInstallParamsSize)
 {
-    HMODULE hModule = NULL;
-    LPSTR FunctionNameA = NULL;
-    PWCHAR Comma;
-    DWORD rc;
+    struct DeviceInfoSet *list;
+    BOOL ret = FALSE;
 
-    *ModulePointer = NULL;
-    *FunctionPointer = NULL;
+    TRACE("%p %p %p %lu\n", DeviceInfoSet, DeviceInfoData,
+        ClassInstallParams, ClassInstallParamsSize);
 
-    Comma = strchrW(InstallerName, ',');
-    if (!Comma)
-    {
-        rc = ERROR_INVALID_PARAMETER;
-        goto cleanup;
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (ClassInstallParams && ClassInstallParams->cbSize != sizeof(SP_CLASSINSTALL_HEADER))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (ClassInstallParams && ClassInstallParamsSize < sizeof(SP_CLASSINSTALL_HEADER))
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (!ClassInstallParams && ClassInstallParamsSize != 0)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
+    {
+        SP_DEVINSTALL_PARAMS_W InstallParams;
+        BOOL Result;
+
+        InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+        Result = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+        if (!Result)
+            goto done;
+
+        if (ClassInstallParams)
+        {
+            /* Check parameters in ClassInstallParams */
+            if (ClassInstallParams->InstallFunction < DIF_SELECTDEVICE
+                || ClassInstallParams->InstallFunction - DIF_SELECTDEVICE >= sizeof(UpdateClassInstallParamHandlers)/sizeof(UpdateClassInstallParamHandlers[0]))
+            {
+                SetLastError(ERROR_INVALID_USER_BUFFER);
+                goto done;
+            }
+            else if (UpdateClassInstallParamHandlers[ClassInstallParams->InstallFunction - DIF_SELECTDEVICE] == NULL)
+            {
+                FIXME("InstallFunction %u is valid, but has no associated update handler\n", ClassInstallParams->InstallFunction);
+                SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+                goto done;
+            }
+            ret = UpdateClassInstallParamHandlers[ClassInstallParams->InstallFunction - DIF_SELECTDEVICE](DeviceInfoSet, DeviceInfoData, ClassInstallParams, ClassInstallParamsSize);
+            if (!ret)
+                goto done;
+            InstallParams.Flags |= DI_CLASSINSTALLPARAMS;
+        }
+        else
+        {
+            InstallParams.Flags &= ~DI_CLASSINSTALLPARAMS;
+        }
+
+        ret = SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+    }
+
+done:
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+static BOOL PropertyChangeHandler(
+       IN HDEVINFO DeviceInfoSet,
+       IN PSP_DEVINFO_DATA DeviceInfoData,
+       IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
+       IN DWORD ClassInstallParamsSize)
+{
+    PSP_PROPCHANGE_PARAMS PropChangeParams = (PSP_PROPCHANGE_PARAMS)ClassInstallParams;
+    BOOL ret = FALSE;
+
+    if (!DeviceInfoData)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (ClassInstallParamsSize != sizeof(SP_PROPCHANGE_PARAMS))
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (PropChangeParams && PropChangeParams->StateChange != DICS_ENABLE
+        && PropChangeParams->StateChange != DICS_DISABLE && PropChangeParams->StateChange != DICS_PROPCHANGE
+        && PropChangeParams->StateChange != DICS_START && PropChangeParams->StateChange != DICS_STOP)
+        SetLastError(ERROR_INVALID_FLAGS);
+    else if (PropChangeParams && PropChangeParams->Scope != DICS_FLAG_GLOBAL
+        && PropChangeParams->Scope != DICS_FLAG_CONFIGSPECIFIC)
+        SetLastError(ERROR_INVALID_FLAGS);
+    else if (PropChangeParams
+        && (PropChangeParams->StateChange == DICS_START || PropChangeParams->StateChange == DICS_STOP)
+        && PropChangeParams->Scope != DICS_FLAG_CONFIGSPECIFIC)
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else
+    {
+        PSP_PROPCHANGE_PARAMS *CurrentPropChangeParams;
+        if (!DeviceInfoData)
+        {
+            struct DeviceInfoSet *list = (struct DeviceInfoSet *)DeviceInfoSet;
+            CurrentPropChangeParams = &list->ClassInstallParams.PropChange;
+        }
+        else
+        {
+            struct DeviceInfoElement *deviceInfo = (struct DeviceInfoElement *)DeviceInfoData->Reserved;
+            CurrentPropChangeParams = &deviceInfo->ClassInstallParams.PropChange;
+        }
+        if (*CurrentPropChangeParams)
+        {
+            MyFree(*CurrentPropChangeParams);
+            *CurrentPropChangeParams = NULL;
+        }
+        if (PropChangeParams)
+        {
+            *CurrentPropChangeParams = MyMalloc(sizeof(SP_PROPCHANGE_PARAMS));
+            if (!*CurrentPropChangeParams)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto done;
+            }
+            memcpy(*CurrentPropChangeParams, PropChangeParams, sizeof(SP_PROPCHANGE_PARAMS));
+        }
+        ret = TRUE;
+    }
+
+done:
+    return ret;
+}
+
+static DWORD
+GetFunctionPointer(
+        IN PWSTR InstallerName,
+        OUT HMODULE* ModulePointer,
+        OUT PVOID* FunctionPointer)
+{
+    HMODULE hModule = NULL;
+    LPSTR FunctionNameA = NULL;
+    PWCHAR Comma;
+    DWORD rc;
+
+    *ModulePointer = NULL;
+    *FunctionPointer = NULL;
+
+    Comma = strchrW(InstallerName, ',');
+    if (!Comma)
+    {
+        rc = ERROR_INVALID_PARAMETER;
+        goto cleanup;
     }
 
     /* Load library */
@@ -3063,7 +3519,7 @@ BOOL WINAPI SetupDiCallClassInstaller(
 {
     BOOL ret = FALSE;
 
-    TRACE("%ld %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData);
+    TRACE("%u %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData);
 
     if (!DeviceInfoSet)
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -3113,6 +3569,10 @@ BOOL WINAPI SetupDiCallClassInstaller(
             case DIF_NEWDEVICEWIZARD_PREANALYZE:
                 CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
                 break;
+            case DIF_PROPERTYCHANGE:
+                CanHandle = CLASS_COINSTALLER | DEVICE_COINSTALLER | CLASS_INSTALLER;
+                DefaultHandler = SetupDiChangeState;
+                break;
             case DIF_REGISTER_COINSTALLERS:
                 CanHandle = CLASS_COINSTALLER | CLASS_INSTALLER;
                 DefaultHandler = SetupDiRegisterCoDeviceInstallers;
@@ -3122,8 +3582,8 @@ BOOL WINAPI SetupDiCallClassInstaller(
                 DefaultHandler = SetupDiSelectBestCompatDrv;
                 break;
             default:
-                FIXME("Install function %ld not implemented\n", InstallFunction);
-                SetLastError(ERROR_INVALID_PARAMETER);
+                ERR("Install function %u not supported\n", InstallFunction);
+                SetLastError(ERROR_NOT_SUPPORTED);
         }
 
         InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
@@ -3151,13 +3611,13 @@ BOOL WINAPI SetupDiCallClassInstaller(
                 hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
                 if (hKey != INVALID_HANDLE_VALUE)
                 {
-                    rc = RegQueryValueExW(hKey, L"CoInstallers32", NULL, &dwRegType, NULL, &dwLength);
+                    rc = RegQueryValueExW(hKey, REGSTR_VAL_COINSTALLERS_32, NULL, &dwRegType, NULL, &dwLength);
                     if (rc == ERROR_SUCCESS && dwRegType == REG_MULTI_SZ)
                     {
                         LPWSTR KeyBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);
                         if (KeyBuffer != NULL)
                         {
-                            rc = RegQueryValueExW(hKey, L"CoInstallers32", NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
+                            rc = RegQueryValueExW(hKey, REGSTR_VAL_COINSTALLERS_32, NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
                             if (rc == ERROR_SUCCESS)
                             {
                                 LPWSTR ptr;
@@ -3186,7 +3646,7 @@ BOOL WINAPI SetupDiCallClassInstaller(
             {
                 rc = RegOpenKeyEx(
                     HKEY_LOCAL_MACHINE,
-                    L"SYSTEM\\CurrentControlSet\\Control\\CoDeviceInstallers",
+                    REGSTR_PATH_CODEVICEINSTALLERS,
                     0, /* Options */
                     KEY_QUERY_VALUE,
                     &hKey);
@@ -3233,13 +3693,13 @@ BOOL WINAPI SetupDiCallClassInstaller(
                 hKey = SetupDiOpenClassRegKey(&DeviceInfoData->ClassGuid, KEY_QUERY_VALUE);
                 if (hKey != INVALID_HANDLE_VALUE)
                 {
-                    rc = RegQueryValueExW(hKey, L"Installer32", NULL, &dwRegType, NULL, &dwLength);
+                    rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, &dwRegType, NULL, &dwLength);
                     if (rc == ERROR_SUCCESS && dwRegType == REG_SZ)
                     {
                         LPWSTR KeyBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);
                         if (KeyBuffer != NULL)
                         {
-                            rc = RegQueryValueExW(hKey, L"Installer32", NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
+                            rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
                             if (rc == ERROR_SUCCESS)
                             {
                                 /* Get ClassInstaller function pointer */
@@ -3363,6 +3823,45 @@ BOOL WINAPI SetupDiCallClassInstaller(
     return ret;
 }
 
+/***********************************************************************
+ *             SetupDiGetDeviceInfoListDetailW  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInfoListDetailW(
+        IN HDEVINFO DeviceInfoSet,
+        OUT PSP_DEVINFO_LIST_DETAIL_DATA_W DeviceInfoListDetailData)
+{
+    struct DeviceInfoSet *list;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p\n", DeviceInfoSet, DeviceInfoListDetailData);
+
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (!DeviceInfoListDetailData)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoListDetailData->cbSize != sizeof(SP_DEVINFO_LIST_DETAIL_DATA_W))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else
+    {
+        memcpy(
+            &DeviceInfoListDetailData->ClassGuid,
+            &list->ClassGuid,
+            sizeof(GUID));
+        DeviceInfoListDetailData->RemoteMachineHandle = list->hMachine;
+        if (list->MachineName)
+            strcpyW(DeviceInfoListDetailData->RemoteMachineName, list->MachineName + 2);
+        else
+            DeviceInfoListDetailData->RemoteMachineName[0] = 0;
+
+        ret = TRUE;
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
 /***********************************************************************
  *             SetupDiGetDeviceInstallParamsA (SETUPAPI.@)
  */
@@ -3432,12 +3931,338 @@ BOOL WINAPI SetupDiGetDeviceInstallParamsW(
     {
         PSP_DEVINSTALL_PARAMS_W Source;
 
-        if (DeviceInfoData)
-            Source = &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->InstallParams;
+        if (DeviceInfoData)
+            Source = &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->InstallParams;
+        else
+            Source = &list->InstallParams;
+        memcpy(DeviceInstallParams, Source, Source->cbSize);
+        ret = TRUE;
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiSetDeviceInstallParamsW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiSetDeviceInstallParamsW(
+       IN HDEVINFO DeviceInfoSet,
+       IN PSP_DEVINFO_DATA DeviceInfoData,
+       IN PSP_DEVINSTALL_PARAMS_W DeviceInstallParams)
+{
+    struct DeviceInfoSet *list;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams);
+
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (!DeviceInstallParams)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInstallParams->cbSize != sizeof(SP_DEVINSTALL_PARAMS_W))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else
+    {
+        PSP_DEVINSTALL_PARAMS_W Destination;
+
+        /* FIXME: Validate parameters */
+
+        if (DeviceInfoData)
+            Destination = &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->InstallParams;
+        else
+            Destination = &list->InstallParams;
+        memcpy(Destination, DeviceInstallParams, DeviceInstallParams->cbSize);
+        ret = TRUE;
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiGetDeviceInstanceIdA(SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInstanceIdA(
+        IN HDEVINFO DeviceInfoSet,
+        IN PSP_DEVINFO_DATA DeviceInfoData,
+        OUT PSTR DeviceInstanceId OPTIONAL,
+        IN DWORD DeviceInstanceIdSize,
+        OUT PDWORD RequiredSize OPTIONAL)
+{
+    PWSTR DeviceInstanceIdW = NULL;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p %p %lu %p\n", DeviceInfoSet, DeviceInfoData,
+          DeviceInstanceId, DeviceInstanceIdSize, RequiredSize);
+
+    if (!DeviceInstanceId && DeviceInstanceIdSize > 0)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
+    {
+        if (DeviceInstanceIdSize != 0)
+        {
+            DeviceInstanceIdW = MyMalloc(DeviceInstanceIdSize * sizeof(WCHAR));
+            if (DeviceInstanceIdW == NULL)
+                return FALSE;
+        }
+
+        ret = SetupDiGetDeviceInstanceIdW(DeviceInfoSet, DeviceInfoData,
+                                          DeviceInstanceIdW, DeviceInstanceIdSize,
+                                          RequiredSize);
+
+        if (ret && DeviceInstanceIdW != NULL)
+        {
+            if (WideCharToMultiByte(CP_ACP, 0, DeviceInstanceIdW, -1,
+                DeviceInstanceId, DeviceInstanceIdSize, NULL, NULL) == 0)
+            {
+                DeviceInstanceId[0] = '\0';
+                ret = FALSE;
+            }
+        }
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiGetDeviceInstanceIdW(SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInstanceIdW(
+        IN HDEVINFO DeviceInfoSet,
+        IN PSP_DEVINFO_DATA DeviceInfoData,
+        OUT PWSTR DeviceInstanceId OPTIONAL,
+        IN DWORD DeviceInstanceIdSize,
+        OUT PDWORD RequiredSize OPTIONAL)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%p %p %p %lu %p\n", DeviceInfoSet, DeviceInfoData,
+          DeviceInstanceId, DeviceInstanceIdSize, RequiredSize);
+
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (!DeviceInfoData)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (!DeviceInstanceId && DeviceInstanceIdSize > 0)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInstanceId && DeviceInstanceIdSize == 0)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
+    {
+        struct DeviceInfoElement *DevInfo = (struct DeviceInfoElement *)DeviceInfoData->Reserved;
+        DWORD required;
+
+        required = (wcslen(DevInfo->DeviceName) + 1) * sizeof(WCHAR);
+        if (RequiredSize)
+            *RequiredSize = required;
+
+        if (required <= DeviceInstanceIdSize)
+        {
+            wcscpy(DeviceInstanceId, DevInfo->DeviceName);
+            ret = TRUE;
+        }
+        else
+            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiGetClassDevPropertySheetsA(SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetClassDevPropertySheetsA(
+        IN HDEVINFO DeviceInfoSet,
+        IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+        IN LPPROPSHEETHEADERA PropertySheetHeader,
+        IN DWORD PropertySheetHeaderPageListSize,
+        OUT PDWORD RequiredSize OPTIONAL,
+        IN DWORD PropertySheetType)
+{
+    PROPSHEETHEADERW psh;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p %p 0%lx %p 0x%lx\n", DeviceInfoSet, DeviceInfoData,
+        PropertySheetHeader, PropertySheetHeaderPageListSize,
+        RequiredSize, PropertySheetType);
+
+    psh.dwFlags = PropertySheetHeader->dwFlags;
+    psh.phpage = PropertySheetHeader->phpage;
+    psh.nPages = PropertySheetHeader->nPages;
+
+    ret = SetupDiGetClassDevPropertySheetsW(DeviceInfoSet, DeviceInfoData, PropertySheetHeader ? &psh : NULL,
+                                            PropertySheetHeaderPageListSize, RequiredSize,
+                                            PropertySheetType);
+    if (ret)
+    {
+        PropertySheetHeader->nPages = psh.nPages;
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+struct ClassDevPropertySheetsData
+{
+    HPROPSHEETPAGE *PropertySheetPages;
+    DWORD MaximumNumberOfPages;
+    DWORD NumberOfPages;
+};
+
+static BOOL WINAPI GetClassDevPropertySheetsCallback(
+        IN HPROPSHEETPAGE hPropSheetPage,
+        IN OUT LPARAM lParam)
+{
+    struct ClassDevPropertySheetsData *PropPageData;
+
+    PropPageData = (struct ClassDevPropertySheetsData *)lParam;
+
+    if (PropPageData->NumberOfPages < PropPageData->MaximumNumberOfPages)
+    {
+        *PropPageData->PropertySheetPages = hPropSheetPage;
+        PropPageData->PropertySheetPages++;
+    }
+
+    PropPageData->NumberOfPages++;
+    return TRUE;
+}
+
+/***********************************************************************
+ *             SetupDiGetClassDevPropertySheetsW(SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetClassDevPropertySheetsW(
+        IN HDEVINFO DeviceInfoSet,
+        IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+        IN OUT LPPROPSHEETHEADERW PropertySheetHeader,
+        IN DWORD PropertySheetHeaderPageListSize,
+        OUT PDWORD RequiredSize OPTIONAL,
+        IN DWORD PropertySheetType)
+{
+    struct DeviceInfoSet *list;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p %p 0%lx %p 0x%lx\n", DeviceInfoSet, DeviceInfoData,
+        PropertySheetHeader, PropertySheetHeaderPageListSize,
+        RequiredSize, PropertySheetType);
+
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (!PropertySheetHeader)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (PropertySheetHeader->dwFlags & PSH_PROPSHEETPAGE)
+        SetLastError(ERROR_INVALID_FLAGS);
+    else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (!DeviceInfoData && IsEqualIID(&list->ClassGuid, &GUID_NULL))
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (!PropertySheetHeader)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (PropertySheetType != DIGCDP_FLAG_ADVANCED
+          && PropertySheetType != DIGCDP_FLAG_BASIC
+          && PropertySheetType != DIGCDP_FLAG_REMOTE_ADVANCED
+          && PropertySheetType != DIGCDP_FLAG_REMOTE_BASIC)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
+    {
+        HKEY hKey = INVALID_HANDLE_VALUE;
+        SP_PROPSHEETPAGE_REQUEST Request;
+        LPWSTR PropPageProvider = NULL;
+        HMODULE hModule = NULL;
+        PROPERTY_PAGE_PROVIDER pPropPageProvider = NULL;
+        struct ClassDevPropertySheetsData PropPageData;
+        DWORD dwLength, dwRegType;
+        DWORD rc;
+
+        if (DeviceInfoData)
+            hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
+        else
+        {
+            hKey = SetupDiOpenClassRegKeyExW(&list->ClassGuid, KEY_QUERY_VALUE,
+                DIOCR_INSTALLER, list->MachineName + 2, NULL);
+        }
+        if (hKey == INVALID_HANDLE_VALUE)
+            goto cleanup;
+
+        rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, &dwRegType, NULL, &dwLength);
+        if (rc == ERROR_FILE_NOT_FOUND)
+        {
+            /* No registry key. As it is optional, don't say it's a bad error */
+            if (RequiredSize)
+                *RequiredSize = 0;
+            ret = TRUE;
+            goto cleanup;
+        }
+        else if (rc != ERROR_SUCCESS && dwRegType != REG_SZ)
+        {
+            SetLastError(rc);
+            goto cleanup;
+        }
+
+        PropPageProvider = HeapAlloc(GetProcessHeap(), 0, dwLength + sizeof(WCHAR));
+        if (!PropPageProvider)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            goto cleanup;
+        }
+        rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, NULL, (LPBYTE)PropPageProvider, &dwLength);
+        if (rc != ERROR_SUCCESS)
+        {
+            SetLastError(rc);
+            goto cleanup;
+        }
+        PropPageProvider[dwLength / sizeof(WCHAR)] = 0;
+
+        rc = GetFunctionPointer(PropPageProvider, &hModule, (PVOID*)&pPropPageProvider);
+        if (rc != ERROR_SUCCESS)
+        {
+            SetLastError(ERROR_INVALID_PROPPAGE_PROVIDER);
+            goto cleanup;
+        }
+
+        Request.cbSize = sizeof(SP_PROPSHEETPAGE_REQUEST);
+        Request.PageRequested = SPPSR_ENUM_ADV_DEVICE_PROPERTIES;
+        Request.DeviceInfoSet = DeviceInfoSet;
+        Request.DeviceInfoData = DeviceInfoData;
+        PropPageData.PropertySheetPages = &PropertySheetHeader->phpage[PropertySheetHeader->nPages];
+        PropPageData.MaximumNumberOfPages = PropertySheetHeaderPageListSize - PropertySheetHeader->nPages;
+        PropPageData.NumberOfPages = 0;
+        ret = pPropPageProvider(&Request, GetClassDevPropertySheetsCallback, (LPARAM)&PropPageData);
+        if (!ret)
+            goto cleanup;
+
+        if (RequiredSize)
+            *RequiredSize = PropPageData.NumberOfPages + PropertySheetHeader->nPages;
+        if (PropPageData.NumberOfPages <= PropPageData.MaximumNumberOfPages)
+        {
+            PropertySheetHeader->nPages += PropPageData.NumberOfPages;
+            ret = TRUE;
+        }
         else
-            Source = &list->InstallParams;
-        memcpy(DeviceInstallParams, Source, Source->cbSize);
-        ret = TRUE;
+        {
+            PropertySheetHeader->nPages += PropPageData.MaximumNumberOfPages;
+            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        }
+
+cleanup:
+        if (hKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hKey);
+        HeapFree(GetProcessHeap(), 0, PropPageProvider);
+        FreeFunctionPointer(hModule, pPropPageProvider);
     }
 
     TRACE("Returning %d\n", ret);
@@ -3445,48 +4270,42 @@ BOOL WINAPI SetupDiGetDeviceInstallParamsW(
 }
 
 /***********************************************************************
- *             SetupDiSetDeviceInstallParamsW (SETUPAPI.@)
+ *             SetupDiCreateDevRegKeyA (SETUPAPI.@)
  */
-BOOL WINAPI SetupDiSetDeviceInstallParamsW(
-       IN HDEVINFO DeviceInfoSet,
-       IN PSP_DEVINFO_DATA DeviceInfoData,
-       IN PSP_DEVINSTALL_PARAMS_W DeviceInstallParams)
+HKEY WINAPI SetupDiCreateDevRegKeyA(
+        IN HDEVINFO DeviceInfoSet,
+        IN PSP_DEVINFO_DATA DeviceInfoData,
+        IN DWORD Scope,
+        IN DWORD HwProfile,
+        IN DWORD KeyType,
+        IN HINF InfHandle OPTIONAL,
+        IN PCSTR InfSectionName OPTIONAL)
 {
-    struct DeviceInfoSet *list;
-    BOOL ret = FALSE;
-
-    TRACE("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams);
+    PCWSTR InfSectionNameW = NULL;
+    HKEY ret = INVALID_HANDLE_VALUE;
 
-    if (!DeviceInfoSet)
-        SetLastError(ERROR_INVALID_HANDLE);
-    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
-        SetLastError(ERROR_INVALID_HANDLE);
-    else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
-        SetLastError(ERROR_INVALID_USER_BUFFER);
-    else if (!DeviceInstallParams)
-        SetLastError(ERROR_INVALID_PARAMETER);
-    else if (DeviceInstallParams->cbSize != sizeof(SP_DEVINSTALL_PARAMS_W))
-        SetLastError(ERROR_INVALID_USER_BUFFER);
-    else
+    if (InfSectionName)
     {
-        PSP_DEVINSTALL_PARAMS_W Destination;
+        InfSectionNameW = MultiByteToUnicode(InfSectionName, CP_ACP);
+        if (InfSectionNameW == NULL) return INVALID_HANDLE_VALUE;
+    }
 
-        /* FIXME: Validate parameters */
+    ret = SetupDiCreateDevRegKeyW(DeviceInfoSet,
+                                  DeviceInfoData,
+                                  Scope,
+                                  HwProfile,
+                                  KeyType,
+                                  InfHandle,
+                                  InfSectionNameW);
 
-        if (DeviceInfoData)
-            Destination = &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->InstallParams;
-        else
-            Destination = &list->InstallParams;
-        memcpy(Destination, DeviceInstallParams, DeviceInstallParams->cbSize);
-        ret = TRUE;
-    }
+    if (InfSectionNameW != NULL)
+        MyFree((PVOID)InfSectionNameW);
 
-    TRACE("Returning %d\n", ret);
     return ret;
 }
 
 /***********************************************************************
- *             SetupDiCreateDevRegKey (SETUPAPI.@)
+ *             SetupDiCreateDevRegKeyW (SETUPAPI.@)
  */
 HKEY WINAPI SetupDiCreateDevRegKeyW(
         IN HDEVINFO DeviceInfoSet,
@@ -3526,25 +4345,101 @@ HKEY WINAPI SetupDiCreateDevRegKeyW(
         LPWSTR pDeviceInstance; /* Points into DriverKey, on the Index field */
         DWORD Index; /* Index used in the DriverKey name */
         DWORD rc;
+        HKEY hHWProfilesKey = INVALID_HANDLE_VALUE;
+        HKEY hHWProfileKey = INVALID_HANDLE_VALUE;
+        HKEY hEnumKey = INVALID_HANDLE_VALUE;
         HKEY hClassKey = INVALID_HANDLE_VALUE;
         HKEY hDeviceKey = INVALID_HANDLE_VALUE;
         HKEY hKey = INVALID_HANDLE_VALUE;
+        HKEY RootKey;
 
-        if (Scope == DICS_FLAG_CONFIGSPECIFIC)
+        if (Scope == DICS_FLAG_GLOBAL)
+            RootKey = list->HKLM;
+        else /* Scope == DICS_FLAG_CONFIGSPECIFIC */
         {
-            FIXME("DICS_FLAG_CONFIGSPECIFIC case unimplemented\n");
-            goto cleanup;
+            rc = RegOpenKeyExW(list->HKLM,
+                REGSTR_PATH_HWPROFILES,
+                0,
+                0,
+                &hHWProfilesKey);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            if (HwProfile == 0)
+            {
+                rc = RegOpenKeyExW(
+                    hHWProfilesKey,
+                    REGSTR_KEY_CURRENT,
+                    0,
+                    KEY_CREATE_SUB_KEY,
+                    &hHWProfileKey);
+            }
+            else
+            {
+                WCHAR subKey[5];
+                snprintfW(subKey, 4, L"%04lu", HwProfile);
+                subKey[4] = '\0';
+                rc = RegOpenKeyExW(
+                    hHWProfilesKey,
+                    subKey,
+                    0,
+                    KEY_CREATE_SUB_KEY,
+                    &hHWProfileKey);
+            }
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            RootKey = hHWProfileKey;
         }
 
         if (KeyType == DIREG_DEV)
         {
-            FIXME("DIREG_DEV case unimplemented\n");
+            struct DeviceInfoElement *deviceInfo = (struct DeviceInfoElement *)DeviceInfoData->Reserved;
+
+            rc = RegCreateKeyExW(
+                RootKey,
+                REGSTR_PATH_SYSTEMENUM,
+                0,
+                NULL,
+                REG_OPTION_NON_VOLATILE,
+                KEY_CREATE_SUB_KEY,
+                NULL,
+                &hEnumKey,
+                NULL);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            rc = RegCreateKeyExW(
+                hEnumKey,
+                deviceInfo->DeviceName,
+                0,
+                NULL,
+                REG_OPTION_NON_VOLATILE,
+#if _WIN32_WINNT >= 0x502
+                KEY_READ | KEY_WRITE,
+#else
+                KEY_ALL_ACCESS,
+#endif
+                NULL,
+                &hKey,
+                NULL);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
         }
         else /* KeyType == DIREG_DRV */
         {
             if (UuidToStringW((UUID*)&DeviceInfoData->ClassGuid, &lpGuidString) != RPC_S_OK)
                 goto cleanup;
-            /* The driver key is in HKLM\System\CurrentControlSet\Control\Class\{GUID}\Index */
+            /* The driver key is in \System\CurrentControlSet\Control\Class\{GUID}\Index */
             DriverKey = HeapAlloc(GetProcessHeap(), 0, (wcslen(lpGuidString) + 7) * sizeof(WCHAR) + sizeof(UNICODE_STRING));
             if (!DriverKey)
             {
@@ -3555,8 +4450,8 @@ HKEY WINAPI SetupDiCreateDevRegKeyW(
             wcscat(DriverKey, lpGuidString);
             wcscat(DriverKey, L"}\\");
             pDeviceInstance = &DriverKey[wcslen(DriverKey)];
-            rc = RegOpenKeyExW(list->HKLM,
-                ControlClass,
+            rc = RegOpenKeyExW(RootKey,
+                REGSTR_PATH_CLASS_NT,
                 0,
                 KEY_CREATE_SUB_KEY,
                 &hClassKey);
@@ -3607,7 +4502,7 @@ HKEY WINAPI SetupDiCreateDevRegKeyW(
             hDeviceKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, Scope, HwProfile, DIREG_DEV, KEY_SET_VALUE);
             if (hDeviceKey == INVALID_HANDLE_VALUE)
                 goto cleanup;
-            rc = RegSetValueEx(hDeviceKey, L"Driver", 0, REG_SZ, (const BYTE *)DriverKey, (wcslen(DriverKey) + 1) * sizeof(WCHAR));
+            rc = RegSetValueEx(hDeviceKey, REGSTR_VAL_DRIVER, 0, REG_SZ, (const BYTE *)DriverKey, (wcslen(DriverKey) + 1) * sizeof(WCHAR));
             if (rc != ERROR_SUCCESS)
             {
                 SetLastError(rc);
@@ -3627,6 +4522,12 @@ cleanup:
         if (lpGuidString)
             RpcStringFreeW(&lpGuidString);
         HeapFree(GetProcessHeap(), 0, DriverKey);
+        if (hHWProfilesKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hHWProfilesKey);
+        if (hHWProfileKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hHWProfileKey);
+        if (hEnumKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hEnumKey);
         if (hClassKey != INVALID_HANDLE_VALUE)
             RegCloseKey(hClassKey);
         if (hDeviceKey != INVALID_HANDLE_VALUE)
@@ -3686,7 +4587,7 @@ HKEY WINAPI SetupDiOpenDevRegKey(
         {
             rc = RegOpenKeyExW(
                 list->HKLM,
-                EnumKeyName,
+                REGSTR_PATH_SYSTEMENUM,
                 0, /* Options */
                 KEY_ENUMERATE_SUB_KEYS,
                 &hRootKey);
@@ -3715,7 +4616,7 @@ HKEY WINAPI SetupDiOpenDevRegKey(
                 goto cleanup;
             }
             /* Read the 'Driver' key */
-            rc = RegQueryValueExW(hKey, L"Driver", NULL, &dwRegType, NULL, &dwLength);
+            rc = RegQueryValueExW(hKey, REGSTR_VAL_DRIVER, NULL, &dwRegType, NULL, &dwLength);
             if (rc != ERROR_SUCCESS)
             {
                 SetLastError(rc);
@@ -3732,7 +4633,7 @@ HKEY WINAPI SetupDiOpenDevRegKey(
                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
                 goto cleanup;
             }
-            rc = RegQueryValueExW(hKey, L"Driver", NULL, &dwRegType, (LPBYTE)DriverKey, &dwLength);
+            rc = RegQueryValueExW(hKey, REGSTR_VAL_DRIVER, NULL, &dwRegType, (LPBYTE)DriverKey, &dwLength);
             if (rc != ERROR_SUCCESS)
             {
                 SetLastError(rc);
@@ -3743,7 +4644,7 @@ HKEY WINAPI SetupDiOpenDevRegKey(
             /* Need to open the driver key */
             rc = RegOpenKeyExW(
                 list->HKLM,
-                ControlClass,
+                REGSTR_PATH_CLASS_NT,
                 0, /* Options */
                 KEY_ENUMERATE_SUB_KEYS,
                 &hRootKey);
@@ -3850,7 +4751,7 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
     else if (CreationFlags & ~(DICD_GENERATE_ID | DICD_INHERIT_CLASSDRVS))
     {
         TRACE("Unknown flags: 0x%08lx\n", CreationFlags & ~(DICD_GENERATE_ID | DICD_INHERIT_CLASSDRVS));
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(ERROR_INVALID_FLAGS);
     }
     else
     {
@@ -3859,7 +4760,7 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
         if (CreationFlags & DICD_GENERATE_ID)
         {
             /* Generate a new unique ID for this device */
-            SetLastError(ERROR_GEN_FAILURE);
+            SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
             FIXME("not implemented\n");
         }
         else
@@ -3886,8 +4787,7 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
             {
                 struct DeviceInfoElement *deviceInfo;
 
-                /* FIXME: ClassGuid can be NULL */
-                if (CreateDeviceInfoElement(DeviceName, ClassGuid, &deviceInfo))
+                if (CreateDeviceInfoElement(list, DeviceName, ClassGuid, &deviceInfo))
                 {
                     InsertTailList(&list->ListHead, &deviceInfo->ListEntry);
 
@@ -3902,7 +4802,7 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
                         else
                         {
                             memcpy(&DeviceInfoData->ClassGuid, ClassGuid, sizeof(GUID));
-                            DeviceInfoData->DevInst = 0; /* FIXME */
+                            DeviceInfoData->DevInst = deviceInfo->dnDevInst;
                             DeviceInfoData->Reserved = (ULONG_PTR)deviceInfo;
                             ret = TRUE;
                         }
@@ -4015,6 +4915,7 @@ AddDriverToList(
         driverInfo->Details.DrvDescription, InfFile, InfInstallSection, Rank);
 
     driverInfo->DriverRank = Rank;
+    memcpy(&driverInfo->DriverDate, &DriverDate, sizeof(FILETIME));
     memcpy(&driverInfo->ClassGuid, ClassGuid, sizeof(GUID));
     driverInfo->Info.DriverType = DriverType;
     driverInfo->Info.Reserved = (ULONG_PTR)driverInfo;
@@ -4038,7 +4939,10 @@ AddDriverToList(
     PreviousEntry = DriverListHead->Flink;
     while (PreviousEntry != DriverListHead)
     {
-        if (((struct DriverInfoElement *)PreviousEntry)->DriverRank >= Rank)
+        struct DriverInfoElement *CurrentDriver;
+        CurrentDriver = CONTAINING_RECORD(PreviousEntry, struct DriverInfoElement, ListEntry);
+        if (CurrentDriver->DriverRank > Rank ||
+            (CurrentDriver->DriverRank == Rank && CurrentDriver->DriverDate.QuadPart > driverInfo->DriverDate.QuadPart))
         {
             /* Insert before the current item */
             InsertHeadList(PreviousEntry, &driverInfo->ListEntry);
@@ -4174,7 +5078,36 @@ GetVersionInformationFromInfFile(
     }
     /* Get driver version. Invalid version = 0.0.0.0 */
     *DriverVersion = 0;
-    /* FIXME: use pVersion to fill DriverVersion variable */
+    if (pVersion)
+    {
+        WORD Major, Minor = 0, Revision = 0, Build = 0;
+        LPWSTR pMinor = NULL, pRevision = NULL, pBuild = NULL;
+        LARGE_INTEGER fullVersion;
+
+        pMinor = strchrW(pVersion, '.');
+        if (pMinor)
+        {
+            *pMinor = 0;
+            pRevision = strchrW(++pMinor, '.');
+            Minor = atoiW(pMinor);
+        }
+        if (pRevision)
+        {
+            *pRevision = 0;
+            pBuild = strchrW(++pRevision, '.');
+            Revision = atoiW(pRevision);
+        }
+        if (pBuild)
+        {
+            *pBuild = 0;
+            pBuild++;
+            Build = atoiW(pBuild);
+        }
+        Major = atoiW(pVersion);
+        fullVersion.u.HighPart = Major << 16 | Minor;
+        fullVersion.u.LowPart = Revision << 16 | Build;
+        memcpy(DriverVersion, &fullVersion, sizeof(LARGE_INTEGER));
+    }
 
     ret = TRUE;
 
@@ -4183,7 +5116,6 @@ cleanup:
         HeapFree(GetProcessHeap(), 0, ProviderName);
     HeapFree(GetProcessHeap(), 0, DriverVer);
 
-    TRACE("Returning %d\n", ret);
     return ret;
 }
 
@@ -4361,10 +5293,14 @@ SetupDiBuildDriverInfoList(
                 wcscpy(pFullFilename, filename);
                 TRACE("Opening file %S\n", FullInfFileName);
 
-                currentInfFileDetails = HeapAlloc(GetProcessHeap(), 0, sizeof(struct InfFileDetails));
+                currentInfFileDetails = HeapAlloc(
+                    GetProcessHeap(),
+                    0,
+                    FIELD_OFFSET(struct InfFileDetails, FullInfFileName) + wcslen(FullInfFileName) * sizeof(WCHAR) + UNICODE_NULL);
                 if (!currentInfFileDetails)
                     continue;
                 memset(currentInfFileDetails, 0, sizeof(struct InfFileDetails));
+                wcscpy(currentInfFileDetails->FullInfFileName, FullInfFileName);
 
                 currentInfFileDetails->hInf = SetupOpenInfFileW(FullInfFileName, NULL, INF_STYLE_WIN4, NULL);
                 ReferenceInfFile(currentInfFileDetails);
@@ -4498,7 +5434,7 @@ SetupDiBuildDriverInfoList(
                                 DriverAlreadyAdded = FALSE;
                                 for (DriverRank = 0, currentId = (LPCWSTR)HardwareIDs; !DriverAlreadyAdded && *currentId; currentId += wcslen(currentId) + 1, DriverRank++)
                                 {
-                                    if (wcscmp(DeviceId, currentId) == 0)
+                                    if (wcsicmp(DeviceId, currentId) == 0)
                                     {
                                         AddDriverToList(
                                             &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->DriverListHead,
@@ -4519,7 +5455,7 @@ SetupDiBuildDriverInfoList(
                                 {
                                     for (DriverRank = 0, currentId = (LPCWSTR)CompatibleIDs; !DriverAlreadyAdded && *currentId; currentId += wcslen(currentId) + 1, DriverRank++)
                                     {
-                                        if (wcscmp(DeviceId, currentId) == 0)
+                                        if (wcsicmp(DeviceId, currentId) == 0)
                                         {
                                             AddDriverToList(
                                                 &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->DriverListHead,
@@ -4600,7 +5536,7 @@ SetupDiDeleteDeviceInfo(
     TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
 
     FIXME("not implemented\n");
-    SetLastError(ERROR_GEN_FAILURE);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
     return FALSE;
 }
 
@@ -4748,7 +5684,7 @@ SetupDiOpenDeviceInfoW(
     else if (OpenFlags & ~(DIOD_CANCEL_REMOVE | DIOD_INHERIT_CLASSDRVS))
     {
         TRACE("Unknown flags: 0x%08lx\n", OpenFlags & ~(DIOD_CANCEL_REMOVE | DIOD_INHERIT_CLASSDRVS));
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(ERROR_INVALID_FLAGS);
     }
     else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
         SetLastError(ERROR_INVALID_USER_BUFFER);
@@ -4779,7 +5715,7 @@ SetupDiOpenDeviceInfoW(
             /* Open supposed registry key */
             rc = RegOpenKeyExW(
                 list->HKLM,
-                EnumKeyName,
+                REGSTR_PATH_SYSTEMENUM,
                 0, /* Options */
                 KEY_ENUMERATE_SUB_KEYS,
                 &hEnumKey);
@@ -4797,12 +5733,16 @@ SetupDiOpenDeviceInfoW(
             RegCloseKey(hEnumKey);
             if (rc != ERROR_SUCCESS)
             {
+                if (rc == ERROR_FILE_NOT_FOUND)
+                    rc = ERROR_NO_SUCH_DEVINST;
                 SetLastError(rc);
                 return FALSE;
             }
 
-            /* FIXME: GUID_NULL is not allowed */
-            if (!CreateDeviceInfoElement(DeviceInstanceId, &GUID_NULL /* FIXME */, &deviceInfo))
+            /* FIXME: try to get ClassGUID from registry, instead of
+             * sending GUID_NULL to CreateDeviceInfoElement
+             */
+            if (!CreateDeviceInfoElement(list, DeviceInstanceId, &GUID_NULL, &deviceInfo))
             {
                 RegCloseKey(hKey);
                 return FALSE;
@@ -4816,7 +5756,7 @@ SetupDiOpenDeviceInfoW(
         if (ret && deviceInfo && DeviceInfoData)
         {
             memcpy(&DeviceInfoData->ClassGuid, &deviceInfo->ClassGuid, sizeof(GUID));
-            DeviceInfoData->DevInst = 0; /* FIXME */
+            DeviceInfoData->DevInst = deviceInfo->dnDevInst;
             DeviceInfoData->Reserved = (ULONG_PTR)deviceInfo;
         }
     }
@@ -5232,7 +6172,7 @@ SetupDiGetDriverInfoDetailA(
     DWORD BufSize = 0;
     DWORD HardwareIDLen = 0;
     BOOL ret = FALSE;
-    
+
     /* do some sanity checks, the unicode version might do more thorough checks */
     if (DriverInfoData == NULL ||
         (DriverInfoDetailData == NULL && DriverInfoDetailDataSize != 0) ||
@@ -5260,7 +6200,7 @@ SetupDiGetDriverInfoDetailA(
     }
     DriverInfoDataW.DriverType = DriverInfoData->DriverType;
     DriverInfoDataW.Reserved = DriverInfoData->Reserved;
-    
+
     /* convert the strings to unicode */
     if (MultiByteToWideChar(CP_ACP,
                             0,
@@ -5406,7 +6346,7 @@ SetupDiGetDriverInfoDetailA(
             }
         }
     }
-    
+
 Cleanup:
     if (DriverInfoDetailDataW != NULL)
     {
@@ -5430,6 +6370,10 @@ SetupDiGetDriverInfoDetailW(
 {
     BOOL ret = FALSE;
 
+    TRACE("%p %p %p %p %lu %p\n", DeviceInfoSet, DeviceInfoData,
+        DriverInfoData, DriverInfoDetailData,
+        DriverInfoDetailDataSize, RequiredSize);
+
     if (!DeviceInfoSet)
         SetLastError(ERROR_INVALID_PARAMETER);
     else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
@@ -5446,8 +6390,8 @@ SetupDiGetDriverInfoDetailW(
         SetLastError(ERROR_INVALID_PARAMETER);
     else if (DriverInfoDetailData && DriverInfoDetailData->cbSize != sizeof(SP_DRVINFO_DETAIL_DATA_W))
         SetLastError(ERROR_INVALID_USER_BUFFER);
-    else if (DriverInfoDetailData && DriverInfoDetailData->Reserved == 0)
-        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DriverInfoData->Reserved == 0)
+        SetLastError(ERROR_NO_DRIVER_SELECTED);
     else
     {
         struct DriverInfoElement *driverInfoElement;
@@ -5467,6 +6411,170 @@ SetupDiGetDriverInfoDetailW(
     return ret;
 }
 
+/* Return the current hardware profile id, or -1 if error */
+static DWORD
+GetCurrentHwProfile(
+    IN HDEVINFO DeviceInfoSet)
+{
+    HKEY hKey = INVALID_HANDLE_VALUE;
+    DWORD dwRegType, dwLength;
+    DWORD hwProfile;
+    LONG rc;
+    DWORD ret = (DWORD)-1;
+
+    rc = RegOpenKeyExW(
+        ((struct DeviceInfoSet *)DeviceInfoSet)->HKLM,
+        REGSTR_PATH_IDCONFIGDB,
+        0, /* Options */
+        KEY_QUERY_VALUE,
+        &hKey);
+    if (rc != ERROR_SUCCESS)
+    {
+        SetLastError(rc);
+        goto cleanup;
+    }
+
+    dwLength = sizeof(DWORD);
+    rc = RegQueryValueExW(
+        hKey,
+        REGSTR_VAL_CURRENTCONFIG,
+        NULL,
+        &dwRegType,
+        (LPBYTE)&hwProfile, &dwLength);
+    if (rc != ERROR_SUCCESS)
+    {
+        SetLastError(rc);
+        goto cleanup;
+    }
+    else if (dwRegType != REG_DWORD || dwLength != sizeof(DWORD))
+    {
+        SetLastError(ERROR_GEN_FAILURE);
+        goto cleanup;
+    }
+
+    ret = hwProfile;
+
+cleanup:
+    if (hKey != INVALID_HANDLE_VALUE)
+        RegCloseKey(hKey);
+
+    return hwProfile;
+}
+
+/***********************************************************************
+ *             SetupDiChangeState (SETUPAPI.@)
+ */
+static BOOL StartDevice(VOID) { FIXME("Stub"); return TRUE; }
+static BOOL StopDevice(VOID) { FIXME("Stub"); return TRUE; }
+BOOL WINAPI
+SetupDiChangeState(
+    IN HDEVINFO DeviceInfoSet,
+    IN OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
+{
+    PSP_PROPCHANGE_PARAMS PropChange;
+    HKEY hKey = INVALID_HANDLE_VALUE;
+    LPCWSTR RegistryValueName;
+    DWORD dwConfigFlags, dwLength, dwRegType;
+    LONG rc;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
+
+    if (!DeviceInfoData)
+        PropChange = ((struct DeviceInfoSet *)DeviceInfoSet)->ClassInstallParams.PropChange;
+    else
+        PropChange = ((struct DeviceInfoElement *)DeviceInfoData->Reserved)->ClassInstallParams.PropChange;
+    if (!PropChange)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        goto cleanup;
+    }
+
+    if (PropChange->Scope == DICS_FLAG_GLOBAL)
+        RegistryValueName = REGSTR_VAL_CONFIGFLAGS;
+    else
+        RegistryValueName = REGSTR_VAL_CSCONFIGFLAGS;
+
+    switch (PropChange->StateChange)
+    {
+        case DICS_ENABLE:
+        case DICS_DISABLE:
+        {
+            /* Enable/disable device in registry */
+            hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, PropChange->Scope, PropChange->HwProfile, DIREG_DEV, KEY_QUERY_VALUE | KEY_SET_VALUE);
+            if (hKey == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
+                hKey = SetupDiCreateDevRegKey(DeviceInfoSet, DeviceInfoData, PropChange->Scope, PropChange->HwProfile, DIREG_DEV, NULL, NULL);
+            if (hKey == INVALID_HANDLE_VALUE)
+                break;
+            dwLength = sizeof(DWORD);
+            rc = RegQueryValueExW(
+                hKey,
+                RegistryValueName,
+                NULL,
+                &dwRegType,
+                (LPBYTE)&dwConfigFlags, &dwLength);
+            if (rc == ERROR_FILE_NOT_FOUND)
+                dwConfigFlags = 0;
+            else if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            else if (dwRegType != REG_DWORD || dwLength != sizeof(DWORD))
+            {
+                SetLastError(ERROR_GEN_FAILURE);
+                goto cleanup;
+            }
+            if (PropChange->StateChange == DICS_ENABLE)
+                dwConfigFlags &= ~(PropChange->Scope == DICS_FLAG_GLOBAL ? CONFIGFLAG_DISABLED : CSCONFIGFLAG_DISABLED);
+            else
+                dwConfigFlags |= (PropChange->Scope == DICS_FLAG_GLOBAL ? CONFIGFLAG_DISABLED : CSCONFIGFLAG_DISABLED);
+            rc = RegSetValueEx(
+                hKey,
+                RegistryValueName,
+                0,
+                REG_DWORD,
+                (LPBYTE)&dwConfigFlags, sizeof(DWORD));
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+
+            /* Enable/disable device if needed */
+            if (PropChange->Scope == DICS_FLAG_GLOBAL
+                || PropChange->HwProfile == 0
+                || PropChange->HwProfile == GetCurrentHwProfile(DeviceInfoSet))
+            {
+                if (PropChange->StateChange == DICS_ENABLE)
+                    ret = StartDevice();
+                else
+                    ret = StopDevice();
+            }
+            else
+                ret = TRUE;
+            break;
+        }
+        case DICS_PROPCHANGE:
+        {
+            ret = StopDevice() && StartDevice();
+            break;
+        }
+        default:
+        {
+            FIXME("Unknown StateChange 0x%lx\n", PropChange->StateChange);
+            SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+        }
+    }
+
+cleanup:
+    if (hKey != INVALID_HANDLE_VALUE)
+        RegCloseKey(hKey);
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
 /***********************************************************************
  *             SetupDiSelectBestCompatDrv (SETUPAPI.@)
  */
@@ -5601,7 +6709,7 @@ SetupDiRegisterCoDeviceInstallers(
         DWORD DoAction;
         WCHAR SectionName[MAX_PATH];
         DWORD SectionNameLength = 0;
-        HKEY hKey = INVALID_HANDLE_VALUE;;
+        HKEY hKey = INVALID_HANDLE_VALUE;
 
         InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
         Result = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
@@ -5682,6 +6790,45 @@ SetupDiInstallDeviceInterfaces(
     return TRUE;
 }
 
+BOOL
+InfIsFromOEMLocation(
+    IN PCWSTR FullName,
+    OUT LPBOOL IsOEMLocation)
+{
+    PWCHAR last;
+
+    last = strrchrW(FullName, '\\');
+    if (!last)
+    {
+        /* No directory specified */
+        *IsOEMLocation = FALSE;
+    }
+    else
+    {
+        WCHAR Windir[MAX_PATH];
+        UINT ret;
+
+        ret = GetWindowsDirectory(Windir, MAX_PATH);
+        if (ret == 0 || ret >= MAX_PATH)
+        {
+            SetLastError(ERROR_GEN_FAILURE);
+            return FALSE;
+        }
+
+        if (strncmpW(FullName, Windir, last - FullName) == 0)
+        {
+            /* The path is %SYSTEMROOT%\Inf */
+            *IsOEMLocation = FALSE;
+        }
+        else
+        {
+            /* The file is in another place */
+            *IsOEMLocation = TRUE;
+        }
+    }
+    return TRUE;
+}
+
 /***********************************************************************
  *             SetupDiInstallDevice (SETUPAPI.@)
  */
@@ -5710,6 +6857,8 @@ SetupDiInstallDevice(
     BOOL RebootRequired = FALSE;
     HKEY hKey = INVALID_HANDLE_VALUE;
     HKEY hClassKey = INVALID_HANDLE_VALUE;
+    BOOL NeedtoCopyFile;
+    LARGE_INTEGER fullVersion;
     LONG rc;
     BOOL ret = FALSE; /* Return value */
 
@@ -5822,10 +6971,11 @@ SetupDiInstallDevice(
 
     /* Write information to driver key */
     *pSectionName = UNICODE_NULL;
+    memcpy(&fullVersion, &SelectedDriver->Info.DriverVersion, sizeof(LARGE_INTEGER));
     TRACE("Write information to driver key\n");
     TRACE("DriverDate      : '%u-%u-%u'\n", DriverDate.wMonth, DriverDate.wDay, DriverDate.wYear);
     TRACE("DriverDesc      : '%S'\n", SelectedDriver->Info.Description);
-    TRACE("DriverVersion   : '%u.%u.%u.%u'\n", SelectedDriver->Info.DriverVersion & 0xff, (SelectedDriver->Info.DriverVersion >> 8) & 0xff, (SelectedDriver->Info.DriverVersion >> 16) & 0xff, (SelectedDriver->Info.DriverVersion >> 24) & 0xff);
+    TRACE("DriverVersion   : '%u.%u.%u.%u'\n", fullVersion.HighPart >> 16, fullVersion.HighPart & 0xffff, fullVersion.LowPart >> 16, fullVersion.LowPart & 0xffff);
     TRACE("InfPath         : '%S'\n", SelectedDriver->Details.InfFileName);
     TRACE("InfSection      : '%S'\n", SelectedDriver->Details.SectionName);
     TRACE("InfSectionExt   : '%S'\n", &SectionName[wcslen(SelectedDriver->Details.SectionName)]);
@@ -5839,7 +6989,7 @@ SetupDiInstallDevice(
         rc = RegSetValueEx(hKey, L"DriverDesc", 0, REG_SZ, (const BYTE *)SelectedDriver->Info.Description, (wcslen(SelectedDriver->Info.Description) + 1) * sizeof(WCHAR));
     if (rc == ERROR_SUCCESS)
     {
-        swprintf(Buffer, L"%u.%u.%u.%u", SelectedDriver->Info.DriverVersion & 0xff, (SelectedDriver->Info.DriverVersion >> 8) & 0xff, (SelectedDriver->Info.DriverVersion >> 16) & 0xff, (SelectedDriver->Info.DriverVersion >> 24) & 0xff);
+        swprintf(Buffer, L"%u.%u.%u.%u", fullVersion.HighPart >> 16, fullVersion.HighPart & 0xffff, fullVersion.LowPart >> 16, fullVersion.LowPart & 0xffff);
         rc = RegSetValueEx(hKey, L"DriverVersion", 0, REG_SZ, (const BYTE *)Buffer, (wcslen(Buffer) + 1) * sizeof(WCHAR));
     }
     if (rc == ERROR_SUCCESS)
@@ -5876,7 +7026,7 @@ SetupDiInstallDevice(
             NULL, 0,
             &RequiredSize);
         if (!Result)
-            goto nextfile;
+            goto nextservice;
         if (RequiredSize > 0)
         {
             /* We got the needed size for the buffer */
@@ -5884,7 +7034,7 @@ SetupDiInstallDevice(
             if (!ServiceName)
             {
                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-                goto nextfile;
+                goto nextservice;
             }
             Result = SetupGetStringFieldW(
                 &ContextService,
@@ -5892,7 +7042,7 @@ SetupDiInstallDevice(
                 ServiceName, RequiredSize,
                 &RequiredSize);
             if (!Result)
-                goto nextfile;
+                goto nextservice;
         }
         Result = SetupGetIntField(
             &ContextService,
@@ -5909,7 +7059,17 @@ SetupDiInstallDevice(
             NULL, 0,
             &RequiredSize);
         if (!Result)
-            goto nextfile;
+        {
+            if (GetLastError() == ERROR_INVALID_PARAMETER)
+            {
+                /* This first is probably missing. It is not
+                 * required, so ignore the error */
+                RequiredSize = 0;
+                Result = TRUE;
+            }
+            else
+                goto nextservice;
+        }
         if (RequiredSize > 0)
         {
             /* We got the needed size for the buffer */
@@ -5917,7 +7077,7 @@ SetupDiInstallDevice(
             if (!ServiceSection)
             {
                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-                goto nextfile;
+                goto nextservice;
             }
             Result = SetupGetStringFieldW(
                 &ContextService,
@@ -5925,12 +7085,13 @@ SetupDiInstallDevice(
                 ServiceSection, RequiredSize,
                 &RequiredSize);
             if (!Result)
-                goto nextfile;
+                goto nextservice;
+
+            SetLastError(ERROR_SUCCESS);
+            Result = SetupInstallServicesFromInfSectionExW(
+                SelectedDriver->InfFileDetails->hInf,
+                ServiceSection, Flags, DeviceInfoSet, DeviceInfoData, ServiceName, NULL);
         }
-        SetLastError(ERROR_SUCCESS);
-        Result = SetupInstallServicesFromInfSectionExW(
-            SelectedDriver->InfFileDetails->hInf,
-            ServiceSection, Flags, DeviceInfoSet, DeviceInfoData, ServiceName, NULL);
         if (Result && (Flags & SPSVCINST_ASSOCSERVICE))
         {
             AssociatedService = ServiceName;
@@ -5938,7 +7099,7 @@ SetupDiInstallDevice(
             if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED)
                 RebootRequired = TRUE;
         }
-nextfile:
+nextservice:
         HeapFree(GetProcessHeap(), 0, ServiceName);
         HeapFree(GetProcessHeap(), 0, ServiceSection);
         if (!Result)
@@ -5946,8 +7107,25 @@ nextfile:
         Result = SetupFindNextLine(&ContextService, &ContextService);
     }
 
-    /* Copy .inf file to Inf\ directory */
-    FIXME("FIXME: Copy .inf file to Inf\\ directory\n"); /* SetupCopyOEMInf */
+    /* Copy .inf file to Inf\ directory (if needed) */
+    Result = InfIsFromOEMLocation(SelectedDriver->InfFileDetails->FullInfFileName, &NeedtoCopyFile);
+    if (!Result)
+        goto cleanup;
+    if (NeedtoCopyFile)
+    {
+        Result = SetupCopyOEMInfW(
+            SelectedDriver->InfFileDetails->FullInfFileName,
+            NULL,
+            SPOST_NONE,
+            SP_COPY_NOOVERWRITE,
+            NULL, 0,
+            NULL,
+            NULL);
+        if (!Result)
+            goto cleanup;
+        /* FIXME: create a new struct InfFileDetails, and set it to SelectedDriver->InfFileDetails,
+         * to release use of current InfFile */
+    }
 
     /* Open device registry key */
     hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_SET_VALUE);
@@ -5966,20 +7144,20 @@ nextfile:
 
     /* Write information to enum key */
     TRACE("Write information to enum key\n");
-    TRACE("Service         : '%S'\n", AssociatedService);
     TRACE("Class           : '%S'\n", ClassName);
     TRACE("ClassGUID       : '%S'\n", lpFullGuidString);
     TRACE("DeviceDesc      : '%S'\n", SelectedDriver->Info.Description);
     TRACE("Mfg             : '%S'\n", SelectedDriver->Info.MfgName);
-    rc = RegSetValueEx(hKey, L"Service", 0, REG_SZ, (const BYTE *)AssociatedService, (wcslen(AssociatedService) + 1) * sizeof(WCHAR));
-    if (rc == ERROR_SUCCESS)
-        rc = RegSetValueEx(hKey, L"Class", 0, REG_SZ, (const BYTE *)ClassName, (wcslen(ClassName) + 1) * sizeof(WCHAR));
+    TRACE("Service         : '%S'\n", AssociatedService);
+    rc = RegSetValueEx(hKey, REGSTR_VAL_CLASS, 0, REG_SZ, (const BYTE *)ClassName, (wcslen(ClassName) + 1) * sizeof(WCHAR));
     if (rc == ERROR_SUCCESS)
-        rc = RegSetValueEx(hKey, L"ClassGUID", 0, REG_SZ, (const BYTE *)lpFullGuidString, (wcslen(lpFullGuidString) + 1) * sizeof(WCHAR));
+        rc = RegSetValueEx(hKey, REGSTR_VAL_CLASSGUID, 0, REG_SZ, (const BYTE *)lpFullGuidString, (wcslen(lpFullGuidString) + 1) * sizeof(WCHAR));
     if (rc == ERROR_SUCCESS)
-        rc = RegSetValueEx(hKey, L"DeviceDesc", 0, REG_SZ, (const BYTE *)SelectedDriver->Info.Description, (wcslen(SelectedDriver->Info.Description) + 1) * sizeof(WCHAR));
+        rc = RegSetValueEx(hKey, REGSTR_VAL_DEVDESC, 0, REG_SZ, (const BYTE *)SelectedDriver->Info.Description, (wcslen(SelectedDriver->Info.Description) + 1) * sizeof(WCHAR));
     if (rc == ERROR_SUCCESS)
-        rc = RegSetValueEx(hKey, L"Mfg", 0, REG_SZ, (const BYTE *)SelectedDriver->Info.MfgName, (wcslen(SelectedDriver->Info.MfgName) + 1) * sizeof(WCHAR));
+        rc = RegSetValueEx(hKey, REGSTR_VAL_MFG, 0, REG_SZ, (const BYTE *)SelectedDriver->Info.MfgName, (wcslen(SelectedDriver->Info.MfgName) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS && *AssociatedService)
+        rc = RegSetValueEx(hKey, REGSTR_VAL_SERVICE, 0, REG_SZ, (const BYTE *)AssociatedService, (wcslen(AssociatedService) + 1) * sizeof(WCHAR));
     if (rc != ERROR_SUCCESS)
     {
        SetLastError(rc);