HMACHINE and DEVINST are not the same thing. Fix their usage
[reactos.git] / reactos / lib / setupapi / devinst.c
index f62dc26..7669a0e 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 "windef.h"
-#include "winbase.h"
-#include "winnt.h"
-#include "winreg.h"
-#include "winternl.h"
-#include "wingdi.h"
-#include "winuser.h"
-#include "winnls.h"
-#include "setupapi.h"
-#include "wine/debug.h"
-#include "wine/unicode.h"
-#include "cfgmgr32.h"
-#include "initguid.h"
-#include "winioctl.h"
-#include "rpc.h"
-#include "rpcdce.h"
-
+#define INITGUID
 #include "setupapi_private.h"
 
-
 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
 
 /* Unicode constants */
@@ -88,133 +66,28 @@ 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);
 
 struct CoInstallerElement
 {
     LIST_ENTRY ListEntry;
 
+    HMODULE Module;
     COINSTALLER_PROC Function;
     BOOL DoPostProcessing;
     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} */
-};
-
-struct DriverInfoElement /* Element of DeviceInfoSet.DriverListHead and DeviceInfoElement.DriverListHead */
-{
-    LIST_ENTRY ListEntry;
-
-    DWORD DriverRank;
-    SP_DRVINFO_DATA_V2_W Info;
-};
-
-struct DeviceInfoElement /* Element of DeviceInfoSet.ListHead */
-{
-    LIST_ENTRY ListEntry;
-
-    /* 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)
-     * - hwndParent
-     *       Used when doing device-specific actions. Can be NULL
-     */
-    PCWSTR DeviceName;
-    PCWSTR UniqueId;
-    PCWSTR DeviceDescription;
-    GUID ClassGuid;
-    DWORD CreationFlags;
-    HWND hwndParent;
-
-    /* Flags is a combination of:
-     * - DI_DIDCOMPAT
-     *       Set when the device driver list is created
-     * FlagsEx is a combination of:
-     */
-    DWORD Flags;
-    DWORD FlagsEx;
-
-    /* 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 */
-    /* Points into DriverListHead list. The pointer is NULL if no driver is
-     * currently chosen. */
-    struct DriverInfoElement *SelectedDriver;
-
-    /* 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 */
-    HWND hwndParent; /* only used on non-device-specific actions, like as a select-device dialog using the global class driver list */
-    HKEY HKLM; /* Local or distant HKEY_LOCAL_MACHINE registry key */
-
-    /* Flags is a combination of:
-     * - DI_DIDCLASS
-     *       Set when the class driver list is created
-     * - DI_COMPAT_FROM_CLASS (FIXME: not supported)
-     *       Forces SetupDiBuildDriverInfoList to build a class drivers list
-     * FlagsEx is a combination of:
-     */
-    DWORD Flags;
-    DWORD FlagsEx;
-
-    /* If the driver is not searched/detected, this list is empty */
-    LIST_ENTRY DriverListHead; /* List of struct DriverInfoElement */
-    /* Points into DriverListHead list. The pointer is NULL if no driver is
-     * currently chosen. */
-    struct DriverInfoElement *SelectedDriver;
-
-    LIST_ENTRY ListHead; /* List of struct DeviceInfoElement */
-};
-
 /***********************************************************************
  *              SetupDiBuildClassInfoList  (SETUPAPI.@)
  */
@@ -281,13 +154,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);
@@ -310,12 +184,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);
@@ -362,14 +236,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]);
@@ -487,6 +361,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;
 
@@ -514,7 +391,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,
@@ -534,20 +411,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]);
@@ -652,6 +529,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,
@@ -722,7 +602,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)
     {
@@ -740,6 +621,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.@)
  */
@@ -750,42 +647,91 @@ 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 = sizeof(struct DeviceInfoSet);
+  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));
 
   list->magic = SETUP_DEV_INFO_SET_MAGIC;
   memcpy(
     &list->ClassGuid,
     ClassGuid ? ClassGuid : &GUID_NULL,
     sizeof(list->ClassGuid));
-  list->hwndParent = hwndParent;
+  list->InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
+  list->InstallParams.Flags |= DI_CLASSINSTALLPARAMS;
+  list->InstallParams.hwndParent = hwndParent;
   if (MachineName)
   {
     rc = RegConnectRegistryW(MachineName, HKEY_LOCAL_MACHINE, &list->HKLM);
     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;
   }
-  list->Flags = 0; /* FIXME */
-  list->FlagsEx = 0; /* FIXME */
+#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;
 }
 
 /***********************************************************************
@@ -822,12 +768,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;
             }
@@ -908,6 +849,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);
 
@@ -1068,8 +1012,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);
@@ -1187,31 +1134,40 @@ end:
 
 static BOOL
 CreateDeviceInfoElement(
+    IN struct DeviceInfoSet *list,
     IN LPCWSTR InstancePath,
-    LPCGUID pClassGuid,
+    IN LPCGUID pClassGuid,
     OUT struct DeviceInfoElement **pDeviceInfo)
 {
+    DWORD size;
+    CONFIGRET cr;
     struct DeviceInfoElement *deviceInfo;
 
     *pDeviceInfo = NULL;
-    if (IsEqualIID(&pClassGuid, &GUID_NULL)) { FIXME("Bad argument!!!"); return FALSE; }/* FIXME: remove */
 
-    deviceInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(struct DeviceInfoElement) + (wcslen(InstancePath) + 1) * sizeof(WCHAR));
+    size = sizeof(struct DeviceInfoElement) + (wcslen(InstancePath) + 1) * sizeof(WCHAR);
+    deviceInfo = HeapAlloc(GetProcessHeap(), 0, size);
     if (!deviceInfo)
     {
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         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;
     deviceInfo->UniqueId = wcsrchr(deviceInfo->Data, '\\');
     deviceInfo->DeviceDescription = NULL;
     memcpy(&deviceInfo->ClassGuid, pClassGuid, sizeof(GUID));
     deviceInfo->CreationFlags = 0;
-    deviceInfo->hwndParent = NULL;
-    deviceInfo->Flags = 0; /* FIXME */
-    deviceInfo->FlagsEx = 0; /* FIXME */
-    deviceInfo->SelectedDriver = NULL;
     InitializeListHead(&deviceInfo->DriverListHead);
     InitializeListHead(&deviceInfo->InterfaceListHead);
 
@@ -1317,6 +1273,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)
             {
@@ -1328,13 +1286,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 */
@@ -1342,7 +1301,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();
@@ -1368,7 +1327,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 */
@@ -1391,7 +1350,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)
@@ -1501,7 +1460,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);
@@ -1725,7 +1684,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);
@@ -1935,11 +1894,6 @@ BOOL WINAPI SetupDiEnumDeviceInterfaces(
                             &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;
                     }
@@ -1960,6 +1914,68 @@ BOOL WINAPI SetupDiEnumDeviceInterfaces(
     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 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);
+    }
+    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);
+    HeapFree(GetProcessHeap(), 0, list);
+    return TRUE;
+}
+
 /***********************************************************************
  *             SetupDiDestroyDeviceInfoList (SETUPAPI.@)
  */
@@ -1973,25 +1989,7 @@ BOOL WINAPI SetupDiDestroyDeviceInfoList(HDEVINFO devinfo)
         struct DeviceInfoSet *list = (struct DeviceInfoSet *)devinfo;
 
         if (list->magic == SETUP_DEV_INFO_SET_MAGIC)
-        {
-            PLIST_ENTRY ListEntry, InterfaceEntry;
-            struct DeviceInfoElement *deviceInfo;
-            while (!IsListEmpty(&list->ListHead))
-            {
-                ListEntry = RemoveHeadList(&list->ListHead);
-                deviceInfo = (struct DeviceInfoElement *)ListEntry;
-                while (!IsListEmpty(&deviceInfo->InterfaceListHead))
-                {
-                    InterfaceEntry = RemoveHeadList(&deviceInfo->InterfaceListHead);
-                    HeapFree(GetProcessHeap(), 0, InterfaceEntry);
-                }
-                HeapFree(GetProcessHeap(), 0, ListEntry);
-            }
-            if (list->HKLM != HKEY_LOCAL_MACHINE)
-                RegCloseKey(list->HKLM);
-            HeapFree(GetProcessHeap(), 0, list);
-            ret = TRUE;
-        }
+            ret = DestroyDeviceInfoSet(list);
         else
             SetLastError(ERROR_INVALID_HANDLE);
     }
@@ -2017,7 +2015,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);
 
@@ -2085,7 +2083,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);
 
@@ -2127,12 +2125,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;
@@ -2162,7 +2155,7 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
     DWORD PropertyBufferSizeW;
     PBYTE PropertyBufferW;
 
-    TRACE("%04lx %p %ld %p %p %ld %p\n", (DWORD)devinfo, DeviceInfoData,
+    TRACE("%p %p %ld %p %p %ld %p\n", devinfo, DeviceInfoData,
         Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize,
         RequiredSize);
 
@@ -2178,19 +2171,26 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
         PropertyBufferSizeW,
         &RequiredSizeW);
 
+    if (bResult || GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+    {
+        bIsStringProperty = (RegType == REG_SZ || RegType == REG_MULTI_SZ || RegType == REG_EXPAND_SZ);
+
+        if (bIsStringProperty)
+           RequiredSizeA = RequiredSizeW / sizeof(WCHAR);
+        else
+            RequiredSizeA = RequiredSizeW;
+        if (RequiredSize)
+            *RequiredSize = RequiredSizeA;
+        if (PropertyRegDataType)
+            *PropertyRegDataType = RegType;
+    }
+
     if (!bResult)
     {
         HeapFree(GetProcessHeap(), 0, PropertyBufferW);
         return bResult;
     }
 
-    bIsStringProperty = (RegType == REG_SZ || RegType == REG_MULTI_SZ || RegType == REG_EXPAND_SZ);
-
-    if (bIsStringProperty)
-        RequiredSizeA = RequiredSizeW / sizeof(WCHAR);
-    else
-        RequiredSizeA = RequiredSizeW;
-
     if (RequiredSizeA <= PropertyBufferSize)
     {
         if (bIsStringProperty && PropertyBufferSize > 0)
@@ -2211,10 +2211,6 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
     }
 
     HeapFree(GetProcessHeap(), 0, PropertyBufferW);
-    if (PropertyRegDataType)
-        *PropertyRegDataType = RegType;
-    if (RequiredSize)
-        *RequiredSize = RequiredSizeA;
     return bResult;
 }
 
@@ -2270,6 +2266,7 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
             case SPDRP_SECURITY:
             case SPDRP_SERVICE:
             case SPDRP_UI_NUMBER:
+            case SPDRP_UI_NUMBER_DESC_FORMAT:
             case SPDRP_UPPERFILTERS:
             {
                 LPCWSTR RegistryPropertyName;
@@ -2307,6 +2304,8 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
                         RegistryPropertyName = L"Service"; break;
                     case SPDRP_UI_NUMBER:
                         RegistryPropertyName = L"UINumber"; break;
+                    case SPDRP_UI_NUMBER_DESC_FORMAT:
+                        RegistryPropertyName = L"UINumberDescFormat"; break;
                     case SPDRP_UPPERFILTERS:
                         RegistryPropertyName = L"UpperFilters"; break;
                     default:
@@ -2349,10 +2348,19 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
                     &BufferSize);
                 if (RequiredSize)
                     *RequiredSize = BufferSize;
-                if (rc == ERROR_SUCCESS)
-                    ret = TRUE;
-                else
-                    SetLastError(rc);
+                switch(rc) {
+                    case ERROR_SUCCESS:
+                        if (PropertyBuffer != NULL || BufferSize == 0)
+                            ret = TRUE;
+                        else
+                            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                        break;
+                    case ERROR_MORE_DATA:
+                        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                        break;
+                    default:
+                        SetLastError(rc);
+                }
                 RegCloseKey(hKey);
                 break;
             }
@@ -2384,7 +2392,6 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
             case SPDRP_EXCLUSIVE:
             case SPDRP_CHARACTERISTICS:
             case SPDRP_ADDRESS:
