Process AddReg directive in main section to install
[reactos.git] / reactos / lib / setupapi / devinst.c
index 7a4c4f2..732adf3 100644 (file)
 
 #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 <windows.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 NTOS_MODE_USER
+#include <ndk/ntndk.h>
 
 #include "setupapi_private.h"
 
@@ -101,6 +93,7 @@ struct CoInstallerElement
 {
     LIST_ENTRY ListEntry;
 
+    HMODULE Module;
     COINSTALLER_PROC Function;
     BOOL DoPostProcessing;
     PVOID PrivateData;
@@ -129,8 +122,10 @@ struct DriverInfoElement /* Element of DeviceInfoSet.DriverListHead and DeviceIn
 
     DWORD DriverRank;
     SP_DRVINFO_DATA_V2_W Info;
+    GUID ClassGuid;
     LPWSTR InfPath;
     LPWSTR InfSection;
+    LPWSTR MatchingId;
 };
 
 struct DeviceInfoElement /* Element of DeviceInfoSet.ListHead */
@@ -1190,13 +1185,12 @@ end:
 static BOOL
 CreateDeviceInfoElement(
     IN LPCWSTR InstancePath,
-    LPCGUID pClassGuid,
+    IN LPCGUID pClassGuid,
     OUT struct DeviceInfoElement **pDeviceInfo)
 {
     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));
     if (!deviceInfo)
@@ -2164,7 +2158,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);
 
@@ -2180,19 +2174,26 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
         PropertyBufferSizeW,
         &RequiredSizeW);
 
+    if (bResult || GetLastError() == ERROR_MORE_DATA)
+    {
+        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)
@@ -2213,10 +2214,6 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
     }
 
     HeapFree(GetProcessHeap(), 0, PropertyBufferW);
-    if (PropertyRegDataType)
-        *PropertyRegDataType = RegType;
-    if (RequiredSize)
-        *RequiredSize = RequiredSizeA;
     return bResult;
 }
 
@@ -2762,9 +2759,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;
 }
 
 /***********************************************************************
@@ -2781,6 +2790,76 @@ BOOL WINAPI SetupDiSetClassInstallParamsA(
     return FALSE;
 }
 
+static DWORD
+GetFunctionPointer(
+        IN PWSTR InstallerName,
+        OUT HMODULE* ModulePointer,
+        OUT PVOID* FunctionPointer)
+{
+    HMODULE hModule = NULL;
+    LPSTR FunctionNameA = NULL;
+    PWCHAR Comma;
+    DWORD rc;
+
+    *ModulePointer = NULL;
+    *FunctionPointer = NULL;
+
+    Comma = strchrW(InstallerName, ',');
+    if (!Comma)
+    {
+        rc = ERROR_INVALID_PARAMETER;
+        goto cleanup;
+    }
+
+    /* W->A conversion for function name */
+    FunctionNameA = UnicodeToMultiByte(Comma + 1, CP_ACP);
+    if (!FunctionNameA)
+    {
+        rc = GetLastError();
+        goto cleanup;
+    }
+
+    /* Load library */
+    *Comma = '\0';
+    hModule = LoadLibraryW(InstallerName);
+    *Comma = ',';
+    if (!hModule)
+    {
+        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.@)
  */