-            case SPDRP_UI_NUMBER_DESC_FORMAT:
             case SPDRP_DEVICE_POWER_DATA:*/
 #if (WINVER >= 0x501)
             /*case SPDRP_REMOVAL_POLICY:
@@ -2395,7 +2402,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);
             }
         }
@@ -2405,6 +2412,154 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
     return ret;
 }
 
+/***********************************************************************
+ *             SetupDiSetDeviceRegistryPropertyA (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiSetDeviceRegistryPropertyA(
+        IN HDEVINFO DeviceInfoSet,
+        IN OUT PSP_DEVINFO_DATA DeviceInfoData,
+        IN DWORD Property,
+        IN CONST BYTE *PropertyBuffer,
+        IN DWORD PropertyBufferSize)
+{
+    FIXME("%p %p 0x%lx %p 0x%lx\n", DeviceInfoSet, DeviceInfoData,
+        Property, PropertyBuffer, PropertyBufferSize);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiSetDeviceRegistryPropertyW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiSetDeviceRegistryPropertyW(
+        IN HDEVINFO DeviceInfoSet,
+        IN OUT PSP_DEVINFO_DATA DeviceInfoData,
+        IN DWORD Property,
+        IN const BYTE *PropertyBuffer,
+        IN DWORD PropertyBufferSize)
+{
+    struct DeviceInfoSet *list;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p 0x%lx %p 0x%lx\n", DeviceInfoSet, DeviceInfoData,
+        Property, PropertyBuffer, PropertyBufferSize);
+
+    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)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else
+    {
+        switch (Property)
+        {
+            case SPDRP_COMPATIBLEIDS:
+            case SPDRP_CONFIGFLAGS:
+            case SPDRP_FRIENDLYNAME:
+            case SPDRP_HARDWAREID:
+            case SPDRP_LOCATION_INFORMATION:
+            case SPDRP_LOWERFILTERS:
+            case SPDRP_SECURITY:
+            case SPDRP_SERVICE:
+            case SPDRP_UI_NUMBER_DESC_FORMAT:
+            case SPDRP_UPPERFILTERS:
+            {
+                LPCWSTR RegistryPropertyName;
+                DWORD RegistryDataType;
+                HKEY hKey;
+                LONG rc;
+
+                switch (Property)
+                {
+                    case SPDRP_COMPATIBLEIDS:
+                        RegistryPropertyName = L"CompatibleIDs";
+                        RegistryDataType = REG_MULTI_SZ;
+                        break;
+                    case SPDRP_CONFIGFLAGS:
+                        RegistryPropertyName = L"ConfigFlags";
+                        RegistryDataType = REG_DWORD;
+                        break;
+                    case SPDRP_FRIENDLYNAME:
+                        RegistryPropertyName = L"FriendlyName";
+                        RegistryDataType = REG_SZ;
+                        break;
+                    case SPDRP_HARDWAREID:
+                        RegistryPropertyName = L"HardwareID";
+                        RegistryDataType = REG_MULTI_SZ;
+                        break;
+                    case SPDRP_LOCATION_INFORMATION:
+                        RegistryPropertyName = L"LocationInformation";
+                        RegistryDataType = REG_SZ;
+                        break;
+                    case SPDRP_LOWERFILTERS:
+                        RegistryPropertyName = L"LowerFilters";
+                        RegistryDataType = REG_MULTI_SZ;
+                        break;
+                    case SPDRP_SECURITY:
+                        RegistryPropertyName = L"Security";
+                        RegistryDataType = REG_BINARY;
+                        break;
+                    case SPDRP_SERVICE:
+                        RegistryPropertyName = L"Service";
+                        RegistryDataType = REG_SZ;
+                        break;
+                    case SPDRP_UI_NUMBER_DESC_FORMAT:
+                        RegistryPropertyName = L"UINumberDescFormat";
+                        RegistryDataType = REG_SZ;
+                        break;
+                    case SPDRP_UPPERFILTERS:
+                        RegistryPropertyName = L"UpperFilters";
+                        RegistryDataType = REG_MULTI_SZ;
+                        break;
+                    default:
+                        /* Should not happen */
+                        RegistryPropertyName = NULL;
+                        RegistryDataType = REG_BINARY;
+                        break;
+                }
+                /* Open device registry key */
+                hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_SET_VALUE);
+                if (hKey != INVALID_HANDLE_VALUE)
+                {
+                    /* Write new data */
+                    rc = RegSetValueExW(
+                        hKey,
+                        RegistryPropertyName,
+                        0, /* Reserved */
+                        RegistryDataType,
+                        PropertyBuffer,
+                        PropertyBufferSize);
+                    if (rc == ERROR_SUCCESS)
+                        ret = TRUE;
+                    else
+                        SetLastError(rc);
+                    RegCloseKey(hKey);
+                }
+                break;
+            }
+
+            /*case SPDRP_CHARACTERISTICS:
+            case SPDRP_DEVTYPE:
+            case SPDRP_EXCLUSIVE:*/
+#if (WINVER >= 0x501)
+            //case SPDRP_REMOVAL_POLICY_OVERRIDE:
+#endif
+            //case SPDRP_SECURITY_SDS:
+
+            default:
+            {
+                ERR("Property 0x%lx not implemented\n", Property);
+                SetLastError(ERROR_NOT_SUPPORTED);
+            }
+        }
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
 
 /***********************************************************************
  *             SetupDiInstallClassA (SETUPAPI.@)
@@ -2471,7 +2626,7 @@ static HKEY CreateClassKey(HINF hInf)
                            0,
                            NULL,
                            REG_OPTION_NON_VOLATILE,
-                           KEY_ALL_ACCESS,
+                           KEY_SET_VALUE,
                            NULL,
                            &hClassKey,
              NULL))
@@ -2510,6 +2665,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))
@@ -2655,6 +2813,9 @@ 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;
@@ -2666,7 +2827,7 @@ HKEY WINAPI SetupDiOpenClassRegKeyExW(
     else
     {
         ERR("Invalid Flags parameter!\n");
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(ERROR_INVALID_FLAGS);
         return INVALID_HANDLE_VALUE;
     }
 
@@ -2685,7 +2846,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)
@@ -2721,7 +2882,7 @@ HKEY WINAPI SetupDiOpenClassRegKeyExW(
     rc = RegOpenKeyExW(hClassesKey,
                      lpFullGuidString,
                      0,
-                     KEY_ALL_ACCESS,
+                     samDesired,
                      &hClassKey);
     if (rc != ERROR_SUCCESS)
     {
@@ -2760,9 +2921,21 @@ BOOL WINAPI SetupDiOpenDeviceInterfaceA(
        DWORD OpenFlags,
        PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
 {
-    FIXME("%p %s %08lx %p\n", DeviceInfoSet,
-        debugstr_a(DevicePath), OpenFlags, DeviceInterfaceData);
-    return FALSE;
+    LPWSTR DevicePathW = NULL;
+    BOOL bResult;
+
+    TRACE("%p %s %08lx %p\n", DeviceInfoSet, debugstr_a(DevicePath), OpenFlags, DeviceInterfaceData);
+
+    DevicePathW = MultiByteToUnicode(DevicePath, CP_ACP);
+    if (DevicePathW == NULL)
+        return FALSE;
+
+    bResult = SetupDiOpenDeviceInterfaceW(DeviceInfoSet,
+        DevicePathW, OpenFlags, DeviceInterfaceData);
+
+    MyFree(DevicePathW);
+
+    return bResult;
 }
 
 /***********************************************************************
@@ -2779,30 +2952,105 @@ BOOL WINAPI SetupDiSetClassInstallParamsA(
     return FALSE;
 }
 
-/***********************************************************************
- *             SetupDiCallClassInstaller (SETUPAPI.@)
- */
-BOOL WINAPI SetupDiCallClassInstaller(
-       IN DI_FUNCTION InstallFunction,
-       IN HDEVINFO DeviceInfoSet,
-       IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
+static DWORD
+GetFunctionPointer(
+        IN PWSTR InstallerName,
+        OUT HMODULE* ModulePointer,
+        OUT PVOID* FunctionPointer)
 {
-    BOOL ret = FALSE;
+    HMODULE hModule = NULL;
+    LPSTR FunctionNameA = NULL;
+    PWCHAR Comma;
+    DWORD rc;
 
-    TRACE("%ld %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData);
+    *ModulePointer = NULL;
+    *FunctionPointer = NULL;
 
-    if (!DeviceInfoSet)
-        SetLastError(ERROR_INVALID_PARAMETER);
-    else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
-        SetLastError(ERROR_INVALID_HANDLE);
-    else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
-        SetLastError(ERROR_INVALID_HANDLE);
-    else if (((struct DeviceInfoSet *)DeviceInfoSet)->HKLM != HKEY_LOCAL_MACHINE)
-        SetLastError(ERROR_INVALID_HANDLE);
-    else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
-        SetLastError(ERROR_INVALID_USER_BUFFER);
-    else
+    Comma = strchrW(InstallerName, ',');
+    if (!Comma)
     {
+        rc = ERROR_INVALID_PARAMETER;
+        goto cleanup;
+    }
+
+    /* Load library */
+    *Comma = '\0';
+    hModule = LoadLibraryW(InstallerName);
+    *Comma = ',';
+    if (!hModule)
+    {
+        rc = GetLastError();
+        goto cleanup;
+    }
+
+    /* Skip comma spaces */
+    while (*Comma == ',' || isspaceW(*Comma))
+        Comma++;
+
+    /* W->A conversion for function name */
+    FunctionNameA = UnicodeToMultiByte(Comma, CP_ACP);
+    if (!FunctionNameA)
+    {
+        rc = GetLastError();
+        goto cleanup;
+    }
+
+    /* Search function */
+    *FunctionPointer = GetProcAddress(hModule, FunctionNameA);
+    if (!*FunctionPointer)
+    {
+        rc = GetLastError();
+        goto cleanup;
+    }
+
+    *ModulePointer = hModule;
+    rc = ERROR_SUCCESS;
+
+cleanup:
+    if (rc != ERROR_SUCCESS && hModule)
+        FreeLibrary(hModule);
+    MyFree(FunctionNameA);
+    return rc;
+}
+
+static DWORD
+FreeFunctionPointer(
+        IN HMODULE ModulePointer,
+        IN PVOID FunctionPointer)
+{
+    if (ModulePointer == NULL)
+        return ERROR_SUCCESS;
+    if (FreeLibrary(ModulePointer))
+       return ERROR_SUCCESS;
+    else
+       return GetLastError();
+}
+
+/***********************************************************************
+ *             SetupDiCallClassInstaller (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiCallClassInstaller(
+       IN DI_FUNCTION InstallFunction,
+       IN HDEVINFO DeviceInfoSet,
+       IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%u %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData);
+
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (((struct DeviceInfoSet *)DeviceInfoSet)->HKLM != HKEY_LOCAL_MACHINE)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else
+    {
+        SP_DEVINSTALL_PARAMS_W InstallParams;
 #define CLASS_COINSTALLER  0x1
 #define DEVICE_COINSTALLER 0x2
 #define CLASS_INSTALLER    0x4
@@ -2847,14 +3095,20 @@ 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);
+        if (!SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams))
+            /* Don't process this call, as a parameter is invalid */
+            CanHandle = 0;
+
         if (CanHandle != 0)
         {
             LIST_ENTRY ClassCoInstallersListHead;
             LIST_ENTRY DeviceCoInstallersListHead;
+            HMODULE ClassInstallerLibrary = NULL;
             CLASS_INSTALL_PROC ClassInstaller = NULL;
             COINSTALLER_CONTEXT_DATA Context;
             PLIST_ENTRY ListEntry;
@@ -2865,11 +3119,43 @@ BOOL WINAPI SetupDiCallClassInstaller(
             InitializeListHead(&ClassCoInstallersListHead);
             InitializeListHead(&DeviceCoInstallersListHead);
 
-            if (CanHandle & CLASS_COINSTALLER)
+            if (CanHandle & DEVICE_COINSTALLER)
             {
-                FIXME("Doesn't use Class co-installers at the moment\n");
+                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);
+                    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);
+                            if (rc == ERROR_SUCCESS)
+                            {
+                                LPWSTR ptr;
+                                for (ptr = KeyBuffer; *ptr; ptr += strlenW(ptr) + 1)
+                                {
+                                    /* Add coinstaller to DeviceCoInstallersListHead list */
+                                    struct CoInstallerElement *coinstaller;
+                                    TRACE("Got device coinstaller '%s'\n", debugstr_w(ptr));
+                                    coinstaller = HeapAlloc(GetProcessHeap(), 0, sizeof(struct CoInstallerElement));
+                                    if (!coinstaller)
+                                        continue;
+                                    memset(coinstaller, 0, sizeof(struct CoInstallerElement));
+                                    if (GetFunctionPointer(ptr, &coinstaller->Module, (PVOID*)&coinstaller->Function) == ERROR_SUCCESS)
+                                        InsertTailList(&DeviceCoInstallersListHead, &coinstaller->ListEntry);
+                                    else
+                                        HeapFree(GetProcessHeap(), 0, coinstaller);
+                                }
+                            }
+                            HeapFree(GetProcessHeap(), 0, KeyBuffer);
+                        }
+                    }
+                    RegCloseKey(hKey);
+                }
             }
-            if (CanHandle & DEVICE_COINSTALLER)
+            if (CanHandle & CLASS_COINSTALLER)
             {
                 rc = RegOpenKeyEx(
                     HKEY_LOCAL_MACHINE,
@@ -2882,20 +3168,29 @@ BOOL WINAPI SetupDiCallClassInstaller(
                     LPWSTR lpGuidString;
                     if (UuidToStringW((UUID*)&DeviceInfoData->ClassGuid, &lpGuidString) == RPC_S_OK)
                     {
-                        rc = RegQueryValueExW(hKey, L"Installer32", NULL, &dwRegType, NULL, &dwLength);
-                        if (rc == ERROR_SUCCESS && dwRegType == REG_SZ)
+                        rc = RegQueryValueExW(hKey, lpGuidString, 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"Installer32", NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
+                                rc = RegQueryValueExW(hKey, lpGuidString, NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
                                 if (rc == ERROR_SUCCESS)
                                 {
-                                    LPCWSTR ptr;
+                                    LPWSTR ptr;
                                     for (ptr = KeyBuffer; *ptr; ptr += strlenW(ptr) + 1)
                                     {
-                                        /* Add coinstaller to DeviceCoInstallersListHead list */
-                                        FIXME("Device coinstaller is '%S'\n", ptr);
+                                        /* Add coinstaller to ClassCoInstallersListHead list */
+                                        struct CoInstallerElement *coinstaller;
+                                        TRACE("Got class coinstaller '%s'\n", debugstr_w(ptr));
+                                        coinstaller = HeapAlloc(GetProcessHeap(), 0, sizeof(struct CoInstallerElement));
+                                        if (!coinstaller)
+                                            continue;
+                                        memset(coinstaller, 0, sizeof(struct CoInstallerElement));
+                                        if (GetFunctionPointer(ptr, &coinstaller->Module, (PVOID*)&coinstaller->Function) == ERROR_SUCCESS)
+                                            InsertTailList(&ClassCoInstallersListHead, &coinstaller->ListEntry);
+                                        else
+                                            HeapFree(GetProcessHeap(), 0, coinstaller);
                                     }
                                 }
                                 HeapFree(GetProcessHeap(), 0, KeyBuffer);
@@ -2906,7 +3201,7 @@ BOOL WINAPI SetupDiCallClassInstaller(
                     RegCloseKey(hKey);
                 }
             }
-            if (CanHandle & CLASS_INSTALLER)
+            if ((CanHandle & CLASS_INSTALLER) && !(InstallParams.FlagsEx & DI_FLAGSEX_CI_FAILED))
             {
                 hKey = SetupDiOpenClassRegKey(&DeviceInfoData->ClassGuid, KEY_QUERY_VALUE);
                 if (hKey != INVALID_HANDLE_VALUE)
@@ -2920,8 +3215,13 @@ BOOL WINAPI SetupDiCallClassInstaller(
                             rc = RegQueryValueExW(hKey, L"Installer32", NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
                             if (rc == ERROR_SUCCESS)
                             {
-                                /* Set ClassInstaller function pointer */
-                                FIXME("Installer is '%S'\n", KeyBuffer);
+                                /* Get ClassInstaller function pointer */
+                                TRACE("Got class installer '%s'\n", debugstr_w(KeyBuffer));
+                                if (GetFunctionPointer(KeyBuffer, &ClassInstallerLibrary, (PVOID*)&ClassInstaller) != ERROR_SUCCESS)
+                                {
+                                    InstallParams.FlagsEx |= DI_FLAGSEX_CI_FAILED;
+                                    SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+                                }
                             }
                             HeapFree(GetProcessHeap(), 0, KeyBuffer);
                         }
@@ -2964,14 +3264,17 @@ BOOL WINAPI SetupDiCallClassInstaller(
 
             /* Call Class installer */
             if (ClassInstaller)
+            {
                 rc = (*ClassInstaller)(InstallFunction, DeviceInfoSet, DeviceInfoData);
+                FreeFunctionPointer(ClassInstallerLibrary, ClassInstaller);
+            }
             else
                 rc = ERROR_DI_DO_DEFAULT;
 
             /* Call default handler */
             if (rc == ERROR_DI_DO_DEFAULT)
             {
-                if (DefaultHandler /*FIXME && DI_NODI_DEFAULTACTION not set */)
+                if (DefaultHandler && !(InstallParams.Flags & DI_NODI_DEFAULTACTION))
                 {
                     if ((*DefaultHandler)(DeviceInfoSet, DeviceInfoData))
                         rc = NO_ERROR;
@@ -2994,6 +3297,7 @@ BOOL WINAPI SetupDiCallClassInstaller(
                     Context.PrivateData = coinstaller->PrivateData;
                     rc = (*coinstaller->Function)(InstallFunction, DeviceInfoSet, DeviceInfoData, &Context);
                 }
+                FreeFunctionPointer(coinstaller->Module, coinstaller->Function);
                 ListEntry = ListEntry->Flink;
             }
 
@@ -3008,6 +3312,7 @@ BOOL WINAPI SetupDiCallClassInstaller(
                     Context.PrivateData = coinstaller->PrivateData;
                     rc = (*coinstaller->Function)(InstallFunction, DeviceInfoSet, DeviceInfoData, &Context);
                 }
+                FreeFunctionPointer(coinstaller->Module, coinstaller->Function);
                 ListEntry = ListEntry->Flink;
             }
 
@@ -3031,6 +3336,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.@)
  */
@@ -3081,138 +3425,800 @@ BOOL WINAPI SetupDiGetDeviceInstallParamsW(
        IN PSP_DEVINFO_DATA DeviceInfoData,
        OUT PSP_DEVINSTALL_PARAMS_W DeviceInstallParams)
 {
-    FIXME("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams);
-    return FALSE;
+    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 Source;
+
+        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;
 }
 
 /***********************************************************************
- *             SetupDiOpenDevRegKey (SETUPAPI.@)
+ *             SetupDiSetDeviceInstallParamsW (SETUPAPI.@)
  */
-HKEY WINAPI SetupDiOpenDevRegKey(
-       HDEVINFO DeviceInfoSet,
-       PSP_DEVINFO_DATA DeviceInfoData,
-       DWORD Scope,
-       DWORD HwProfile,
-       DWORD KeyType,
-       REGSAM samDesired)
+BOOL WINAPI SetupDiSetDeviceInstallParamsW(
+       IN HDEVINFO DeviceInfoSet,
+       IN PSP_DEVINFO_DATA DeviceInfoData,
+       IN PSP_DEVINSTALL_PARAMS_W DeviceInstallParams)
 {
-    FIXME("%p %p %ld %ld %ld %lx\n", DeviceInfoSet, DeviceInfoData,
-          Scope, HwProfile, KeyType, samDesired);
-    return INVALID_HANDLE_VALUE;
+    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;
 }
 
 /***********************************************************************
- *             SetupDiCreateDeviceInfoA (SETUPAPI.@)
+ *             SetupDiGetDeviceInstanceIdA(SETUPAPI.@)
  */
-BOOL WINAPI SetupDiCreateDeviceInfoA(
-       HDEVINFO DeviceInfoSet,
-       PCSTR DeviceName,
-       CONST GUID *ClassGuid,
-       PCSTR DeviceDescription,
-       HWND hwndParent,
-       DWORD CreationFlags,
-       PSP_DEVINFO_DATA DeviceInfoData)
+BOOL WINAPI SetupDiGetDeviceInstanceIdA(
+        IN HDEVINFO DeviceInfoSet,
+        IN PSP_DEVINFO_DATA DeviceInfoData,
+        OUT PSTR DeviceInstanceId OPTIONAL,
+        IN DWORD DeviceInstanceIdSize,
+        OUT PDWORD RequiredSize OPTIONAL)
 {
-    LPWSTR DeviceNameW = NULL;
-    LPWSTR DeviceDescriptionW = NULL;
-    BOOL bResult;
+    PWSTR DeviceInstanceIdW = NULL;
+    BOOL ret = FALSE;
 
-    TRACE("\n");
+    TRACE("%p %p %p %lu %p\n", DeviceInfoSet, DeviceInfoData,
+          DeviceInstanceId, DeviceInstanceIdSize, RequiredSize);
 
-    if (DeviceName)
-    {
-        DeviceNameW = MultiByteToUnicode(DeviceName, CP_ACP);
-        if (DeviceNameW == NULL) return FALSE;
-    }
-    if (DeviceDescription)
+    if (!DeviceInstanceId && DeviceInstanceIdSize > 0)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
     {
-        DeviceDescriptionW = MultiByteToUnicode(DeviceDescription, CP_ACP);
-        if (DeviceDescriptionW == NULL)
+        if (DeviceInstanceIdSize != 0)
         {
-            if (DeviceNameW) MyFree(DeviceNameW);
-            return FALSE;
+            DeviceInstanceIdW = MyMalloc(DeviceInstanceIdSize * sizeof(WCHAR));
+            if (DeviceInstanceIdW == NULL)
+                return FALSE;
         }
-    }
 
-    bResult = SetupDiCreateDeviceInfoW(DeviceInfoSet, DeviceNameW,
-                                       ClassGuid, DeviceDescriptionW,
-                                       hwndParent, CreationFlags,
-                                       DeviceInfoData);
+        ret = SetupDiGetDeviceInstanceIdW(DeviceInfoSet, DeviceInfoData,
+                                          DeviceInstanceIdW, DeviceInstanceIdSize,
+                                          RequiredSize);
 
-    if (DeviceNameW) MyFree(DeviceNameW);
-    if (DeviceDescriptionW) MyFree(DeviceDescriptionW);
+        if (ret && DeviceInstanceIdW != NULL)
+        {
+            if (WideCharToMultiByte(CP_ACP, 0, DeviceInstanceIdW, -1,
+                DeviceInstanceId, DeviceInstanceIdSize, NULL, NULL) == 0)
+            {
+                DeviceInstanceId[0] = '\0';
+                ret = FALSE;
+            }
+        }
+    }
 
-    return bResult;
+    TRACE("Returning %d\n", ret);
+    return ret;
 }
 
 /***********************************************************************
- *             SetupDiCreateDeviceInfoW (SETUPAPI.@)
+ *             SetupDiGetDeviceInstanceIdW(SETUPAPI.@)
  */
-BOOL WINAPI SetupDiCreateDeviceInfoW(
-       HDEVINFO DeviceInfoSet,
-       PCWSTR DeviceName,
-       CONST GUID *ClassGuid,
-       PCWSTR DeviceDescription,
-       HWND hwndParent,
-       DWORD CreationFlags,
-       PSP_DEVINFO_DATA DeviceInfoData)
+BOOL WINAPI SetupDiGetDeviceInstanceIdW(
+        IN HDEVINFO DeviceInfoSet,
+        IN PSP_DEVINFO_DATA DeviceInfoData,
+        OUT PWSTR DeviceInstanceId OPTIONAL,
+        IN DWORD DeviceInstanceIdSize,
+        OUT PDWORD RequiredSize OPTIONAL)
 {
-    struct DeviceInfoSet *list;
     BOOL ret = FALSE;
 
-    TRACE("%p %S %s %S %p %lx %p\n", DeviceInfoSet, DeviceName,
-        debugstr_guid(ClassGuid), DeviceDescription,
-        hwndParent, CreationFlags, DeviceInfoData);
+    TRACE("%p %p %p %lu %p\n", DeviceInfoSet, DeviceInfoData,
+          DeviceInstanceId, DeviceInstanceIdSize, RequiredSize);
 
     if (!DeviceInfoSet)
         SetLastError(ERROR_INVALID_HANDLE);
-    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+    else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
         SetLastError(ERROR_INVALID_HANDLE);
-    else if (!ClassGuid)
+    else if (!DeviceInfoData)
         SetLastError(ERROR_INVALID_PARAMETER);
-    else if (!IsEqualIID(&list->ClassGuid, &GUID_NULL) && !IsEqualIID(&list->ClassGuid, ClassGuid))
-        SetLastError(ERROR_CLASS_MISMATCH);
-    else if (CreationFlags & ~(DICD_GENERATE_ID | DICD_INHERIT_CLASSDRVS))
-    {
-        TRACE("Unknown flags: 0x%08lx\n", CreationFlags & ~(DICD_GENERATE_ID | DICD_INHERIT_CLASSDRVS));
+    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
     {
-        SP_DEVINFO_DATA DevInfo;
+        struct DeviceInfoElement *DevInfo = (struct DeviceInfoElement *)DeviceInfoData->Reserved;
+        DWORD required;
 
-        if (CreationFlags & DICD_GENERATE_ID)
+        required = (wcslen(DevInfo->DeviceName) + 1) * sizeof(WCHAR);
+        if (RequiredSize)
+            *RequiredSize = required;
+
+        if (required <= DeviceInstanceIdSize)
         {
-            /* Generate a new unique ID for this device */
-            SetLastError(ERROR_GEN_FAILURE);
-            FIXME("not implemented\n");
+            wcscpy(DeviceInstanceId, DevInfo->DeviceName);
+            ret = TRUE;
         }
         else
-        {
-            /* Device name is fully qualified. Try to open it */
-            BOOL rc;
+            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+    }
 
-            DevInfo.cbSize = sizeof(SP_DEVINFO_DATA);
-            rc = SetupDiOpenDeviceInfoW(
-                DeviceInfoSet,
-                DeviceName,
-                NULL, /* hwndParent */
-                CreationFlags & DICD_INHERIT_CLASSDRVS ? DIOD_INHERIT_CLASSDRVS : 0,
-                &DevInfo);
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
 
-            if (rc)
-            {
-                /* SetupDiOpenDeviceInfoW has already added
-                 * the device info to the device info set
-                 */
-                SetLastError(ERROR_DEVINST_ALREADY_EXISTS);
-            }
-            else if (GetLastError() == ERROR_FILE_NOT_FOUND)
-            {
-                struct DeviceInfoElement *deviceInfo;
+/***********************************************************************
+ *             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;
 
-                /* FIXME: ClassGuid can be NULL */
-                if (CreateDeviceInfoElement(DeviceName, ClassGuid, &deviceInfo))
+    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, L"EnumPropPages32", 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, L"EnumPropPages32", 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(rc);
+            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
+        {
+            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);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDevRegKeyA (SETUPAPI.@)
+ */
+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)
+{
+    PCWSTR InfSectionNameW = NULL;
+    HKEY ret = INVALID_HANDLE_VALUE;
+
+    if (InfSectionName)
+    {
+        InfSectionNameW = MultiByteToUnicode(InfSectionName, CP_ACP);
+        if (InfSectionNameW == NULL) return INVALID_HANDLE_VALUE;
+    }
+
+    ret = SetupDiCreateDevRegKeyW(DeviceInfoSet,
+                                  DeviceInfoData,
+                                  Scope,
+                                  HwProfile,
+                                  KeyType,
+                                  InfHandle,
+                                  InfSectionNameW);
+
+    if (InfSectionNameW != NULL)
+        MyFree((PVOID)InfSectionNameW);
+
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDevRegKeyW (SETUPAPI.@)
+ */
+HKEY WINAPI SetupDiCreateDevRegKeyW(
+        IN HDEVINFO DeviceInfoSet,
+        IN PSP_DEVINFO_DATA DeviceInfoData,
+        IN DWORD Scope,
+        IN DWORD HwProfile,
+        IN DWORD KeyType,
+        IN HINF InfHandle OPTIONAL,
+        IN PCWSTR InfSectionName OPTIONAL)
+{
+    struct DeviceInfoSet *list;
+    HKEY ret = INVALID_HANDLE_VALUE;
+
+    TRACE("%p %p %lu %lu %lu %p %s\n", DeviceInfoSet, DeviceInfoData,
+          Scope, HwProfile, KeyType, InfHandle, debugstr_w(InfSectionName));
+
+    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)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (Scope != DICS_FLAG_GLOBAL && Scope != DICS_FLAG_CONFIGSPECIFIC)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (KeyType != DIREG_DEV && KeyType != DIREG_DRV)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (InfHandle && !InfSectionName)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (!InfHandle && InfSectionName)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
+    {
+        LPWSTR lpGuidString = NULL;
+        LPWSTR DriverKey = NULL; /* {GUID}\Index */
+        LPWSTR pDeviceInstance; /* Points into DriverKey, on the Index field */
+        DWORD Index; /* Index used in the DriverKey name */
+        DWORD rc;
+        HKEY hClassKey = INVALID_HANDLE_VALUE;
+        HKEY hDeviceKey = INVALID_HANDLE_VALUE;
+        HKEY hKey = INVALID_HANDLE_VALUE;
+
+        if (Scope == DICS_FLAG_CONFIGSPECIFIC)
+        {
+            FIXME("DICS_FLAG_CONFIGSPECIFIC case unimplemented\n");
+            goto cleanup;
+        }
+
+        if (KeyType == DIREG_DEV)
+        {
+            FIXME("DIREG_DEV case unimplemented\n");
+        }
+        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 */
+            DriverKey = HeapAlloc(GetProcessHeap(), 0, (wcslen(lpGuidString) + 7) * sizeof(WCHAR) + sizeof(UNICODE_STRING));
+            if (!DriverKey)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto cleanup;
+            }
+            wcscpy(DriverKey, L"{");
+            wcscat(DriverKey, lpGuidString);
+            wcscat(DriverKey, L"}\\");
+            pDeviceInstance = &DriverKey[wcslen(DriverKey)];
+            rc = RegOpenKeyExW(list->HKLM,
+                ControlClass,
+                0,
+                KEY_CREATE_SUB_KEY,
+                &hClassKey);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+
+            /* Try all values for Index between 0 and 9999 */
+            Index = 0;
+            while (Index <= 9999)
+            {
+                DWORD Disposition;
+                wsprintf(pDeviceInstance, L"%04lu", Index);
+                rc = RegCreateKeyEx(hClassKey,
+                    DriverKey,
+                    0,
+                    NULL,
+                    REG_OPTION_NON_VOLATILE,
+#if _WIN32_WINNT >= 0x502
+                    KEY_READ | KEY_WRITE,
+#else
+                    KEY_ALL_ACCESS,
+#endif
+                    NULL,
+                    &hKey,
+                    &Disposition);
+                if (rc != ERROR_SUCCESS)
+                {
+                    SetLastError(rc);
+                    goto cleanup;
+                }
+                if (Disposition == REG_CREATED_NEW_KEY)
+                    break;
+                RegCloseKey(hKey);
+                hKey = INVALID_HANDLE_VALUE;
+                Index++;
+            }
+            if (Index > 9999)
+            {
+                /* Unable to create more than 9999 devices within the same class */
+                SetLastError(ERROR_GEN_FAILURE);
+                goto cleanup;
+            }
+
+            /* Open device key, to write Driver value */
+            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));
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+        }
+
+        /* Do installation of the specified section */
+        if (InfHandle)
+        {
+            FIXME("Need to install section %s in file %p\n",
+                debugstr_w(InfSectionName), InfHandle);
+        }
+        ret = hKey;
+
+cleanup:
+        if (lpGuidString)
+            RpcStringFreeW(&lpGuidString);
+        HeapFree(GetProcessHeap(), 0, DriverKey);
+        if (hClassKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hClassKey);
+        if (hDeviceKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hDeviceKey);
+        if (hKey != INVALID_HANDLE_VALUE && hKey != ret)
+            RegCloseKey(hKey);
+    }
+
+    TRACE("Returning 0x%p\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiOpenDevRegKey (SETUPAPI.@)
+ */
+HKEY WINAPI SetupDiOpenDevRegKey(
+       HDEVINFO DeviceInfoSet,
+       PSP_DEVINFO_DATA DeviceInfoData,
+       DWORD Scope,
+       DWORD HwProfile,
+       DWORD KeyType,
+       REGSAM samDesired)
+{
+    struct DeviceInfoSet *list;
+    HKEY ret = INVALID_HANDLE_VALUE;
+
+    TRACE("%p %p %lu %lu %lu 0x%lx\n", DeviceInfoSet, DeviceInfoData,
+          Scope, HwProfile, KeyType, samDesired);
+
+    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)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (Scope != DICS_FLAG_GLOBAL && Scope != DICS_FLAG_CONFIGSPECIFIC)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (KeyType != DIREG_DEV && KeyType != DIREG_DRV)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else
+    {
+        HKEY hKey = INVALID_HANDLE_VALUE;
+        HKEY hRootKey = INVALID_HANDLE_VALUE;
+        struct DeviceInfoElement *deviceInfo = (struct DeviceInfoElement *)DeviceInfoData->Reserved;
+        LPWSTR DriverKey = NULL;
+        DWORD dwLength = 0;
+        DWORD dwRegType;
+        DWORD rc;
+
+        if (Scope == DICS_FLAG_CONFIGSPECIFIC)
+        {
+            FIXME("DICS_FLAG_CONFIGSPECIFIC case unimplemented\n");
+        }
+        else /* Scope == DICS_FLAG_GLOBAL */
+        {
+            rc = RegOpenKeyExW(
+                list->HKLM,
+                EnumKeyName,
+                0, /* Options */
+                KEY_ENUMERATE_SUB_KEYS,
+                &hRootKey);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            rc = RegOpenKeyExW(
+                hRootKey,
+                deviceInfo->DeviceName,
+                0, /* Options */
+                KeyType == DIREG_DEV ? samDesired : KEY_QUERY_VALUE,
+                &hKey);
+            RegCloseKey(hRootKey);
+            hRootKey = INVALID_HANDLE_VALUE;
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            if (KeyType == DIREG_DEV)
+            {
+                /* We're done. Just return the hKey handle */
+                ret = hKey;
+                goto cleanup;
+            }
+            /* Read the 'Driver' key */
+            rc = RegQueryValueExW(hKey, L"Driver", NULL, &dwRegType, NULL, &dwLength);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            if (dwRegType != REG_SZ)
+            {
+                SetLastError(ERROR_GEN_FAILURE);
+                goto cleanup;
+            }
+            DriverKey = HeapAlloc(GetProcessHeap(), 0, dwLength);
+            if (!DriverKey)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto cleanup;
+            }
+            rc = RegQueryValueExW(hKey, L"Driver", NULL, &dwRegType, (LPBYTE)DriverKey, &dwLength);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            RegCloseKey(hKey);
+            hKey = INVALID_HANDLE_VALUE;
+            /* Need to open the driver key */
+            rc = RegOpenKeyExW(
+                list->HKLM,
+                ControlClass,
+                0, /* Options */
+                KEY_ENUMERATE_SUB_KEYS,
+                &hRootKey);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            rc = RegOpenKeyExW(
+                hRootKey,
+                DriverKey,
+                0, /* Options */
+                samDesired,
+                &hKey);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            ret = hKey;
+        }
+cleanup:
+        if (hRootKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hRootKey);
+        if (hKey != INVALID_HANDLE_VALUE && hKey != ret)
+            RegCloseKey(hKey);
+    }
+
+    TRACE("Returning 0x%p\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDeviceInfoA (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiCreateDeviceInfoA(
+       HDEVINFO DeviceInfoSet,
+       PCSTR DeviceName,
+       CONST GUID *ClassGuid,
+       PCSTR DeviceDescription,
+       HWND hwndParent,
+       DWORD CreationFlags,
+       PSP_DEVINFO_DATA DeviceInfoData)
+{
+    LPWSTR DeviceNameW = NULL;
+    LPWSTR DeviceDescriptionW = NULL;
+    BOOL bResult;
+
+    TRACE("\n");
+
+    if (DeviceName)
+    {
+        DeviceNameW = MultiByteToUnicode(DeviceName, CP_ACP);
+        if (DeviceNameW == NULL) return FALSE;
+    }
+    if (DeviceDescription)
+    {
+        DeviceDescriptionW = MultiByteToUnicode(DeviceDescription, CP_ACP);
+        if (DeviceDescriptionW == NULL)
+        {
+            if (DeviceNameW) MyFree(DeviceNameW);
+            return FALSE;
+        }
+    }
+
+    bResult = SetupDiCreateDeviceInfoW(DeviceInfoSet, DeviceNameW,
+                                       ClassGuid, DeviceDescriptionW,
+                                       hwndParent, CreationFlags,
+                                       DeviceInfoData);
+
+    if (DeviceNameW) MyFree(DeviceNameW);
+    if (DeviceDescriptionW) MyFree(DeviceDescriptionW);
+
+    return bResult;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDeviceInfoW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiCreateDeviceInfoW(
+       HDEVINFO DeviceInfoSet,
+       PCWSTR DeviceName,
+       CONST GUID *ClassGuid,
+       PCWSTR DeviceDescription,
+       HWND hwndParent,
+       DWORD CreationFlags,
+       PSP_DEVINFO_DATA DeviceInfoData)
+{
+    struct DeviceInfoSet *list;
+    BOOL ret = FALSE;
+
+    TRACE("%p %S %s %S %p %lx %p\n", DeviceInfoSet, DeviceName,
+        debugstr_guid(ClassGuid), DeviceDescription,
+        hwndParent, CreationFlags, DeviceInfoData);
+
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (!ClassGuid)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (!IsEqualIID(&list->ClassGuid, &GUID_NULL) && !IsEqualIID(&list->ClassGuid, ClassGuid))
+        SetLastError(ERROR_CLASS_MISMATCH);
+    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_FLAGS);
+    }
+    else
+    {
+        SP_DEVINFO_DATA DevInfo;
+
+        if (CreationFlags & DICD_GENERATE_ID)
+        {
+            /* Generate a new unique ID for this device */
+            SetLastError(ERROR_GEN_FAILURE);
+            FIXME("not implemented\n");
+        }
+        else
+        {
+            /* Device name is fully qualified. Try to open it */
+            BOOL rc;
+
+            DevInfo.cbSize = sizeof(SP_DEVINFO_DATA);
+            rc = SetupDiOpenDeviceInfoW(
+                DeviceInfoSet,
+                DeviceName,
+                NULL, /* hwndParent */
+                CreationFlags & DICD_INHERIT_CLASSDRVS ? DIOD_INHERIT_CLASSDRVS : 0,
+                &DevInfo);
+
+            if (rc)
+            {
+                /* SetupDiOpenDeviceInfoW has already added
+                 * the device info to the device info set
+                 */
+                SetLastError(ERROR_DEVINST_ALREADY_EXISTS);
+            }
+            else if (GetLastError() == ERROR_FILE_NOT_FOUND)
+            {
+                struct DeviceInfoElement *deviceInfo;
+
+                if (CreateDeviceInfoElement(list, DeviceName, ClassGuid, &deviceInfo))
                 {
                     InsertTailList(&list->ListHead, &deviceInfo->ListEntry);
 
@@ -3227,7 +4233,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;
                         }
@@ -3248,48 +4254,76 @@ static BOOL
 AddDriverToList(
     IN PLIST_ENTRY DriverListHead,
     IN DWORD DriverType, /* SPDIT_CLASSDRIVER or SPDIT_COMPATDRIVER */
+    IN LPGUID ClassGuid,
     IN INFCONTEXT ContextDevice,
+    IN struct InfFileDetails *InfFileDetails,
     IN LPCWSTR InfFile,
     IN LPCWSTR ProviderName,
     IN LPCWSTR ManufacturerName,
+    IN LPCWSTR MatchingId,
     FILETIME DriverDate,
     DWORDLONG DriverVersion,
     IN DWORD Rank)
 {
-    struct DriverInfoElement *driverInfo;
+    struct DriverInfoElement *driverInfo = NULL;
+    HANDLE hFile = INVALID_HANDLE_VALUE;
     DWORD RequiredSize = 128; /* Initial buffer size */
     BOOL Result = FALSE;
     PLIST_ENTRY PreviousEntry;
-    LPWSTR DeviceDescription = NULL;
     LPWSTR InfInstallSection = NULL;
+    BOOL ret = FALSE;
 
     driverInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(struct DriverInfoElement));
     if (!driverInfo)
     {
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-        return FALSE;
-    }
-
-    SetLastError(ERROR_INSUFFICIENT_BUFFER);
-    while (!Result && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
-    {
-        HeapFree(GetProcessHeap(), 0, DeviceDescription);
-        DeviceDescription = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
-        if (!DeviceDescription)
-            return FALSE;
-        Result = SetupGetStringFieldW(
-            &ContextDevice,
-            0, /* Field index */
-            DeviceDescription, RequiredSize,
-            &RequiredSize);
+        goto cleanup;
     }
+    memset(driverInfo, 0, sizeof(struct DriverInfoElement));
+
+    driverInfo->Details.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
+    driverInfo->Details.Reserved = (ULONG_PTR)driverInfo;
+
+    /* Copy InfFileName field */
+    wcsncpy(driverInfo->Details.InfFileName, InfFile, MAX_PATH - 1);
+    driverInfo->Details.InfFileName[MAX_PATH - 1] = '\0';
+
+    /* Fill InfDate field */
+    /* FIXME: hFile = CreateFile(driverInfo->Details.InfFileName,
+        GENERIC_READ, FILE_SHARE_READ,
+        NULL, OPEN_EXISTING, 0, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        goto cleanup;
+    Result = GetFileTime(hFile, NULL, NULL, &driverInfo->Details.InfDate);
+    if (!Result)
+        goto cleanup;*/
+
+    /* Fill SectionName field */
+    Result = SetupGetStringFieldW(
+        &ContextDevice,
+        1,
+        driverInfo->Details.SectionName, LINE_LEN,
+        NULL);
     if (!Result)
+        goto cleanup;
+
+    /* Fill DrvDescription field */
+    Result = SetupGetStringFieldW(
+        &ContextDevice,
+        0, /* Field index */
+        driverInfo->Details.DrvDescription, LINE_LEN,
+        NULL);
+
+    /* Copy MatchingId information */
+    driverInfo->MatchingId = HeapAlloc(GetProcessHeap(), 0, (wcslen(MatchingId) + 1) * sizeof(WCHAR));
+    if (!driverInfo->MatchingId)
     {
-        HeapFree(GetProcessHeap(), 0, driverInfo);
-        HeapFree(GetProcessHeap(), 0, DeviceDescription);
-        return FALSE;
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        goto cleanup;
     }
+    RtlCopyMemory(driverInfo->MatchingId, MatchingId, (wcslen(MatchingId) + 1) * sizeof(WCHAR));
 
+    /* Get inf install section */
     Result = FALSE;
     RequiredSize = 128; /* Initial buffer size */
     SetLastError(ERROR_INSUFFICIENT_BUFFER);
@@ -3298,11 +4332,7 @@ AddDriverToList(
         HeapFree(GetProcessHeap(), 0, InfInstallSection);
         InfInstallSection = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
         if (!InfInstallSection)
-        {
-            HeapFree(GetProcessHeap(), 0, driverInfo);
-            HeapFree(GetProcessHeap(), 0, DeviceDescription);
-            return FALSE;
-        }
+            goto cleanup;
         Result = SetupGetStringFieldW(
             &ContextDevice,
             1, /* Field index */
@@ -3310,20 +4340,16 @@ AddDriverToList(
             &RequiredSize);
     }
     if (!Result)
-    {
-        HeapFree(GetProcessHeap(), 0, driverInfo);
-        HeapFree(GetProcessHeap(), 0, DeviceDescription);
-        HeapFree(GetProcessHeap(), 0, InfInstallSection);
-        return FALSE;
-    }
+        goto cleanup;
 
     TRACE("Adding driver '%S' [%S/%S] (Rank 0x%lx)\n",
-        DeviceDescription, InfFile, InfInstallSection, Rank);
+        driverInfo->Details.DrvDescription, InfFile, InfInstallSection, Rank);
 
     driverInfo->DriverRank = Rank;
+    memcpy(&driverInfo->ClassGuid, ClassGuid, sizeof(GUID));
     driverInfo->Info.DriverType = DriverType;
     driverInfo->Info.Reserved = (ULONG_PTR)driverInfo;
-    wcsncpy(driverInfo->Info.Description, DeviceDescription, LINE_LEN - 1);
+    wcsncpy(driverInfo->Info.Description, driverInfo->Details.DrvDescription, LINE_LEN - 1);
     driverInfo->Info.Description[LINE_LEN - 1] = '\0';
     wcsncpy(driverInfo->Info.MfgName, ManufacturerName, LINE_LEN - 1);
     driverInfo->Info.MfgName[LINE_LEN - 1] = '\0';
@@ -3336,6 +4362,8 @@ AddDriverToList(
         driverInfo->Info.ProviderName[0] = '\0';
     driverInfo->Info.DriverDate = DriverDate;
     driverInfo->Info.DriverVersion = DriverVersion;
+    ReferenceInfFile(InfFileDetails);
+    driverInfo->InfFileDetails = InfFileDetails;
 
     /* Insert current driver in driver list, according to its rank */
     PreviousEntry = DriverListHead->Flink;
@@ -3347,6 +4375,7 @@ AddDriverToList(
             InsertHeadList(PreviousEntry, &driverInfo->ListEntry);
             break;
         }
+        PreviousEntry = PreviousEntry->Flink;
     }
     if (PreviousEntry == DriverListHead)
     {
@@ -3354,9 +4383,20 @@ AddDriverToList(
         InsertTailList(DriverListHead, &driverInfo->ListEntry);
     }
 
-    HeapFree(GetProcessHeap(), 0, DeviceDescription);
+    ret = TRUE;
+
+cleanup:
+    if (!ret)
+    {
+        if (driverInfo)
+            HeapFree(GetProcessHeap(), 0, driverInfo->MatchingId);
+        HeapFree(GetProcessHeap(), 0, driverInfo);
+    }
+    if (hFile != INVALID_HANDLE_VALUE)
+        CloseHandle(hFile);
     HeapFree(GetProcessHeap(), 0, InfInstallSection);
-    return TRUE;
+
+    return ret;
 }
 
 static BOOL
@@ -3369,9 +4409,15 @@ GetVersionInformationFromInfFile(
 {
     DWORD RequiredSize;
     WCHAR guidW[MAX_GUID_STRING_LEN + 1];
+    LPWSTR DriverVer = NULL;
     LPWSTR ProviderName = NULL;
+    LPWSTR pComma; /* Points into DriverVer */
+    LPWSTR pVersion = NULL; /* Points into DriverVer */
+    SYSTEMTIME SystemTime;
     BOOL Result;
+    BOOL ret = FALSE; /* Final result */
 
+    /* Get class Guid */
     if (!SetupGetLineTextW(
         NULL, /* Context */
         hInf,
@@ -3379,28 +4425,29 @@ GetVersionInformationFromInfFile(
         guidW, sizeof(guidW),
         NULL /* Required size */))
     {
-        return FALSE;
+        goto cleanup;
     }
-
-    /* Get Provider name, driver date, and driver version */
-
     guidW[37] = '\0'; /* Replace the } by a NULL character */
     if (UuidFromStringW(&guidW[1], ClassGuid) != RPC_S_OK)
     {
-        return FALSE;
+        SetLastError(ERROR_GEN_FAILURE);
+        goto cleanup;
     }
+
+    /* Get provider name */
     Result = SetupGetLineTextW(
         NULL, /* Context */
         hInf, L"Version", L"Provider",
         NULL, 0,
         &RequiredSize);
-    if (!Result && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+    if (Result)
     {
+        /* We know know the needed buffer size */
         ProviderName = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
         if (!ProviderName)
         {
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-            return FALSE;
+            goto cleanup;
         }
         Result = SetupGetLineTextW(
             NULL, /* Context */
@@ -3408,11 +4455,66 @@ GetVersionInformationFromInfFile(
             ProviderName, RequiredSize,
             &RequiredSize);
     }
-    //FIXME: DriverDate = Version.DriverVer => invalid date = 00/00/00
-    //FIXME: DriverVersion = Version.DriverVer => invalid = 0
-
+    if (!Result)
+        goto cleanup;
     *pProviderName = ProviderName;
-    return TRUE;
+
+    /* Read the "DriverVer" value */
+    Result = SetupGetLineTextW(
+        NULL, /* Context */
+        hInf, L"Version", L"DriverVer",
+        NULL, 0,
+        &RequiredSize);
+    if (Result)
+    {
+        /* We know know the needed buffer size */
+        DriverVer = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
+        if (!DriverVer)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            goto cleanup;
+        }
+        Result = SetupGetLineTextW(
+            NULL, /* Context */
+            hInf, L"Version", L"DriverVer",
+            DriverVer, RequiredSize,
+            &RequiredSize);
+    }
+    if (!Result)
+        goto cleanup;
+
+    /* Get driver date and driver version, by analyzing the "DriverVer" value */
+    pComma = wcschr(DriverVer, ',');
+    if (pComma != NULL)
+    {
+        *pComma = UNICODE_NULL;
+        pVersion = pComma + 1;
+    }
+    /* Get driver date version. Invalid date = 00/00/00 */
+    memset(DriverDate, 0, sizeof(FILETIME));
+    if (wcslen(DriverVer) == 10
+        && (DriverVer[2] == '-' || DriverVer[2] == '/')
+        && (DriverVer[5] == '-' || DriverVer[5] == '/'))
+    {
+        memset(&SystemTime, 0, sizeof(SYSTEMTIME));
+        DriverVer[2] = DriverVer[5] = UNICODE_NULL;
+        SystemTime.wMonth = ((DriverVer[0] - '0') * 10) + DriverVer[1] - '0';
+        SystemTime.wDay  = ((DriverVer[3] - '0') * 10) + DriverVer[4] - '0';
+        SystemTime.wYear = ((DriverVer[6] - '0') * 1000) + ((DriverVer[7] - '0') * 100) + ((DriverVer[8] - '0') * 10) + DriverVer[9] - '0';
+        SystemTimeToFileTime(&SystemTime, DriverDate);
+    }
+    /* Get driver version. Invalid version = 0.0.0.0 */
+    *DriverVersion = 0;
+    /* FIXME: use pVersion to fill DriverVersion variable */
+
+    ret = TRUE;
+
+cleanup:
+    if (!ret)
+        HeapFree(GetProcessHeap(), 0, ProviderName);
+    HeapFree(GetProcessHeap(), 0, DriverVer);
+
+    return ret;
 }
 
 /***********************************************************************
@@ -3425,15 +4527,17 @@ SetupDiBuildDriverInfoList(
     IN DWORD DriverType)
 {
     struct DeviceInfoSet *list;
+    SP_DEVINSTALL_PARAMS_W InstallParams;
     PVOID Buffer = NULL;
-    HINF hInf = INVALID_HANDLE_VALUE;
+    struct InfFileDetails *currentInfFileDetails = NULL;
     LPWSTR ProviderName = NULL;
     LPWSTR ManufacturerName = NULL;
-    LPWSTR ManufacturerSection = NULL;
+    WCHAR ManufacturerSection[LINE_LEN + 1];
     LPWSTR HardwareIDs = NULL;
     LPWSTR CompatibleIDs = NULL;
+    LPWSTR FullInfFileName = NULL;
     FILETIME DriverDate;
-    DWORDLONG DriverVersion;
+    DWORDLONG DriverVersion = 0;
     DWORD RequiredSize;
     BOOL ret = FALSE;
 
@@ -3455,7 +4559,12 @@ SetupDiBuildDriverInfoList(
         SetLastError(ERROR_INVALID_USER_BUFFER);
     else
     {
-        BOOL Result = FALSE;
+        BOOL Result;
+
+        InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+        Result = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+        if (!Result)
+            goto done;
 
         if (DriverType == SPDIT_COMPATDRIVER)
         {
@@ -3532,34 +4641,84 @@ SetupDiBuildDriverInfoList(
                 break;
             }
             Result = SetupGetInfFileListW(
-                NULL, /* Directory path */
+                *InstallParams.DriverPath ? InstallParams.DriverPath : NULL,
                 INF_STYLE_WIN4,
                 Buffer, RequiredSize,
                 &RequiredSize);
         }
+        if (!Result && GetLastError() == ERROR_FILE_NOT_FOUND)
+        {
+            /* No .inf file in specified directory. So, we should
+             * success as we created an empty driver info list.
+             */
+            ret = TRUE;
+            goto done;
+        }
         if (Result)
         {
             LPCWSTR filename;
+            LPWSTR pFullFilename;
+
+            if (*InstallParams.DriverPath)
+            {
+                DWORD len;
+                len = GetFullPathNameW(InstallParams.DriverPath, 0, NULL, NULL);
+                if (len == 0)
+                    goto done;
+                FullInfFileName = HeapAlloc(GetProcessHeap(), 0, len + MAX_PATH);
+                if (!FullInfFileName)
+                    goto done;
+                len = GetFullPathNameW(InstallParams.DriverPath, len, FullInfFileName, NULL);
+                if (len == 0)
+                    goto done;
+                if (*FullInfFileName && FullInfFileName[wcslen(FullInfFileName) - 1] != '\\')
+                    wcscat(FullInfFileName, L"\\");
+                pFullFilename = &FullInfFileName[wcslen(FullInfFileName)];
+            }
+            else
+            {
+                FullInfFileName = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
+                if (!FullInfFileName)
+                    goto done;
+                pFullFilename = &FullInfFileName[0];
+            }
 
             for (filename = (LPCWSTR)Buffer; *filename; filename += wcslen(filename) + 1)
             {
                 INFCONTEXT ContextManufacturer, ContextDevice;
                 GUID ClassGuid;
-                TRACE("Opening file %S\n", filename);
 
-                hInf = SetupOpenInfFileW(filename, NULL, INF_STYLE_WIN4, NULL);
-                if (hInf == INVALID_HANDLE_VALUE)
+                wcscpy(pFullFilename, filename);
+                TRACE("Opening file %S\n", FullInfFileName);
+
+                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);
+                if (currentInfFileDetails->hInf == INVALID_HANDLE_VALUE)
+                {
+                    HeapFree(GetProcessHeap(), 0, currentInfFileDetails);
+                    currentInfFileDetails = NULL;
                     continue;
+                }
 
                 if (!GetVersionInformationFromInfFile(
-                    hInf,
+                    currentInfFileDetails->hInf,
                     &ClassGuid,
                     &ProviderName,
                     &DriverDate,
                     &DriverVersion))
                 {
-                    SetupCloseInfFile(hInf);
-                    hInf = INVALID_HANDLE_VALUE;
+                    SetupCloseInfFile(currentInfFileDetails->hInf);
+                    HeapFree(GetProcessHeap(), 0, currentInfFileDetails->hInf);
+                    currentInfFileDetails = NULL;
                     continue;
                 }
 
@@ -3568,14 +4727,12 @@ SetupDiBuildDriverInfoList(
                     /* Check if the ClassGuid in this .inf file is corresponding with our needs */
                     if (!IsEqualIID(&list->ClassGuid, &GUID_NULL) && !IsEqualIID(&list->ClassGuid, &ClassGuid))
                     {
-                        SetupCloseInfFile(hInf);
-                        hInf = INVALID_HANDLE_VALUE;
-                        continue;
+                        goto next;
                     }
                 }
 
                 /* Get the manufacturers list */
-                Result = SetupFindFirstLineW(hInf, L"Manufacturer", NULL, &ContextManufacturer);
+                Result = SetupFindFirstLineW(currentInfFileDetails->hInf, L"Manufacturer", NULL, &ContextManufacturer);
                 while (Result)
                 {
                     Result = SetupGetStringFieldW(
@@ -3598,29 +4755,24 @@ SetupDiBuildDriverInfoList(
                             ManufacturerName, RequiredSize,
                             &RequiredSize);
                     }
+                    /* Get manufacturer section name */
                     Result = SetupGetStringFieldW(
                         &ContextManufacturer,
                         1, /* Field index */
-                        NULL, 0,
+                        ManufacturerSection, LINE_LEN,
                         &RequiredSize);
                     if (Result)
                     {
-                        /* We got the needed size for the buffer */
-                        ManufacturerSection = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
-                        if (!ManufacturerSection)
+                        ManufacturerSection[RequiredSize] = 0; /* Final NULL char */
+                        /* Add (possible) extension to manufacturer section name */
+                        Result = SetupDiGetActualSectionToInstallW(
+                            currentInfFileDetails->hInf, ManufacturerSection, ManufacturerSection, LINE_LEN, NULL, NULL);
+                        if (Result)
                         {
-                            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-                            goto done;
+                            TRACE("Enumerating devices in manufacturer %S\n", ManufacturerSection);
+                            Result = SetupFindFirstLineW(currentInfFileDetails->hInf, ManufacturerSection, NULL, &ContextDevice);
                         }
-                        Result = SetupGetStringFieldW(
-                            &ContextManufacturer,
-                            1, /* Field index */
-                            ManufacturerSection, RequiredSize,
-                            &RequiredSize);
                     }
-
-                    TRACE("Enumerating devices in manufacturer %S\n", ManufacturerSection);
-                    Result = SetupFindFirstLineW(hInf, ManufacturerSection, NULL, &ContextDevice);
                     while (Result)
                     {
                         if (DriverType == SPDIT_CLASSDRIVER)
@@ -3629,10 +4781,13 @@ SetupDiBuildDriverInfoList(
                             if (!AddDriverToList(
                                 &list->DriverListHead,
                                 DriverType,
+                                &ClassGuid,
                                 ContextDevice,
+                                currentInfFileDetails,
                                 filename,
                                 ProviderName,
                                 ManufacturerName,
+                                NULL,
                                 DriverDate, DriverVersion,
                                 0))
                             {
@@ -3677,15 +4832,18 @@ 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,
                                             DriverType,
+                                            &ClassGuid,
                                             ContextDevice,
+                                            currentInfFileDetails,
                                             filename,
                                             ProviderName,
                                             ManufacturerName,
+                                            currentId,
                                             DriverDate, DriverVersion,
                                             DriverRank  + (i == 2 ? 0 : 0x1000 + i - 3));
                                         DriverAlreadyAdded = TRUE;
@@ -3695,15 +4853,18 @@ 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,
                                                 DriverType,
+                                                &ClassGuid,
                                                 ContextDevice,
+                                                currentInfFileDetails,
                                                 filename,
                                                 ProviderName,
                                                 ManufacturerName,
+                                                currentId,
                                                 DriverDate, DriverVersion,
                                                 DriverRank + (i == 2 ? 0x2000 : 0x3000 + i - 3));
                                             DriverAlreadyAdded = TRUE;
@@ -3717,16 +4878,17 @@ SetupDiBuildDriverInfoList(
                     }
 
                     HeapFree(GetProcessHeap(), 0, ManufacturerName);
-                    HeapFree(GetProcessHeap(), 0, ManufacturerSection);
-                    ManufacturerName = ManufacturerSection = NULL;
+                    ManufacturerName = NULL;
                     Result = SetupFindNextLine(&ContextManufacturer, &ContextManufacturer);
                 }
+
+                ret = TRUE;
+next:
                 HeapFree(GetProcessHeap(), 0, ProviderName);
                 ProviderName = NULL;
-                ret = TRUE;
 
-                SetupCloseInfFile(hInf);
-                hInf = INVALID_HANDLE_VALUE;
+                DereferenceInfFile(currentInfFileDetails);
+                currentInfFileDetails = NULL;
             }
             ret = TRUE;
         }
@@ -3737,20 +4899,24 @@ done:
     {
         if (DeviceInfoData)
         {
-            struct DeviceInfoElement *deviceInfo = (struct DeviceInfoElement *)DeviceInfoData;
-            deviceInfo->Flags |= DI_DIDCOMPAT;
+            InstallParams.Flags |= DI_DIDCOMPAT;
+            InstallParams.FlagsEx |= DI_FLAGSEX_DIDCOMPATINFO;
         }
         else
-            list->Flags |= DI_DIDCLASS;
+        {
+            InstallParams.Flags |= DI_DIDCLASS;
+            InstallParams.FlagsEx |= DI_FLAGSEX_DIDINFOLIST;
+        }
+        ret = SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
     }
 
     HeapFree(GetProcessHeap(), 0, ProviderName);
     HeapFree(GetProcessHeap(), 0, ManufacturerName);
-    HeapFree(GetProcessHeap(), 0, ManufacturerSection);
     HeapFree(GetProcessHeap(), 0, HardwareIDs);
     HeapFree(GetProcessHeap(), 0, CompatibleIDs);
-    if (hInf != INVALID_HANDLE_VALUE)
-        SetupCloseInfFile(hInf);
+    HeapFree(GetProcessHeap(), 0, FullInfFileName);
+    if (currentInfFileDetails)
+        DereferenceInfFile(currentInfFileDetails);
     HeapFree(GetProcessHeap(), 0, Buffer);
 
     TRACE("Returning %d\n", ret);
@@ -3782,11 +4948,78 @@ SetupDiDestroyDriverInfoList(
     IN PSP_DEVINFO_DATA DeviceInfoData,
     IN DWORD DriverType)
 {
+    struct DeviceInfoSet *list;
+    BOOL ret = FALSE;
+
     TRACE("%p %p 0x%lx\n", DeviceInfoSet, DeviceInfoData, DriverType);
 
-    FIXME("not implemented\n");
-    SetLastError(ERROR_GEN_FAILURE);
-    return FALSE;
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (DriverType != SPDIT_CLASSDRIVER && DriverType != SPDIT_COMPATDRIVER)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DriverType == SPDIT_COMPATDRIVER && !DeviceInfoData)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else
+    {
+        PLIST_ENTRY ListEntry;
+        struct DriverInfoElement *driverInfo;
+        SP_DEVINSTALL_PARAMS_W InstallParams;
+
+        InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
+        if (!SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams))
+            goto done;
+
+        if (!DeviceInfoData)
+            /* Fall back to destroying class driver list */
+            DriverType = SPDIT_CLASSDRIVER;
+
+        if (DriverType == SPDIT_CLASSDRIVER)
+        {
+            while (!IsListEmpty(&list->DriverListHead))
+            {
+                 ListEntry = RemoveHeadList(&list->DriverListHead);
+                 driverInfo = (struct DriverInfoElement *)ListEntry;
+                 DestroyDriverInfoElement(driverInfo);
+            }
+            InstallParams.Reserved = 0;
+            InstallParams.Flags &= ~(DI_DIDCLASS | DI_MULTMFGS);
+            InstallParams.FlagsEx &= ~DI_FLAGSEX_DIDINFOLIST;
+            ret = SetupDiSetDeviceInstallParamsW(DeviceInfoSet, NULL, &InstallParams);
+        }
+        else
+        {
+            SP_DEVINSTALL_PARAMS_W InstallParamsSet;
+            struct DeviceInfoElement *deviceInfo;
+
+            InstallParamsSet.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
+            if (!SetupDiGetDeviceInstallParamsW(DeviceInfoSet, NULL, &InstallParamsSet))
+                goto done;
+            deviceInfo = (struct DeviceInfoElement *)DeviceInfoData->Reserved;
+            while (!IsListEmpty(&deviceInfo->DriverListHead))
+            {
+                 ListEntry = RemoveHeadList(&deviceInfo->DriverListHead);
+                 driverInfo = (struct DriverInfoElement *)ListEntry;
+                 if ((PVOID)InstallParamsSet.Reserved == driverInfo)
+                 {
+                     InstallParamsSet.Reserved = 0;
+                     SetupDiSetDeviceInstallParamsW(DeviceInfoSet, NULL, &InstallParamsSet);
+                 }
+                 DestroyDriverInfoElement(driverInfo);
+            }
+            InstallParams.Reserved = 0;
+            InstallParams.Flags &= ~DI_DIDCOMPAT;
+            InstallParams.FlagsEx &= ~DI_FLAGSEX_DIDCOMPATINFO;
+            ret = SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+        }
+    }
+
+done:
+    TRACE("Returning %d\n", ret);
+    return ret;
 }
 
 
@@ -3849,7 +5082,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);
@@ -3898,12 +5131,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;
@@ -3917,7 +5154,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;
         }
     }
@@ -4056,6 +5293,67 @@ SetupDiEnumDriverInfoW(
     return ret;
 }
 
+
+/***********************************************************************
+ *             SetupDiGetSelectedDriverA (SETUPAPI.@)
+ */
+BOOL WINAPI
+SetupDiGetSelectedDriverA(
+    IN HDEVINFO DeviceInfoSet,
+    IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+    OUT PSP_DRVINFO_DATA_A DriverInfoData)
+{
+    SP_DRVINFO_DATA_V2_W driverInfoData2W;
+    BOOL ret = FALSE;
+
+    if (DriverInfoData == NULL)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA_V1_A) && DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA_V2_A))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else
+    {
+        driverInfoData2W.cbSize = sizeof(SP_DRVINFO_DATA_V2_W);
+
+        ret = SetupDiGetSelectedDriverW(DeviceInfoSet,
+                                        DeviceInfoData,
+                                        &driverInfoData2W);
+
+        if (ret)
+        {
+            /* Do W->A conversion */
+            DriverInfoData->DriverType = driverInfoData2W.DriverType;
+            DriverInfoData->Reserved = driverInfoData2W.Reserved;
+            if (WideCharToMultiByte(CP_ACP, 0, driverInfoData2W.Description, -1,
+                DriverInfoData->Description, LINE_LEN, NULL, NULL) == 0)
+            {
+                DriverInfoData->Description[0] = '\0';
+                ret = FALSE;
+            }
+            if (WideCharToMultiByte(CP_ACP, 0, driverInfoData2W.MfgName, -1,
+                DriverInfoData->MfgName, LINE_LEN, NULL, NULL) == 0)
+            {
+                DriverInfoData->MfgName[0] = '\0';
+                ret = FALSE;
+            }
+            if (WideCharToMultiByte(CP_ACP, 0, driverInfoData2W.ProviderName, -1,
+                DriverInfoData->ProviderName, LINE_LEN, NULL, NULL) == 0)
+            {
+                DriverInfoData->ProviderName[0] = '\0';
+                ret = FALSE;
+            }
+            if (DriverInfoData->cbSize == sizeof(SP_DRVINFO_DATA_V2_A))
+            {
+                /* Copy more fields */
+                DriverInfoData->DriverDate = driverInfoData2W.DriverDate;
+                DriverInfoData->DriverVersion = driverInfoData2W.DriverVersion;
+            }
+        }
+    }
+
+    return ret;
+}
+
+
 /***********************************************************************
  *             SetupDiGetSelectedDriverW (SETUPAPI.@)
  */
@@ -4081,41 +5379,398 @@ SetupDiGetSelectedDriverW(
         SetLastError(ERROR_INVALID_USER_BUFFER);
     else
     {
-        struct DriverInfoElement *driverInfo;
-        
+        SP_DEVINSTALL_PARAMS InstallParams;
+
+        InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+        if (SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams))
+        {
+            struct DriverInfoElement *driverInfo;
+            driverInfo = (struct DriverInfoElement *)InstallParams.Reserved;
+            if (driverInfo == NULL)
+                SetLastError(ERROR_NO_DRIVER_SELECTED);
+            else
+            {
+                memcpy(
+                    &DriverInfoData->DriverType,
+                    &driverInfo->Info.DriverType,
+                    DriverInfoData->cbSize - FIELD_OFFSET(SP_DRVINFO_DATA_W, DriverType));
+                ret = TRUE;
+            }
+        }
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+
+/***********************************************************************
+ *             SetupDiSetSelectedDriverA (SETUPAPI.@)
+ */
+BOOL WINAPI
+SetupDiSetSelectedDriverA(
+    IN HDEVINFO DeviceInfoSet,
+    IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+    IN OUT PSP_DRVINFO_DATA_A DriverInfoData OPTIONAL)
+{
+    SP_DRVINFO_DATA_V1_W DriverInfoDataW;
+    PSP_DRVINFO_DATA_W pDriverInfoDataW = NULL;
+    BOOL ret = FALSE;
+
+    if (DriverInfoData != NULL)
+    {
+        if (DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA_V2_A) &&
+            DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA_V1_A));
+        {
+            SetLastError(ERROR_INVALID_PARAMETER);
+            return FALSE;
+        }
+
+        DriverInfoDataW.cbSize = sizeof(SP_DRVINFO_DATA_V1_W);
+        DriverInfoDataW.Reserved = DriverInfoData->Reserved;
+
+        if (DriverInfoDataW.Reserved == 0)
+        {
+            DriverInfoDataW.DriverType = DriverInfoData->DriverType;
+
+            /* convert the strings to unicode */
+            if (!MultiByteToWideChar(CP_ACP,
+                                     0,
+                                     DriverInfoData->Description,
+                                     LINE_LEN,
+                                     DriverInfoDataW.Description,
+                                     LINE_LEN) ||
+                !MultiByteToWideChar(CP_ACP,
+                                     0,
+                                     DriverInfoData->ProviderName,
+                                     LINE_LEN,
+                                     DriverInfoDataW.ProviderName,
+                                     LINE_LEN))
+            {
+                return FALSE;
+            }
+        }
+
+        pDriverInfoDataW = (PSP_DRVINFO_DATA_W)&DriverInfoDataW;
+    }
+
+    ret = SetupDiSetSelectedDriverW(DeviceInfoSet,
+                                    DeviceInfoData,
+                                    pDriverInfoDataW);
+
+    if (ret && pDriverInfoDataW != NULL)
+    {
+        DriverInfoData->Reserved = DriverInfoDataW.Reserved;
+    }
+
+    return ret;
+}
+
+
+/***********************************************************************
+ *             SetupDiSetSelectedDriverW (SETUPAPI.@)
+ */
+BOOL WINAPI
+SetupDiSetSelectedDriverW(
+    IN HDEVINFO DeviceInfoSet,
+    IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+    IN OUT PSP_DRVINFO_DATA_W DriverInfoData OPTIONAL)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DriverInfoData);
+
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (((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 (DriverInfoData && DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA_V1_W) && DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA_V2_W))
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+    else
+    {
+        struct DriverInfoElement **pDriverInfo;
+        PLIST_ENTRY ListHead, ItemList;
+
         if (DeviceInfoData)
-            driverInfo = ((struct DeviceInfoElement *)DeviceInfoData->Reserved)->SelectedDriver;
+        {
+            pDriverInfo = (struct DriverInfoElement **)&((struct DeviceInfoElement *)DeviceInfoData->Reserved)->InstallParams.Reserved;
+            ListHead = &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->DriverListHead;
+        }
         else
-            driverInfo = ((struct DeviceInfoSet *)DeviceInfoSet)->SelectedDriver;
+        {
+            pDriverInfo = (struct DriverInfoElement **)&((struct DeviceInfoSet *)DeviceInfoSet)->InstallParams.Reserved;
+            ListHead = &((struct DeviceInfoSet *)DeviceInfoSet)->DriverListHead;
+        }
 
-        if (driverInfo == NULL)
-            SetLastError(ERROR_NO_DRIVER_SELECTED);
-        else
+        if (!DriverInfoData)
         {
-            memcpy(
-                &DriverInfoData->DriverType,
-                &driverInfo->Info.DriverType,
-                DriverInfoData->cbSize - FIELD_OFFSET(SP_DRVINFO_DATA_W, DriverType));
+            *pDriverInfo = NULL;
             ret = TRUE;
         }
+        else
+        {
+            /* Search selected driver in list */
+            ItemList = ListHead->Flink;
+            while (ItemList != ListHead)
+            {
+                if (DriverInfoData->Reserved != 0)
+                {
+                    if (DriverInfoData->Reserved == (ULONG_PTR)ItemList)
+                        break;
+                }
+                else
+                {
+                    /* The caller wants to compare only DriverType, Description and ProviderName fields */
+                    struct DriverInfoElement *driverInfo = (struct DriverInfoElement *)ItemList;
+                    if (driverInfo->Info.DriverType == DriverInfoData->DriverType
+                        && wcscmp(driverInfo->Info.Description, DriverInfoData->Description) == 0
+                        && wcscmp(driverInfo->Info.ProviderName, DriverInfoData->ProviderName) == 0)
+                    {
+                        break;
+                    }
+                }
+            }
+            if (ItemList == ListHead)
+                SetLastError(ERROR_INVALID_PARAMETER);
+            else
+            {
+                *pDriverInfo = (struct DriverInfoElement *)ItemList;
+                DriverInfoData->Reserved = (ULONG_PTR)ItemList;
+                ret = TRUE;
+                TRACE("Choosing driver whose rank is 0x%lx\n",
+                    ((struct DriverInfoElement *)ItemList)->DriverRank);
+                if (DeviceInfoData)
+                    memcpy(&DeviceInfoData->ClassGuid, &(*pDriverInfo)->ClassGuid, sizeof(GUID));
+            }
+        }
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiGetDriverInfoDetailA (SETUPAPI.@)
+ */
+BOOL WINAPI
+SetupDiGetDriverInfoDetailA(
+    IN HDEVINFO DeviceInfoSet,
+    IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+    IN PSP_DRVINFO_DATA_A DriverInfoData,
+    OUT PSP_DRVINFO_DETAIL_DATA_A DriverInfoDetailData OPTIONAL,
+    IN DWORD DriverInfoDetailDataSize,
+    OUT PDWORD RequiredSize OPTIONAL)
+{
+    SP_DRVINFO_DATA_V2_W DriverInfoDataW;
+    PSP_DRVINFO_DETAIL_DATA_W DriverInfoDetailDataW = NULL;
+    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) ||
+        (DriverInfoDetailData != NULL &&
+         (DriverInfoDetailDataSize < FIELD_OFFSET(SP_DRVINFO_DETAIL_DATA_A, HardwareID) + sizeof(CHAR) ||
+          DriverInfoDetailData->cbSize != sizeof(SP_DRVINFO_DETAIL_DATA_A))))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        goto Cleanup;
+    }
+
+    /* make sure we support both versions of the SP_DRVINFO_DATA structure */
+    if (DriverInfoData->cbSize == sizeof(SP_DRVINFO_DATA_V1_A))
+    {
+        DriverInfoDataW.cbSize = sizeof(SP_DRVINFO_DATA_V1_W);
+    }
+    else if (DriverInfoData->cbSize == sizeof(SP_DRVINFO_DATA_V2_A))
+    {
+        DriverInfoDataW.cbSize = sizeof(SP_DRVINFO_DATA_V2_W);
+    }
+    else
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        goto Cleanup;
+    }
+    DriverInfoDataW.DriverType = DriverInfoData->DriverType;
+    DriverInfoDataW.Reserved = DriverInfoData->Reserved;
+    
+    /* convert the strings to unicode */
+    if (MultiByteToWideChar(CP_ACP,
+                            0,
+                            DriverInfoData->Description,
+                            LINE_LEN,
+                            DriverInfoDataW.Description,
+                            LINE_LEN) &&
+        MultiByteToWideChar(CP_ACP,
+                            0,
+                            DriverInfoData->MfgName,
+                            LINE_LEN,
+                            DriverInfoDataW.MfgName,
+                            LINE_LEN) &&
+        MultiByteToWideChar(CP_ACP,
+                            0,
+                            DriverInfoData->ProviderName,
+                            LINE_LEN,
+                            DriverInfoDataW.ProviderName,
+                            LINE_LEN))
+    {
+        if (DriverInfoDataW.cbSize == sizeof(SP_DRVINFO_DATA_V2_W))
+        {
+            DriverInfoDataW.DriverDate = ((PSP_DRVINFO_DATA_V2_A)DriverInfoData)->DriverDate;
+            DriverInfoDataW.DriverVersion = ((PSP_DRVINFO_DATA_V2_A)DriverInfoData)->DriverVersion;
+        }
+
+        if (DriverInfoDetailData != NULL)
+        {
+            /* calculate the unicode buffer size from the ansi buffer size */
+            HardwareIDLen = DriverInfoDetailDataSize - FIELD_OFFSET(SP_DRVINFO_DETAIL_DATA_A, HardwareID);
+            BufSize = FIELD_OFFSET(SP_DRVINFO_DETAIL_DATA_W, HardwareID) +
+                      (HardwareIDLen * sizeof(WCHAR));
+
+            DriverInfoDetailDataW = MyMalloc(BufSize);
+            if (DriverInfoDetailDataW == NULL)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto Cleanup;
+            }
+
+            /* initialize the buffer */
+            ZeroMemory(DriverInfoDetailDataW,
+                       BufSize);
+            DriverInfoDetailDataW->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_W);
+        }
+
+        /* call the unicode version */
+        ret = SetupDiGetDriverInfoDetailW(DeviceInfoSet,
+                                          DeviceInfoData,
+                                          &DriverInfoDataW,
+                                          DriverInfoDetailDataW,
+                                          BufSize,
+                                          RequiredSize);
+
+        if (ret)
+        {
+            if (DriverInfoDetailDataW != NULL)
+            {
+                /* convert the SP_DRVINFO_DETAIL_DATA_W structure to ansi */
+                DriverInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_A);
+                DriverInfoDetailData->InfDate = DriverInfoDetailDataW->InfDate;
+                DriverInfoDetailData->Reserved = DriverInfoDetailDataW->Reserved;
+                if (WideCharToMultiByte(CP_ACP,
+                                        0,
+                                        DriverInfoDetailDataW->SectionName,
+                                        LINE_LEN,
+                                        DriverInfoDetailData->SectionName,
+                                        LINE_LEN,
+                                        NULL,
+                                        NULL) &&
+                    WideCharToMultiByte(CP_ACP,
+                                        0,
+                                        DriverInfoDetailDataW->InfFileName,
+                                        MAX_PATH,
+                                        DriverInfoDetailData->InfFileName,
+                                        MAX_PATH,
+                                        NULL,
+                                        NULL) &&
+                    WideCharToMultiByte(CP_ACP,
+                                        0,
+                                        DriverInfoDetailDataW->DrvDescription,
+                                        LINE_LEN,
+                                        DriverInfoDetailData->DrvDescription,
+                                        LINE_LEN,
+                                        NULL,
+                                        NULL) &&
+                    WideCharToMultiByte(CP_ACP,
+                                        0,
+                                        DriverInfoDetailDataW->HardwareID,
+                                        HardwareIDLen,
+                                        DriverInfoDetailData->HardwareID,
+                                        HardwareIDLen,
+                                        NULL,
+                                        NULL))
+                {
+                    DWORD len, cnt = 0;
+                    DWORD hwidlen = HardwareIDLen;
+                    CHAR *s = DriverInfoDetailData->HardwareID;
+
+                    /* count the strings in the list */
+                    while (*s != '\0')
+                    {
+                        len = lstrlenA(s) + 1;
+                        if (hwidlen > len)
+                        {
+                            cnt++;
+                            s += len;
+                            hwidlen -= len;
+                        }
+                        else
+                        {
+                            /* looks like the string list wasn't terminated... */
+                            SetLastError(ERROR_INVALID_USER_BUFFER);
+                            ret = FALSE;
+                            break;
+                        }
+                    }
+
+                    /* make sure CompatIDsOffset points to the second string in the
+                       list, if present */
+                    if (cnt > 1)
+                    {
+                        DriverInfoDetailData->CompatIDsOffset = lstrlenA(DriverInfoDetailData->HardwareID) + 1;
+                        DriverInfoDetailData->CompatIDsLength = (DWORD)(s - DriverInfoDetailData->HardwareID) -
+                                                                DriverInfoDetailData->CompatIDsOffset + 1;
+                    }
+                    else
+                    {
+                        DriverInfoDetailData->CompatIDsOffset = 0;
+                        DriverInfoDetailData->CompatIDsLength = 0;
+                    }
+                }
+                else
+                {
+                    ret = FALSE;
+                }
+            }
+
+            if (RequiredSize != NULL)
+            {
+                *RequiredSize = FIELD_OFFSET(SP_DRVINFO_DETAIL_DATA_A, HardwareID) +
+                                (((*RequiredSize) - FIELD_OFFSET(SP_DRVINFO_DETAIL_DATA_W, HardwareID)) / sizeof(WCHAR));
+            }
+        }
+    }
+    
+Cleanup:
+    if (DriverInfoDetailDataW != NULL)
+    {
+        MyFree(DriverInfoDetailDataW);
     }
 