@@ -2857,6 +2936,7 @@ BOOL WINAPI SetupDiCallClassInstaller(
         {
             LIST_ENTRY ClassCoInstallersListHead;
             LIST_ENTRY DeviceCoInstallersListHead;
+            HMODULE ClassInstallerLibrary = NULL;
             CLASS_INSTALL_PROC ClassInstaller = NULL;
             COINSTALLER_CONTEXT_DATA Context;
             PLIST_ENTRY ListEntry;
@@ -2867,11 +2947,11 @@ 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");
+                FIXME("Doesn't use Device co-installers at the moment\n");
             }
-            if (CanHandle & DEVICE_COINSTALLER)
+            if (CanHandle & CLASS_COINSTALLER)
             {
                 rc = RegOpenKeyEx(
                     HKEY_LOCAL_MACHINE,
@@ -2884,20 +2964,20 @@ BOOL WINAPI SetupDiCallClassInstaller(
                     LPWSTR lpGuidString;
                     if (UuidToStringW((UUID*)&DeviceInfoData->ClassGuid, &lpGuidString) == RPC_S_OK)
                     {
-                        rc = RegQueryValueExW(hKey, L"Installer32", NULL, &dwRegType, NULL, &dwLength);
+                        rc = RegQueryValueExW(hKey, lpGuidString, NULL, &dwRegType, NULL, &dwLength);
                         if (rc == ERROR_SUCCESS && dwRegType == REG_SZ)
                         {
                             LPWSTR KeyBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);
                             if (KeyBuffer != NULL)
                             {
-                                rc = RegQueryValueExW(hKey, L"Installer32", NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
+                                rc = RegQueryValueExW(hKey, lpGuidString, NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
                                 if (rc == ERROR_SUCCESS)
                                 {
                                     LPCWSTR 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 */
+                                        FIXME("Class coinstaller is '%S'. UNIMPLEMENTED!\n", ptr);
                                     }
                                 }
                                 HeapFree(GetProcessHeap(), 0, KeyBuffer);
@@ -2922,8 +3002,8 @@ 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 */
+                                rc = GetFunctionPointer(KeyBuffer, &ClassInstallerLibrary, (PVOID*)&ClassInstaller);
                             }
                             HeapFree(GetProcessHeap(), 0, KeyBuffer);
                         }
@@ -2966,7 +3046,10 @@ BOOL WINAPI SetupDiCallClassInstaller(
 
             /* Call Class installer */
             if (ClassInstaller)
+            {
                 rc = (*ClassInstaller)(InstallFunction, DeviceInfoSet, DeviceInfoData);
+                FreeFunctionPointer(ClassInstallerLibrary, ClassInstaller);
+            }
             else
                 rc = ERROR_DI_DO_DEFAULT;
 
@@ -2996,6 +3079,7 @@ BOOL WINAPI SetupDiCallClassInstaller(
                     Context.PrivateData = coinstaller->PrivateData;
                     rc = (*coinstaller->Function)(InstallFunction, DeviceInfoSet, DeviceInfoData, &Context);
                 }
+                FreeFunctionPointer(coinstaller->Module, coinstaller->Function);
                 ListEntry = ListEntry->Flink;
             }
 
@@ -3010,6 +3094,7 @@ BOOL WINAPI SetupDiCallClassInstaller(
                     Context.PrivateData = coinstaller->PrivateData;
                     rc = (*coinstaller->Function)(InstallFunction, DeviceInfoSet, DeviceInfoData, &Context);
                 }
+                FreeFunctionPointer(coinstaller->Module, coinstaller->Function);
                 ListEntry = ListEntry->Flink;
             }
 
@@ -3087,6 +3172,156 @@ BOOL WINAPI SetupDiGetDeviceInstallParamsW(
     return FALSE;
 }
 
+/***********************************************************************
+ *             SetupDiCreateDevRegKey (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,
+                    KEY_READ | KEY_WRITE,
+                    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.@)
  */
@@ -3098,9 +3333,130 @@ HKEY WINAPI SetupDiOpenDevRegKey(
        DWORD KeyType,
        REGSAM samDesired)
 {
-    FIXME("%p %p %ld %ld %ld %lx\n", DeviceInfoSet, DeviceInfoData,
+    struct DeviceInfoSet *list;
+    HKEY ret = INVALID_HANDLE_VALUE;
+
+    TRACE("%p %p %lu %lu %lu 0x%lx\n", DeviceInfoSet, DeviceInfoData,
           Scope, HwProfile, KeyType, samDesired);
-    return INVALID_HANDLE_VALUE;
+
+    if (!DeviceInfoSet)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+        SetLastError(ERROR_INVALID_HANDLE);
+    else if (!DeviceInfoData)
+        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;
 }
 
 /***********************************************************************
@@ -3250,30 +3606,34 @@ static BOOL
 AddDriverToList(
     IN PLIST_ENTRY DriverListHead,
     IN DWORD DriverType, /* SPDIT_CLASSDRIVER or SPDIT_COMPATDRIVER */
+    IN LPGUID ClassGuid,
     IN INFCONTEXT ContextDevice,
     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;
     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;
+        goto cleanup;
     }
+    memset(driverInfo, 0, sizeof(struct DriverInfoElement));
 
+    /* Fill InfSection field */
     SetLastError(ERROR_INSUFFICIENT_BUFFER);
-    driverInfo->InfSection = NULL;
     while (!Result && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
     {
         HeapFree(GetProcessHeap(), 0, driverInfo->InfSection);
@@ -3281,8 +3641,7 @@ AddDriverToList(
         if (!driverInfo->InfSection)
         {
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-            HeapFree(GetProcessHeap(), 0, driverInfo);
-            return FALSE;
+            goto cleanup;
         }
         Result = SetupGetStringFieldW(
             &ContextDevice,
@@ -3291,22 +3650,27 @@ AddDriverToList(
             &RequiredSize);
     }
     if (!Result)
-    {
-        HeapFree(GetProcessHeap(), 0, driverInfo->InfSection);
-        HeapFree(GetProcessHeap(), 0, driverInfo);
-        return FALSE;
-    }
+        goto cleanup;
 
+    /* Copy InfFile information */
     driverInfo->InfPath = HeapAlloc(GetProcessHeap(), 0, (wcslen(InfFile) + 1) * sizeof(WCHAR));
     if (!driverInfo->InfPath)
     {
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-        HeapFree(GetProcessHeap(), 0, driverInfo->InfSection);
-        HeapFree(GetProcessHeap(), 0, driverInfo);
-        return FALSE;
+        goto cleanup;
     }
     RtlCopyMemory(driverInfo->InfPath, InfFile, (wcslen(InfFile) + 1) * sizeof(WCHAR));
 
+    /* Copy MatchingId information */
+    driverInfo->MatchingId = HeapAlloc(GetProcessHeap(), 0, (wcslen(MatchingId) + 1) * sizeof(WCHAR));
+    if (!driverInfo->MatchingId)
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        goto cleanup;
+    }
+    RtlCopyMemory(driverInfo->MatchingId, MatchingId, (wcslen(MatchingId) + 1) * sizeof(WCHAR));
+
+    /* Get device description */
     Result = FALSE;
     RequiredSize = 128; /* Initial buffer size */
     SetLastError(ERROR_INSUFFICIENT_BUFFER);
@@ -3315,7 +3679,7 @@ AddDriverToList(
         HeapFree(GetProcessHeap(), 0, DeviceDescription);
         DeviceDescription = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
         if (!DeviceDescription)
-            return FALSE;
+            goto cleanup;
         Result = SetupGetStringFieldW(
             &ContextDevice,
             0, /* Field index */
@@ -3323,14 +3687,9 @@ AddDriverToList(
             &RequiredSize);
     }
     if (!Result)
-    {
-        HeapFree(GetProcessHeap(), 0, driverInfo->InfPath);
-        HeapFree(GetProcessHeap(), 0, driverInfo->InfSection);
-        HeapFree(GetProcessHeap(), 0, driverInfo);
-        HeapFree(GetProcessHeap(), 0, DeviceDescription);
-        return FALSE;
-    }
+        goto cleanup;
 
+    /* Get inf install section */
     Result = FALSE;
     RequiredSize = 128; /* Initial buffer size */
     SetLastError(ERROR_INSUFFICIENT_BUFFER);
@@ -3339,13 +3698,7 @@ AddDriverToList(
         HeapFree(GetProcessHeap(), 0, InfInstallSection);
         InfInstallSection = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
         if (!InfInstallSection)
-        {
-            HeapFree(GetProcessHeap(), 0, driverInfo->InfPath);
-            HeapFree(GetProcessHeap(), 0, driverInfo->InfSection);
-            HeapFree(GetProcessHeap(), 0, driverInfo);
-            HeapFree(GetProcessHeap(), 0, DeviceDescription);
-            return FALSE;
-        }
+            goto cleanup;
         Result = SetupGetStringFieldW(
             &ContextDevice,
             1, /* Field index */
@@ -3353,19 +3706,13 @@ AddDriverToList(
             &RequiredSize);
     }
     if (!Result)
-    {
-        HeapFree(GetProcessHeap(), 0, driverInfo->InfPath);
-        HeapFree(GetProcessHeap(), 0, driverInfo->InfSection);
-        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->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);
@@ -3399,9 +3746,23 @@ AddDriverToList(
         InsertTailList(DriverListHead, &driverInfo->ListEntry);
     }
 
+    ret = TRUE;
+
+cleanup:
+    if (!ret)
+    {
+        if (driverInfo)
+        {
+            HeapFree(GetProcessHeap(), 0, driverInfo->InfPath);
+            HeapFree(GetProcessHeap(), 0, driverInfo->InfSection);
+            HeapFree(GetProcessHeap(), 0, driverInfo->MatchingId);
+        }
+        HeapFree(GetProcessHeap(), 0, driverInfo);
+    }
     HeapFree(GetProcessHeap(), 0, DeviceDescription);
     HeapFree(GetProcessHeap(), 0, InfInstallSection);
-    return TRUE;
+
+    return ret;
 }
 
 static BOOL
@@ -3414,9 +3775,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,
@@ -3424,28 +3791,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 */
@@ -3453,13 +3821,67 @@ GetVersionInformationFromInfFile(
             ProviderName, RequiredSize,
             &RequiredSize);
     }
-    //FIXME: DriverDate = Version.DriverVer => invalid date = 00/00/00
-    RtlZeroMemory(DriverDate, sizeof(FILETIME));
-    //FIXME: DriverVersion = Version.DriverVer => invalid = 0
+    if (!Result)
+        goto cleanup;
+    *pProviderName = ProviderName;
+
+    /* 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 */
 
-    *pProviderName = ProviderName;
-    return TRUE;
+    ret = TRUE;
+
+cleanup:
+    if (!ret)
+        HeapFree(GetProcessHeap(), 0, ProviderName);
+    HeapFree(GetProcessHeap(), 0, DriverVer);
+
+    TRACE("Returning %d\n", ret);
+    return ret;
 }
 
 /***********************************************************************
@@ -3480,7 +3902,7 @@ SetupDiBuildDriverInfoList(
     LPWSTR HardwareIDs = NULL;
     LPWSTR CompatibleIDs = NULL;
     FILETIME DriverDate;
-    DWORDLONG DriverVersion;
+    DWORDLONG DriverVersion = 0;
     DWORD RequiredSize;
     BOOL ret = FALSE;
 
@@ -3674,10 +4096,12 @@ SetupDiBuildDriverInfoList(
                             if (!AddDriverToList(
                                 &list->DriverListHead,
                                 DriverType,
+                                &ClassGuid,
                                 ContextDevice,
                                 filename,
                                 ProviderName,
                                 ManufacturerName,
+                                NULL,
                                 DriverDate, DriverVersion,
                                 0))
                             {
@@ -3727,10 +4151,12 @@ SetupDiBuildDriverInfoList(
                                         AddDriverToList(
                                             &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->DriverListHead,
                                             DriverType,
+                                            &ClassGuid,
                                             ContextDevice,
                                             filename,
                                             ProviderName,
                                             ManufacturerName,
+                                            currentId,
                                             DriverDate, DriverVersion,
                                             DriverRank  + (i == 2 ? 0 : 0x1000 + i - 3));
                                         DriverAlreadyAdded = TRUE;
@@ -3745,10 +4171,12 @@ SetupDiBuildDriverInfoList(
                                             AddDriverToList(
                                                 &((struct DeviceInfoElement *)DeviceInfoData->Reserved)->DriverListHead,
                                                 DriverType,
+                                                &ClassGuid,
                                                 ContextDevice,
                                                 filename,
                                                 ProviderName,
                                                 ManufacturerName,
+                                                currentId,
                                                 DriverDate, DriverVersion,
                                                 DriverRank + (i == 2 ? 0x2000 : 0x3000 + i - 3));
                                             DriverAlreadyAdded = TRUE;
@@ -4227,6 +4655,8 @@ SetupDiSetSelectedDriverW(
                 ret = TRUE;
                 TRACE("Choosing driver whose rank is 0x%lx\n",
                     ((struct DriverInfoElement *)ItemList)->DriverRank);
+                if (DeviceInfoData)
+                    memcpy(&DeviceInfoData->ClassGuid, &(*pDriverInfo)->ClassGuid, sizeof(GUID));
             }
         }
     }
@@ -4378,20 +4808,27 @@ SetupDiInstallDevice(
     IN PSP_DEVINFO_DATA DeviceInfoData)
 {
     struct DriverInfoElement *DriverInfo;
-    struct DeviceInfoSet *DevInfoSet = (struct DeviceInfoSet *)DeviceInfoSet;
     struct DeviceInfoElement *DevInfo = (struct DeviceInfoElement *)DeviceInfoData->Reserved;
     SYSTEMTIME DriverDate;
     WCHAR SectionName[MAX_PATH];
+    WCHAR Buffer[32];
     DWORD SectionNameLength = 0;
     BOOL Result = FALSE;
     INFCONTEXT ContextService;
-    UINT Flags;
+    INT Flags;
     DWORD RequiredSize;
-    HINF hInf = NULL;
+    HINF hInf = INVALID_HANDLE_VALUE;
     LPCWSTR AssociatedService = NULL;
+    LPWSTR pSectionName = NULL;
+    LPWSTR ClassName = NULL;
+    GUID ClassGuid;
+    LPWSTR lpGuidString = NULL, lpFullGuidString = NULL;
     BOOL RebootRequired = FALSE;
-    HKEY hEnumKey, hKey;
+    HKEY hKey = INVALID_HANDLE_VALUE;
+    HKEY hClassKey = INVALID_HANDLE_VALUE;
     LONG rc;
+    HWND hWnd;
+    BOOL ret = FALSE; /* Return value */
 
     TRACE("%p %p\n", DeviceInfoSet, DeviceInfoData);
 
@@ -4413,46 +4850,124 @@ SetupDiInstallDevice(
     if (!Result)
     {
         /* One parameter is bad */
-        return FALSE;
+        goto cleanup;
     }
 
     /* FIXME: If DI_FLAGSEX_SETFAILEDINSTALL is set, set FAILEDINSTALL flag in ConfigFlags registry and exit */
 
     if (DeviceInfoData)
+    {
         DriverInfo = ((struct DeviceInfoElement *)DeviceInfoData->Reserved)->SelectedDriver;
+        hWnd = ((struct DeviceInfoElement *)DeviceInfoData->Reserved)->hwndParent;
+    }
     else
+    {
         DriverInfo = ((struct DeviceInfoSet *)DeviceInfoSet)->SelectedDriver;
+        hWnd = ((struct DeviceInfoSet *)DeviceInfoSet)->hwndParent;
+    }
     FileTimeToSystemTime(&DriverInfo->Info.DriverDate, &DriverDate);
 
     hInf = SetupOpenInfFileW(DriverInfo->InfPath, NULL, INF_STYLE_WIN4, NULL);
     if (hInf == INVALID_HANDLE_VALUE)
-        return FALSE;
+        goto cleanup;
 
     Result = SetupDiGetActualSectionToInstallW(hInf, DriverInfo->InfSection,
         SectionName, MAX_PATH, &SectionNameLength, NULL);
     if (!Result || SectionNameLength > MAX_PATH - 9)
+        goto cleanup;
+    pSectionName = &SectionName[wcslen(SectionName)];
+
+    /* Get information from [Version] section */
+    ClassName = NULL;
+    RequiredSize = 0;
+    if (!SetupDiGetINFClassW(DriverInfo->InfPath, &ClassGuid, ClassName, RequiredSize, &RequiredSize))
     {
-        SetupCloseInfFile(hInf);
-        return FALSE;
+        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+            goto cleanup;
+        ClassName = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(WCHAR));
+        if (!ClassName)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            goto cleanup;
+        }
+        if (!SetupDiGetINFClassW(DriverInfo->InfPath, &ClassGuid, ClassName, RequiredSize, &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 */
+    hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_SET_VALUE);
+    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 */
+    /* Files have already been copied in SetupDiInstallDriverFiles.
+     * Process only registry entries. */
+    *pSectionName = '\0';
+    Result = SetupInstallFromInfSectionW(hWnd, hInf, SectionName,
+        SPINST_REGISTRY, hKey, NULL, 0,
+        SetupDefaultQueueCallbackW, NULL,
+        NULL, NULL);
+    if (!Result)
+        goto cleanup;
 
-    /* Create driver key information */
-    FIXME("FIXME: Create driver key information\n");
-    
     /* Write information to driver key */
-    FIXME("FIXME: Write information to driver key\n");
-    FIXME("DriverDate      : '%u-%u-%u'\n", 0, DriverDate.wMonth, DriverDate.wDay, DriverDate.wYear);
-    FIXME("DriverDateData  :"); { ULONG i; for (i = 0; i < sizeof(DriverInfo->Info.DriverDate); i++) DbgPrint(" %02x", ((PCHAR)&DriverInfo->Info.DriverDate)[i] & 0xff); } DbgPrint("\n");
-    FIXME("DriverDesc      : '%S'\n", DriverInfo->Info.Description);
-    FIXME("DriverVersion   : '%u.%u.%u.%u'\n", DriverInfo->Info.DriverVersion & 0xff, (DriverInfo->Info.DriverVersion >> 8) & 0xff, (DriverInfo->Info.DriverVersion >> 16) & 0xff, (DriverInfo->Info.DriverVersion >> 24) & 0xff);
-    FIXME("InfPath         : '%S'\n", DriverInfo->InfPath);
-    FIXME("InfSection      : '%S'\n", DriverInfo->InfSection); /* FIXME: remove extension */
-    FIXME("InfSectionExt   : '%S'\n", L"???"); /* FIXME */
-    FIXME("MatchingDeviceId: '%S'\n", L"???"); /* FIXME */
-    FIXME("ProviderName    : '%S'\n", DriverInfo->Info.ProviderName);
-
-    /* Install services */
-    wcscat(SectionName, L".Services");
+    *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", DriverInfo->Info.Description);
+    TRACE("DriverVersion   : '%u.%u.%u.%u'\n", DriverInfo->Info.DriverVersion & 0xff, (DriverInfo->Info.DriverVersion >> 8) & 0xff, (DriverInfo->Info.DriverVersion >> 16) & 0xff, (DriverInfo->Info.DriverVersion >> 24) & 0xff);
+    TRACE("InfPath         : '%S'\n", DriverInfo->InfPath);
+    TRACE("InfSection      : '%S'\n", DriverInfo->InfSection);
+    TRACE("InfSectionExt   : '%S'\n", &SectionName[wcslen(DriverInfo->InfSection)]); /* FIXME */
+    TRACE("MatchingDeviceId: '%S'\n", DriverInfo->MatchingId);
+    TRACE("ProviderName    : '%S'\n", DriverInfo->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 *)&DriverInfo->Info.DriverDate, sizeof(FILETIME));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"DriverDesc", 0, REG_SZ, (const BYTE *)DriverInfo->Info.Description, (wcslen(DriverInfo->Info.Description) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+    {
+        swprintf(Buffer, L"%u.%u.%u.%u", DriverInfo->Info.DriverVersion & 0xff, (DriverInfo->Info.DriverVersion >> 8) & 0xff, (DriverInfo->Info.DriverVersion >> 16) & 0xff, (DriverInfo->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 *)DriverInfo->InfPath, (wcslen(DriverInfo->InfPath) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"InfSection", 0, REG_SZ, (const BYTE *)DriverInfo->InfSection, (wcslen(DriverInfo->InfSection) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"InfSectionExt", 0, REG_SZ, (const BYTE *)&SectionName[wcslen(DriverInfo->InfSection)], (wcslen(SectionName) - wcslen(DriverInfo->InfSection) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"MatchingDeviceId", 0, REG_SZ, (const BYTE *)DriverInfo->MatchingId, (wcslen(DriverInfo->MatchingId) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"ProviderName", 0, REG_SZ, (const BYTE *)DriverInfo->Info.ProviderName, (wcslen(DriverInfo->Info.ProviderName) + 1) * sizeof(WCHAR));
+    if (rc != ERROR_SUCCESS)
+    {
+       SetLastError(rc);
+       goto cleanup;
+    }
+    RegCloseKey(hKey);
+    hKey = INVALID_HANDLE_VALUE;
+
+    /* Install .Services section */
+    wcscpy(pSectionName, L".Services");
     Result = SetupFindFirstLineW(hInf, SectionName, NULL, &ContextService);
     while (Result)
     {
@@ -4465,7 +4980,7 @@ SetupDiInstallDevice(
             NULL, 0,
             &RequiredSize);
         if (!Result)
-            goto cleanup;
+            goto nextfile;
         if (RequiredSize > 0)
         {
             /* We got the needed size for the buffer */
@@ -4473,7 +4988,7 @@ SetupDiInstallDevice(
             if (!ServiceName)
             {
                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-                goto cleanup;
+                goto nextfile;
             }
             Result = SetupGetStringFieldW(
                 &ContextService,
@@ -4481,7 +4996,7 @@ SetupDiInstallDevice(
                 ServiceName, RequiredSize,
                 &RequiredSize);
             if (!Result)
-                goto cleanup;
+                goto nextfile;
         }
         Result = SetupGetIntField(
             &ContextService,
@@ -4498,7 +5013,7 @@ SetupDiInstallDevice(
             NULL, 0,
             &RequiredSize);
         if (!Result)
-            goto cleanup;
+            goto nextfile;
         if (RequiredSize > 0)
         {
             /* We got the needed size for the buffer */
@@ -4506,7 +5021,7 @@ SetupDiInstallDevice(
             if (!ServiceSection)
             {
                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-               goto cleanup;
+                goto nextfile;
             }
             Result = SetupGetStringFieldW(
                 &ContextService,
@@ -4514,10 +5029,10 @@ SetupDiInstallDevice(
                 ServiceSection, RequiredSize,
                &RequiredSize);
             if (!Result)
-               goto cleanup;
+               goto nextfile;
         }
         SetLastError(ERROR_SUCCESS);
-        Result = SetupInstallServicesFromInfSectionExW(hInf, ServiceSection, Flags, DeviceInfoSet, DeviceInfoData, NULL, NULL);
+        Result = SetupInstallServicesFromInfSectionExW(hInf, ServiceSection, Flags, DeviceInfoSet, DeviceInfoData, ServiceName, NULL);
         if (Result && (Flags & SPSVCINST_ASSOCSERVICE))
         {
             AssociatedService = ServiceName;
@@ -4525,79 +5040,79 @@ SetupDiInstallDevice(
             if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED)
                 RebootRequired = TRUE;
         }
-cleanup:
+nextfile:
         HeapFree(GetProcessHeap(), 0, ServiceName);
         HeapFree(GetProcessHeap(), 0, ServiceSection);
         if (!Result)
-        {
-            HeapFree(GetProcessHeap(), 0, (LPWSTR)AssociatedService);
-            SetupCloseInfFile(hInf);
-            return FALSE;
-        }
+            goto cleanup;
         Result = SetupFindNextLine(&ContextService, &ContextService);
     }
 
     /* Copy .inf file to Inf\ directory */
     FIXME("FIXME: Copy .inf file to Inf\\ directory\n"); /* SetupCopyOEMInf */
 
+    /* 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(hWnd, hInf, SectionName,
+        SPINST_REGISTRY, hKey, NULL, 0,
+        SetupDefaultQueueCallbackW, NULL,
+        NULL, NULL);
+    if (!Result)
+        goto cleanup;
+
     /* Write information to enum key */
-    rc = RegOpenKeyExW(DevInfoSet->HKLM,
-        EnumKeyName,
-        0,
-        KEY_ENUMERATE_SUB_KEYS,
-        &hEnumKey);
-    if (rc != ERROR_SUCCESS)
-    {
-       SetLastError(rc);
-       HeapFree(GetProcessHeap(), 0, (LPWSTR)AssociatedService);
-       SetupCloseInfFile(hInf);
-       return FALSE;
-    }
-    rc = RegOpenKeyExW(
-        hEnumKey,
-        DevInfo->DeviceName,
-        0, /* Options */
-        KEY_SET_VALUE,
-        &hKey);
-    RegCloseKey(hEnumKey);
-    if (rc != ERROR_SUCCESS)
-    {
-       SetLastError(rc);
-       HeapFree(GetProcessHeap(), 0, (LPWSTR)AssociatedService);
-       SetupCloseInfFile(hInf);
-       return FALSE;
-    }
-    FIXME("FIXME: Write information to enum key\n");
-    FIXME("ParentIdPrefix  : '%S'\n", L"0000"); /* FIXME */
-    //FIXME("Service         : '%S'\n", AssociatedService);
-    FIXME("Class           : '%S'\n", L"???"); /* FIXME: SetupDiGetINFClass */
-    FIXME("ClassGUID       : '%S'\n", L"???"); /* FIXME: SetupDiGetINFClass */
-    //FIXME("DeviceDesc      : '%S'\n", DriverInfo->Info.Description);
-    FIXME("Driver          : '%S'\n", L"???"); /* FIXME: autogenerated key */
-    //FIXME("Mfg             : '%S'\n", DriverInfo->Info.MfgName);
+    TRACE("Write information to enum key\n");
+    TRACE("Service         : '%S'\n", AssociatedService);
+    TRACE("Class           : '%S'\n", ClassName);
+    TRACE("ClassGUID       : '%S'\n", lpFullGuidString);
+    TRACE("DeviceDesc      : '%S'\n", DriverInfo->Info.Description);
+    TRACE("Mfg             : '%S'\n", DriverInfo->Info.MfgName);
     rc = RegSetValueEx(hKey, L"Service", 0, REG_SZ, (const BYTE *)AssociatedService, (wcslen(AssociatedService) + 1) * sizeof(WCHAR));
+    if (rc == ERROR_SUCCESS)
+        rc = RegSetValueEx(hKey, L"Class", 0, REG_SZ, (const BYTE *)ClassName, (wcslen(ClassName) + 1) * sizeof(WCHAR));
+    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 *)DriverInfo->Info.Description, (wcslen(DriverInfo->Info.Description) + 1) * sizeof(WCHAR));
     if (rc == ERROR_SUCCESS)
         rc = RegSetValueEx(hKey, L"Mfg", 0, REG_SZ, (const BYTE *)DriverInfo->Info.MfgName, (wcslen(DriverInfo->Info.MfgName) + 1) * sizeof(WCHAR));
-    RegCloseKey(hKey);
     if (rc != ERROR_SUCCESS)
     {
        SetLastError(rc);
-       HeapFree(GetProcessHeap(), 0, (LPWSTR)AssociatedService);
-       SetupCloseInfFile(hInf);
-       return FALSE;
+       goto cleanup;
     }
 
-    /* Load the driver/call AddDevice */
-    FIXME("FIXME: Load the driver/call AddDevice\n");
-
-    /* Send IRP_MN_START_DEVICE if needed */
-    //if (!RebootRequired && !(Flags & (DI_NEEDRESTART | DI_NEEDREBOOT | DI_DONOTCALLCONFIGMG)))
-        FIXME("FIXME: Send IRP_MN_START_DEVICE\n");
+    /* Start the device */
+    if (!RebootRequired && !(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);
-    SetupCloseInfFile(hInf);
-    return TRUE;
+    HeapFree(GetProcessHeap(), 0, ClassName);
+    HeapFree(GetProcessHeap(), 0, lpFullGuidString);
+    if (hInf != INVALID_HANDLE_VALUE)
+        SetupCloseInfFile(hInf);
+
+    TRACE("Returning %d\n", ret);
+    return ret;
 }