-    TRACE("Returning %d\n", ret);
     return ret;
 }
 
 /***********************************************************************
- *             SetupDiSetSelectedDriverW (SETUPAPI.@)
+ *             SetupDiGetDriverInfoDetailW (SETUPAPI.@)
  */
 BOOL WINAPI
-SetupDiSetSelectedDriverW(
+SetupDiGetDriverInfoDetailW(
     IN HDEVINFO DeviceInfoSet,
     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
-    IN OUT PSP_DRVINFO_DATA_W DriverInfoData OPTIONAL)
+    IN PSP_DRVINFO_DATA_W DriverInfoData,
+    OUT PSP_DRVINFO_DETAIL_DATA_W DriverInfoDetailData OPTIONAL,
+    IN DWORD DriverInfoDetailDataSize,
+    OUT PDWORD RequiredSize OPTIONAL)
 {
     BOOL ret = FALSE;
 
-    TRACE("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DriverInfoData);
+    TRACE("%p %p %p %p %lu %p\n", DeviceInfoSet, DeviceInfoData,
+        DriverInfoData, DriverInfoDetailData,
+        DriverInfoDetailDataSize, RequiredSize);
 
     if (!DeviceInfoSet)
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -4125,63 +5780,29 @@ SetupDiSetSelectedDriverW(
         SetLastError(ERROR_INVALID_HANDLE);
     else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
         SetLastError(ERROR_INVALID_USER_BUFFER);
-    else if (DriverInfoData && DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA_V1_W) && DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA_V2_W))
+    else if (!DriverInfoData)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (!DriverInfoDetailData && DriverInfoDetailDataSize != 0)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DriverInfoDetailData && DriverInfoDetailDataSize < sizeof(SP_DRVINFO_DETAIL_DATA_W))
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DriverInfoDetailData && DriverInfoDetailData->cbSize != sizeof(SP_DRVINFO_DETAIL_DATA_W))
         SetLastError(ERROR_INVALID_USER_BUFFER);
+    else if (DriverInfoData->Reserved == 0)
+        SetLastError(ERROR_NO_DRIVER_SELECTED);
     else
     {
-        struct DriverInfoElement **pDriverInfo;
-        PLIST_ENTRY ListHead, ItemList;
-
-        if (DeviceInfoData)
-        {
-            pDriverInfo = &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->SelectedDriver;
-            ListHead = &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->DriverListHead;
-        }
-        else
-        {
-            pDriverInfo = &((struct DeviceInfoSet *)DeviceInfoSet)->SelectedDriver;
-            ListHead = &((struct DeviceInfoSet *)DeviceInfoSet)->DriverListHead;
-        }
-
-        if (!DriverInfoData)
-        {
-            *pDriverInfo = NULL;
-            ret = TRUE;
-        }
-        else
-        {
-            /* Search selected driver in list */
-            ItemList = ListHead->Flink;
-            while (ItemList != ListHead)
-            {
-                if (DriverInfoData->Reserved != 0)
-                {
-                    if (DriverInfoData->Reserved == (ULONG_PTR)ItemList)
-                        break;
-                }
-                else
-                {
-                    /* The caller wants to compare only DriverType, Description and ProviderName fields */
-                    struct DriverInfoElement *driverInfo = (struct DriverInfoElement *)ItemList;
-                    if (driverInfo->Info.DriverType == DriverInfoData->DriverType
-                        && wcscmp(driverInfo->Info.Description, DriverInfoData->Description) == 0
-                        && wcscmp(driverInfo->Info.ProviderName, DriverInfoData->ProviderName) == 0)
-                    {
-                        break;
-                    }
-                }
-            }
-            if (ItemList == ListHead)
-                SetLastError(ERROR_INVALID_PARAMETER);
-            else
-            {
-                *pDriverInfo = (struct DriverInfoElement *)ItemList;
-                DriverInfoData->Reserved = (ULONG_PTR)ItemList;
-                ret = TRUE;
-                TRACE("Choosing driver whose rank is 0x%lx\n",
-                    ((struct DriverInfoElement *)ItemList)->DriverRank);
-            }
-        }
+        struct DriverInfoElement *driverInfoElement;
+        driverInfoElement = (struct DriverInfoElement *)DriverInfoData->Reserved;
+
+        memcpy(
+            DriverInfoDetailData,
+            &driverInfoElement->Details,
+            driverInfoElement->Details.cbSize);
+        /* FIXME: copy HardwareIDs/CompatibleIDs if buffer is big enough
+         * Don't forget to set CompatIDsOffset and CompatIDsLength fields.
+         */
+        ret = TRUE;
     }
 
     TRACE("Returning %d\n", ret);
@@ -4232,12 +5853,64 @@ SetupDiInstallDriverFiles(
     IN HDEVINFO DeviceInfoSet,
     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
 {
+    BOOL ret = FALSE;
+
     TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
 
-    FIXME("SetupDiInstallDriverFiles not implemented. Doing nothing\n");
-    //SetLastError(ERROR_GEN_FAILURE);
-    //return FALSE;
-    return TRUE;
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (((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 (DeviceInfoData && ((struct DeviceInfoElement *)DeviceInfoData->Reserved)->InstallParams.Reserved == 0)
+        SetLastError(ERROR_NO_DRIVER_SELECTED);
+    else if (!DeviceInfoData && ((struct DeviceInfoSet *)DeviceInfoSet)->InstallParams.Reserved == 0)
+        SetLastError(ERROR_NO_DRIVER_SELECTED);
+    else
+    {
+        SP_DEVINSTALL_PARAMS_W InstallParams;
+        struct DriverInfoElement *SelectedDriver;
+        WCHAR SectionName[MAX_PATH];
+        DWORD SectionNameLength = 0;
+
+        InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
+        ret = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+        if (!ret)
+            goto done;
+
+        SelectedDriver = (struct DriverInfoElement *)InstallParams.Reserved;
+        if (!SelectedDriver)
+        {
+            SetLastError(ERROR_NO_DRIVER_SELECTED);
+            goto done;
+        }
+
+        ret = SetupDiGetActualSectionToInstallW(
+            SelectedDriver->InfFileDetails->hInf,
+            SelectedDriver->Details.SectionName,
+            SectionName, MAX_PATH, &SectionNameLength, NULL);
+        if (!ret)
+            goto done;
+
+        if (!InstallParams.InstallMsgHandler)
+        {
+            InstallParams.InstallMsgHandler = SetupDefaultQueueCallbackW;
+            InstallParams.InstallMsgHandlerContext = SetupInitDefaultQueueCallback(InstallParams.hwndParent);
+            SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+        }
+        ret = SetupInstallFromInfSectionW(InstallParams.hwndParent,
+            SelectedDriver->InfFileDetails->hInf, SectionName,
+            SPINST_FILES, NULL, NULL, SP_COPY_NEWER,
+            InstallParams.InstallMsgHandler, InstallParams.InstallMsgHandlerContext,
+            DeviceInfoSet, DeviceInfoData);
+    }
+
+done:
+    TRACE("Returning %d\n", ret);
+    return ret;
 }
 
 /***********************************************************************
@@ -4248,12 +5921,91 @@ SetupDiRegisterCoDeviceInstallers(
     IN HDEVINFO DeviceInfoSet,
     IN PSP_DEVINFO_DATA DeviceInfoData)
 {
+    BOOL ret = FALSE; /* Return value */
+
     TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
 
-    FIXME("SetupDiRegisterCoDeviceInstallers not implemented. Doing nothing\n");
-    //SetLastError(ERROR_GEN_FAILURE);
-    //return FALSE;
-    return TRUE;
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
+        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
+    {
+        SP_DEVINSTALL_PARAMS_W InstallParams;
+        struct DriverInfoElement *SelectedDriver;
+        BOOL Result;
+        DWORD DoAction;
+        WCHAR SectionName[MAX_PATH];
+        DWORD SectionNameLength = 0;
+        HKEY hKey = INVALID_HANDLE_VALUE;
+
+        InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
+        Result = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+        if (!Result)
+            goto cleanup;
+
+        SelectedDriver = (struct DriverInfoElement *)InstallParams.Reserved;
+        if (SelectedDriver == NULL)
+        {
+            SetLastError(ERROR_NO_DRIVER_SELECTED);
+            goto cleanup;
+        }
+
+        /* Get .CoInstallers section name */
+        Result = SetupDiGetActualSectionToInstallW(
+            SelectedDriver->InfFileDetails->hInf,
+            SelectedDriver->Details.SectionName,
+            SectionName, MAX_PATH, &SectionNameLength, NULL);
+        if (!Result || SectionNameLength > MAX_PATH - wcslen(L".CoInstallers") - 1)
+            goto cleanup;
+        wcscat(SectionName, L".CoInstallers");
+
+        /* Open/Create driver key information */
+#if _WIN32_WINNT >= 0x502
+        hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_WRITE);
+#else
+        hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS);
+#endif
+        if (hKey == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
+            hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
+        if (hKey == INVALID_HANDLE_VALUE)
+            goto cleanup;
+
+        /* Install .CoInstallers section */
+        DoAction = SPINST_REGISTRY;
+        if (!(InstallParams.Flags & DI_NOFILECOPY))
+        {
+            DoAction |= SPINST_FILES;
+            if (!InstallParams.InstallMsgHandler)
+            {
+                InstallParams.InstallMsgHandler = SetupDefaultQueueCallbackW;
+                InstallParams.InstallMsgHandlerContext = SetupInitDefaultQueueCallback(InstallParams.hwndParent);
+                SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+            }
+        }
+        Result = SetupInstallFromInfSectionW(InstallParams.hwndParent,
+            SelectedDriver->InfFileDetails->hInf, SectionName,
+            DoAction, hKey, NULL, SP_COPY_NEWER,
+            InstallParams.InstallMsgHandler, InstallParams.InstallMsgHandlerContext,
+            DeviceInfoSet, DeviceInfoData);
+        if (!Result)
+            goto cleanup;
+
+        ret = TRUE;
+
+cleanup:
+        if (hKey != INVALID_HANDLE_VALUE)
+            RegCloseKey(hKey);
+    }
+
+    TRACE("Returning %d\n", ret);
+    return ret;
 }
 
 /***********************************************************************
@@ -4272,6 +6024,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.@)
  */
@@ -4280,22 +6071,354 @@ SetupDiInstallDevice(
     IN HDEVINFO DeviceInfoSet,
     IN PSP_DEVINFO_DATA DeviceInfoData)
 {
+    struct DeviceInfoElement *DevInfo = (struct DeviceInfoElement *)DeviceInfoData->Reserved;
+    SP_DEVINSTALL_PARAMS_W InstallParams;
+    struct DriverInfoElement *SelectedDriver;
+    SYSTEMTIME DriverDate;
+    WCHAR SectionName[MAX_PATH];
+    WCHAR Buffer[32];
+    DWORD SectionNameLength = 0;
+    BOOL Result = FALSE;
+    INFCONTEXT ContextService;
+    INT Flags;
+    ULONG DoAction;
+    DWORD RequiredSize;
+    LPCWSTR AssociatedService = NULL;
+    LPWSTR pSectionName = NULL;
+    WCHAR ClassName[MAX_CLASS_NAME_LEN];
+    GUID ClassGuid;
+    LPWSTR lpGuidString = NULL, lpFullGuidString = NULL;
+    BOOL RebootRequired = FALSE;
+    HKEY hKey = INVALID_HANDLE_VALUE;
+    HKEY hClassKey = INVALID_HANDLE_VALUE;
+    BOOL NeedtoCopyFile;
+    LONG rc;
+    BOOL ret = FALSE; /* Return value */
+
     TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
 
-    /* Steps to follow:
-     * 0. If DI_FLAGSEX_SETFAILEDINSTALL is set, set FAILEDINSTALL flag in ConfigFlags registry and exit
-     * 1. Create driver key and write InfPath and ProviderName
-     * 2a Process inf sections: {DDInstall}, {DDInstall}.HW [SetupDiOpenDevRegKey]
-     *  b Process {DDInstall}.LogConfigOverride if present [SetupDiOpenDevRegKey]
-     *  c Process {DDInstall}.Services [SetupDiOpenDevRegKey]
-     * 3. Copy inf file to Inf\ directory [SetupCopyOEMInf]
-     * 4. Install other waiting files
-     * 5. Load the driver/Call AddDevice
-     * 6. Send IRP_MN_START_DEVICE if DI_NEEDRESTART, DI_NEEDREBOOT and DI_DONOTCALLCONFIGMG are not set
-     */
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (((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
+        Result = TRUE;
 
-    FIXME("SetupDiInstallDevice not implemented. Doing nothing\n");
-    //SetLastError(ERROR_GEN_FAILURE);
-    //return FALSE;
-    return TRUE;
+    if (!Result)
+    {
+        /* One parameter is bad */
+        goto cleanup;
+    }
+
+    InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
+    Result = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+    if (!Result)
+        goto cleanup;
+
+    if (InstallParams.FlagsEx & DI_FLAGSEX_SETFAILEDINSTALL)
+    {
+        /* FIXME: set FAILEDINSTALL in ConfigFlags registry value */
+        goto cleanup;
+    }
+
+    SelectedDriver = (struct DriverInfoElement *)InstallParams.Reserved;
+    if (SelectedDriver == NULL)
+    {
+        SetLastError(ERROR_NO_DRIVER_SELECTED);
+        goto cleanup;
+    }
+
+    FileTimeToSystemTime(&SelectedDriver->Info.DriverDate, &DriverDate);
+
+    Result = SetupDiGetActualSectionToInstallW(
+        SelectedDriver->InfFileDetails->hInf,
+        SelectedDriver->Details.SectionName,
+        SectionName, MAX_PATH, &SectionNameLength, NULL);
+    if (!Result || SectionNameLength > MAX_PATH - 9)
+        goto cleanup;
+    pSectionName = &SectionName[wcslen(SectionName)];
+
+    /* Get information from [Version] section */
+    if (!SetupDiGetINFClassW(SelectedDriver->Details.InfFileName, &ClassGuid, ClassName, MAX_CLASS_NAME_LEN, &RequiredSize))
+        goto cleanup;
+    /* Format ClassGuid to a string */
+    if (UuidToStringW((UUID*)&ClassGuid, &lpGuidString) != RPC_S_OK)
+        goto cleanup;
+    RequiredSize = lstrlenW(lpGuidString);
+    lpFullGuidString = HeapAlloc(GetProcessHeap(), 0, (RequiredSize + 3) * sizeof(WCHAR));
+    if (!lpFullGuidString)
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        goto cleanup;
+    }
+    lpFullGuidString[0] = '{';
+    memcpy(&lpFullGuidString[1], lpGuidString, RequiredSize * sizeof(WCHAR));
+    lpFullGuidString[RequiredSize + 1] = '}';
+    lpFullGuidString[RequiredSize + 2] = '\0';
+
+    /* Open/Create driver key information */
+#if _WIN32_WINNT >= 0x502
+    hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_WRITE);
+#else
+    hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS);
+#endif
+    if (hKey == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
+        hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
+    if (hKey == INVALID_HANDLE_VALUE)
+        goto cleanup;
+
+    /* Install main section */
+    DoAction = SPINST_REGISTRY;
+    if (!(InstallParams.Flags & DI_NOFILECOPY))
+    {
+        DoAction |= SPINST_FILES;
+        if (!InstallParams.InstallMsgHandler)
+        {
+            InstallParams.InstallMsgHandler = SetupDefaultQueueCallbackW;
+            InstallParams.InstallMsgHandlerContext = SetupInitDefaultQueueCallback(InstallParams.hwndParent);
+            SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+        }
+    }
+    *pSectionName = '\0';
+    Result = SetupInstallFromInfSectionW(InstallParams.hwndParent,
+        SelectedDriver->InfFileDetails->hInf, SectionName,
+        DoAction, hKey, NULL, SP_COPY_NEWER,
+        InstallParams.InstallMsgHandler, InstallParams.InstallMsgHandlerContext,
+        DeviceInfoSet, DeviceInfoData);
+    if (!Result)
+        goto cleanup;
+    if (!(InstallParams.Flags & DI_NOFILECOPY) && !(InstallParams.Flags & DI_NOVCP))
+    {
+        if (Result && InstallParams.InstallMsgHandler == SetupDefaultQueueCallbackW)
+        {
+            /* Delete resources allocated by SetupInitDefaultQueueCallback */
+            SetupTermDefaultQueueCallback(InstallParams.InstallMsgHandlerContext);
+        }
+    }
+    InstallParams.Flags |= DI_NOFILECOPY;
+    SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
+
+    /* Write information to driver key */
+    *pSectionName = UNICODE_NULL;
+    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("InfPath         : '%S'\n", SelectedDriver->Details.InfFileName);
+    TRACE("InfSection      : '%S'\n", SelectedDriver->Details.SectionName);
+    TRACE("InfSectionExt   : '%S'\n", &SectionName[wcslen(SelectedDriver->Details.SectionName)]);
+    TRACE("MatchingDeviceId: '%S'\n", SelectedDriver->MatchingId);
+    TRACE("ProviderName    : '%S'\n", SelectedDriver->Info.ProviderName);
+    swprintf(Buffer, L"%u-%u-%u", DriverDate.wMonth, DriverDate.wDay, DriverDate.wYear);
+    rc = RegSetValueEx(hKey, L"DriverDate", 0, REG_SZ, (const BYTE *)Buffer, (wcslen(Buffer) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"DriverDateData", 0, REG_BINARY, (const BYTE *)&SelectedDriver->Info.DriverDate, sizeof(FILETIME));
+    if (rc == ERROR_SUCCESS)
+        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);
+        rc = RegSetValueEx(hKey, L"DriverVersion", 0, REG_SZ, (const BYTE *)Buffer, (wcslen(Buffer) + 1) * sizeof(WCHAR));
+    }
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"InfPath", 0, REG_SZ, (const BYTE *)SelectedDriver->Details.InfFileName, (wcslen(SelectedDriver->Details.InfFileName) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"InfSection", 0, REG_SZ, (const BYTE *)SelectedDriver->Details.SectionName, (wcslen(SelectedDriver->Details.SectionName) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"InfSectionExt", 0, REG_SZ, (const BYTE *)&SectionName[wcslen(SelectedDriver->Details.SectionName)], (wcslen(SectionName) - wcslen(SelectedDriver->Details.SectionName) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"MatchingDeviceId", 0, REG_SZ, (const BYTE *)SelectedDriver->MatchingId, (wcslen(SelectedDriver->MatchingId) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"ProviderName", 0, REG_SZ, (const BYTE *)SelectedDriver->Info.ProviderName, (wcslen(SelectedDriver->Info.ProviderName) + 1) * sizeof(WCHAR));
+    if (rc != ERROR_SUCCESS)
+    {
+       SetLastError(rc);
+       goto cleanup;
+    }
+    RegCloseKey(hKey);
+    hKey = INVALID_HANDLE_VALUE;
+
+    /* FIXME: Process .LogConfigOverride section */
+
+    /* Install .Services section */
+    wcscpy(pSectionName, L".Services");
+    Result = SetupFindFirstLineW(SelectedDriver->InfFileDetails->hInf, SectionName, NULL, &ContextService);
+    while (Result)
+    {
+        LPWSTR ServiceName = NULL;
+        LPWSTR ServiceSection = NULL;
+
+        Result = SetupGetStringFieldW(
+            &ContextService,
+            1, /* Field index */
+            NULL, 0,
+            &RequiredSize);
+        if (!Result)
+            goto nextfile;
+        if (RequiredSize > 0)
+        {
+            /* We got the needed size for the buffer */
+            ServiceName = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
+            if (!ServiceName)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto nextfile;
+            }
+            Result = SetupGetStringFieldW(
+                &ContextService,
+                1, /* Field index */
+                ServiceName, RequiredSize,
+                &RequiredSize);
+            if (!Result)
+                goto nextfile;
+        }
+        Result = SetupGetIntField(
+            &ContextService,
+            2, /* Field index */
+            &Flags);
+        if (!Result)
+        {
+            /* The field may be empty. Ignore the error */
+            Flags = 0;
+        }
+        Result = SetupGetStringFieldW(
+            &ContextService,
+            3, /* Field index */
+            NULL, 0,
+            &RequiredSize);
+        if (!Result)
+        {
+            if (GetLastError() == ERROR_INVALID_PARAMETER)
+            {
+                /* This first is probably missing. It is not
+                 * required, so ignore the error */
+                RequiredSize = 0;
+                Result = TRUE;
+            }
+            else
+                goto nextfile;
+        }
+        if (RequiredSize > 0)
+        {
+            /* We got the needed size for the buffer */
+            ServiceSection = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
+            if (!ServiceSection)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto nextfile;
+            }
+            Result = SetupGetStringFieldW(
+                &ContextService,
+                3, /* Field index */
+                ServiceSection, RequiredSize,
+                &RequiredSize);
+            if (!Result)
+                goto nextfile;
+
+            SetLastError(ERROR_SUCCESS);
+            Result = SetupInstallServicesFromInfSectionExW(
+                SelectedDriver->InfFileDetails->hInf,
+                ServiceSection, Flags, DeviceInfoSet, DeviceInfoData, ServiceName, NULL);
+        }
+        if (Result && (Flags & SPSVCINST_ASSOCSERVICE))
+        {
+            AssociatedService = ServiceName;
+            ServiceName = NULL;
+            if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED)
+                RebootRequired = TRUE;
+        }
+nextfile:
+        HeapFree(GetProcessHeap(), 0, ServiceName);
+        HeapFree(GetProcessHeap(), 0, ServiceSection);
+        if (!Result)
+            goto cleanup;
+        Result = SetupFindNextLine(&ContextService, &ContextService);
+    }
+
+    /* 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);
+    if (hKey == INVALID_HANDLE_VALUE)
+        goto cleanup;
+
+    /* Install .HW section */
+    wcscpy(pSectionName, L".HW");
+    Result = SetupInstallFromInfSectionW(InstallParams.hwndParent,
+        SelectedDriver->InfFileDetails->hInf, SectionName,
+        SPINST_REGISTRY, hKey, NULL, 0,
+        InstallParams.InstallMsgHandler, InstallParams.InstallMsgHandlerContext,
+        DeviceInfoSet, DeviceInfoData);
+    if (!Result)
+        goto cleanup;
+
+    /* Write information to enum key */
+    TRACE("Write information to enum key\n");
+    TRACE("Class           : '%S'\n", ClassName);
+    TRACE("ClassGUID       : '%S'\n", lpFullGuidString);
+    TRACE("DeviceDesc      : '%S'\n", SelectedDriver->Info.Description);
+    TRACE("Mfg             : '%S'\n", SelectedDriver->Info.MfgName);
+    TRACE("Service         : '%S'\n", AssociatedService);
+    rc = RegSetValueEx(hKey, L"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));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"DeviceDesc", 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));
+    if (rc == ERROR_SUCCESS && *AssociatedService)
+        rc = RegSetValueEx(hKey, L"Service", 0, REG_SZ, (const BYTE *)AssociatedService, (wcslen(AssociatedService) + 1) * sizeof(WCHAR));
+    if (rc != ERROR_SUCCESS)
+    {
+       SetLastError(rc);
+       goto cleanup;
+    }
+
+    /* Start the device */
+    if (!RebootRequired && !(InstallParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT | DI_DONOTCALLCONFIGMG)))
+    {
+        PLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData;
+        NTSTATUS Status;
+        RtlInitUnicodeString(&ResetDeviceData.DeviceInstance, DevInfo->DeviceName);
+        Status = NtPlugPlayControl(PlugPlayControlResetDevice, &ResetDeviceData, sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA));
+        ret = NT_SUCCESS(Status);
+    }
+    else
+        ret = TRUE;
+
+cleanup:
+    /* End of installation */
+    if (hClassKey != INVALID_HANDLE_VALUE)
+        RegCloseKey(hClassKey);
+    if (hKey != INVALID_HANDLE_VALUE)
+        RegCloseKey(hKey);
+    if (lpGuidString)
+        RpcStringFreeW(&lpGuidString);
+    HeapFree(GetProcessHeap(), 0, (LPWSTR)AssociatedService);
+    HeapFree(GetProcessHeap(), 0, lpFullGuidString);
+
+    TRACE("Returning %d\n", ret);
+    return ret;
 }