[SETUPAPI] Fix broken control definition.
[reactos.git] / dll / win32 / setupapi / devclass.c
index f409695..86fc33d 100644 (file)
 
 #include "setupapi_private.h"
 
-WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+#include <wingdi.h>
+#include <shellapi.h>
+#include <strsafe.h>
 
 /* Unicode constants */
 static const WCHAR BackSlash[] = {'\\',0};
-static const WCHAR Class[]  = {'C','l','a','s','s',0};
 static const WCHAR ClassGUID[]  = {'C','l','a','s','s','G','U','I','D',0};
 static const WCHAR ClassInstall32[]  = {'C','l','a','s','s','I','n','s','t','a','l','l','3','2',0};
 static const WCHAR DotServices[]  = {'.','S','e','r','v','i','c','e','s',0};
 static const WCHAR InterfaceInstall32[]  = {'I','n','t','e','r','f','a','c','e','I','n','s','t','a','l','l','3','2',0};
 static const WCHAR SetupapiDll[]  = {'s','e','t','u','p','a','p','i','.','d','l','l',0};
-static const WCHAR Version[]  = {'V','e','r','s','i','o','n',0};
 
 typedef BOOL
 (WINAPI* PROPERTY_PAGE_PROVIDER) (
@@ -76,6 +76,7 @@ static const INSTALL_PARAMS_DATA InstallParamsData[] = {
 };
 #undef ADD_PARAM_HANDLER
 
+#define UNKNOWN_ICON_INDEX 18
 
 /***********************************************************************
  *             SetupDiDestroyClassImageList(SETUPAPI.@)
@@ -99,10 +100,17 @@ SetupDiDestroyClassImageList(
         SetLastError(ERROR_INVALID_USER_BUFFER);
     else
     {
-        //DestroyIcon()
-        //ImageList_Destroy();
-        FIXME("Stub %p\n", ClassImageListData);
-        SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+        /* If Reserved wasn't NULL, then this is valid too */
+        if (ClassImageListData->ImageList)
+        {
+            ImageList_Destroy(ClassImageListData->ImageList);
+            ClassImageListData->ImageList = NULL;
+        }
+
+        MyFree(list);
+        ClassImageListData->Reserved = 0;
+
+        ret = TRUE;
     }
 
     TRACE("Returning %d\n", ret);
@@ -155,10 +163,16 @@ SETUP_CreateDevicesListFromEnumerator(
         rc = RegOpenKeyExW(hEnumeratorKey, KeyBuffer, 0, KEY_ENUMERATE_SUB_KEYS, &hDeviceIdKey);
         if (rc != ERROR_SUCCESS)
             goto cleanup;
-        strcpyW(InstancePath, Enumerator);
-        strcatW(InstancePath, BackSlash);
-        strcatW(InstancePath, KeyBuffer);
-        strcatW(InstancePath, BackSlash);
+
+        if (FAILED(StringCchCopyW(InstancePath, _countof(InstancePath), Enumerator)) ||
+            FAILED(StringCchCatW(InstancePath, _countof(InstancePath), BackSlash))  ||
+            FAILED(StringCchCatW(InstancePath, _countof(InstancePath), KeyBuffer))  ||
+            FAILED(StringCchCatW(InstancePath, _countof(InstancePath), BackSlash)))
+        {
+            rc = ERROR_GEN_FAILURE;
+            goto cleanup;
+        }
+
         pEndOfInstancePath = &InstancePath[strlenW(InstancePath)];
 
         /* Enumerate instance IDs (subkeys of hDeviceIdKey) */
@@ -330,9 +344,6 @@ cleanup:
     return rc;
 }
 
-/***********************************************************************
- *             SetupDiGetClassImageIndex (SETUPAPI.@)
- */
 static BOOL
 SETUP_GetIconIndex(
     IN HKEY hClassKey,
@@ -378,6 +389,9 @@ cleanup:
     return ret;
 }
 
+/***********************************************************************
+ *             SetupDiGetClassImageIndex (SETUPAPI.@)
+ */
 BOOL WINAPI
 SetupDiGetClassImageIndex(
     IN PSP_CLASSIMAGELIST_DATA ClassImageListData,
@@ -397,8 +411,6 @@ SetupDiGetClassImageIndex(
         SetLastError(ERROR_INVALID_USER_BUFFER);
     else if (list->magic != SETUP_CLASS_IMAGE_LIST_MAGIC)
         SetLastError(ERROR_INVALID_USER_BUFFER);
-    else if (!ImageIndex)
-        SetLastError(ERROR_INVALID_PARAMETER);
     else
     {
         DWORD i;
@@ -458,6 +470,105 @@ SetupDiGetClassImageListExA(
     return ret;
 }
 
+static BOOL WINAPI 
+SETUP_GetClassIconInfo(IN CONST GUID *ClassGuid, OUT PINT OutIndex, OUT LPWSTR *OutDllName)
+{
+    LPWSTR Buffer = NULL;
+    INT iconIndex = -UNKNOWN_ICON_INDEX;
+    HKEY hKey = INVALID_HANDLE_VALUE;
+    BOOL ret = FALSE;
+
+    if (ClassGuid)
+    {
+        hKey = SetupDiOpenClassRegKey(ClassGuid, KEY_QUERY_VALUE);
+        if (hKey != INVALID_HANDLE_VALUE)
+        {
+            SETUP_GetIconIndex(hKey, &iconIndex);
+        }
+    }
+
+    if (iconIndex > 0)
+    {
+        /* Look up icon in dll specified by Installer32 or EnumPropPages32 key */
+        PWCHAR Comma;
+        LONG rc;
+        DWORD dwRegType, dwLength;
+        rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, &dwRegType, NULL, &dwLength);
+        if (rc == ERROR_SUCCESS && dwRegType == REG_SZ)
+        {
+            Buffer = MyMalloc(dwLength + sizeof(WCHAR));
+            if (Buffer == NULL)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto cleanup;
+            }
+            rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, NULL, (LPBYTE)Buffer, &dwLength);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            /* make sure the returned buffer is NULL-terminated */
+            Buffer[dwLength / sizeof(WCHAR)] = 0;
+        }
+        else if
+            (ERROR_SUCCESS == (rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, &dwRegType, NULL, &dwLength))
+            && dwRegType == REG_SZ)
+        {
+            Buffer = MyMalloc(dwLength + sizeof(WCHAR));
+            if (Buffer == NULL)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto cleanup;
+            }
+            rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, NULL, (LPBYTE)Buffer, &dwLength);
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                goto cleanup;
+            }
+            /* make sure the returned buffer is NULL-terminated */
+            Buffer[dwLength / sizeof(WCHAR)] = 0;
+        }
+        else
+        {
+            /* Unable to find where to load the icon */
+            SetLastError(ERROR_FILE_NOT_FOUND);
+            goto cleanup;
+        }
+        Comma = strchrW(Buffer, ',');
+        if (!Comma)
+        {
+            SetLastError(ERROR_GEN_FAILURE);
+            goto cleanup;
+        }
+        *Comma = '\0';
+        *OutDllName = Buffer;
+    }
+    else
+    {
+        /* Look up icon in setupapi.dll */
+        iconIndex = -iconIndex;
+        *OutDllName = NULL;
+    }
+
+    *OutIndex = iconIndex;
+    ret = TRUE;
+
+    TRACE("Icon index %d, dll name %s\n", iconIndex, debugstr_w(*OutDllName ? *OutDllName : SetupapiDll));
+
+cleanup:
+
+    if (hKey != INVALID_HANDLE_VALUE)
+        RegCloseKey(hKey);
+
+    if (Buffer && !ret)
+        MyFree(Buffer);
+
+    return ret;
+}
+
+
 /***********************************************************************
  *             SetupDiGetClassImageListExW(SETUPAPI.@)
  */
@@ -486,6 +597,7 @@ SetupDiGetClassImageListExW(
         HICON hIcon;
         DWORD size;
         INT i, bpp;
+        UINT idx;
 
         /* Get list of all class GUIDs in given computer */
         ret = SetupDiBuildClassInfoListExW(
@@ -569,24 +681,45 @@ SetupDiGetClassImageListExW(
         for (i = 0; i < list->NumberOfGuids; i++)
         {
             INT miniIconIndex;
+            LPWSTR DllName = NULL;
 
-            ret = SetupDiLoadClassIcon(
-                &list->Guids[i],
-                NULL,
-                &miniIconIndex);
-            if (ret)
+            if (SETUP_GetClassIconInfo(&list->Guids[i], &miniIconIndex, &DllName))
             {
-                hIcon = LoadImage(hInstance, MAKEINTRESOURCE(miniIconIndex), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
-                if (hIcon)
+                if (DllName && ExtractIconExW(DllName, -miniIconIndex, NULL, &hIcon, 1) == 1)
                 {
                     list->IconIndexes[i] = ImageList_AddIcon(ClassImageListData->ImageList, hIcon);
-                    DestroyIcon(hIcon);
                 }
+                else if(!DllName)
+                {
+                    hIcon = LoadImage(hInstance, MAKEINTRESOURCE(miniIconIndex), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
+                    list->IconIndexes[i] = ImageList_AddIcon(ClassImageListData->ImageList, hIcon);
+                }
+
+                if(hIcon)
+                    DestroyIcon(hIcon);
                 else
                     list->IconIndexes[i] = -1;
+
+                if(DllName)
+                    MyFree(DllName);
             }
             else
+            {
                 list->IconIndexes[i] = -1; /* Special value to indicate that the icon is unavailable */
+            }
+        }
+
+        /* Finally, add the overlay icons to the image list */
+        for (i = 0; i <= 2; i++)
+        {
+            hIcon = LoadImage(hInstance, MAKEINTRESOURCE(500 + i), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
+            if (hIcon)
+            {
+                idx = ImageList_AddIcon(ClassImageListData->ImageList, hIcon);
+                if (idx != -1)
+                    ImageList_SetOverlayImage(ClassImageListData->ImageList, idx, i + 1);
+                DestroyIcon(hIcon);
+            }
         }
 
         ret = TRUE;
@@ -646,105 +779,40 @@ SetupDiLoadClassIcon(
     OUT HICON *LargeIcon OPTIONAL,
     OUT PINT MiniIconIndex OPTIONAL)
 {
-    LPWSTR Buffer = NULL;
-    LPCWSTR DllName;
-    INT iconIndex = -18;
-    HKEY hKey = INVALID_HANDLE_VALUE;
-
+    INT iconIndex = 0;
+    LPWSTR DllName = NULL;
     BOOL ret = FALSE;
+    HICON hIcon = NULL;
 
-    if (ClassGuid)
+    if (LargeIcon)
     {
-        hKey = SetupDiOpenClassRegKey(ClassGuid, KEY_QUERY_VALUE);
-        if (hKey != INVALID_HANDLE_VALUE)
-            SETUP_GetIconIndex(hKey, &iconIndex);
-    }
+        if(!SETUP_GetClassIconInfo(ClassGuid, &iconIndex, &DllName))
+            goto cleanup;
 
-    if (iconIndex > 0)
-    {
-        /* Look up icon in dll specified by Installer32 or EnumPropPages32 key */
-        PWCHAR Comma;
-        LONG rc;
-        DWORD dwRegType, dwLength;
-        rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, &dwRegType, NULL, &dwLength);
-        if (rc == ERROR_SUCCESS && dwRegType == REG_SZ)
+        if (!DllName || ExtractIconExW(DllName, -iconIndex, &hIcon, NULL, 1) != 1 || hIcon == NULL)
         {
-            Buffer = MyMalloc(dwLength + sizeof(WCHAR));
-            if (Buffer == NULL)
-            {
-                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-                goto cleanup;
-            }
-            rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, NULL, (LPBYTE)Buffer, &dwLength);
-            if (rc != ERROR_SUCCESS)
-            {
-                SetLastError(rc);
-                goto cleanup;
-            }
-            /* make sure the returned buffer is NULL-terminated */
-            Buffer[dwLength / sizeof(WCHAR)] = 0;
-        }
-        else if
-            (ERROR_SUCCESS == (rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, &dwRegType, NULL, &dwLength))
-            && dwRegType == REG_SZ)
-        {
-            Buffer = MyMalloc(dwLength + sizeof(WCHAR));
-            if (Buffer == NULL)
-            {
-                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-                goto cleanup;
-            }
-            rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, NULL, (LPBYTE)Buffer, &dwLength);
-            if (rc != ERROR_SUCCESS)
-            {
-                SetLastError(rc);
+            /* load the default unknown device icon if ExtractIcon failed */
+            if(DllName)
+                iconIndex = UNKNOWN_ICON_INDEX;
+
+            hIcon = LoadImage(hInstance, MAKEINTRESOURCE(iconIndex), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
+
+            if(!hIcon)
                 goto cleanup;
-            }
-            /* make sure the returned buffer is NULL-terminated */
-            Buffer[dwLength / sizeof(WCHAR)] = 0;
-        }
-        else
-        {
-            /* Unable to find where to load the icon */
-            SetLastError(ERROR_FILE_NOT_FOUND);
-            goto cleanup;
         }
-        Comma = strchrW(Buffer, ',');
-        if (!Comma)
-        {
-            SetLastError(ERROR_GEN_FAILURE);
-            goto cleanup;
-        }
-        *Comma = '\0';
-        DllName = Buffer;
-    }
-    else
-    {
-        /* Look up icon in setupapi.dll */
-        DllName = SetupapiDll;
-        iconIndex = -iconIndex;
-    }
 
-    TRACE("Icon index %d, dll name %s\n", iconIndex, debugstr_w(DllName));
-    if (LargeIcon)
-    {
-        *LargeIcon = LoadImage(hInstance, MAKEINTRESOURCE(iconIndex), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
-        if (!*LargeIcon)
-        {
-            SetLastError(ERROR_INVALID_INDEX);
-            goto cleanup;
-        }
+        *LargeIcon = hIcon;
     }
+
     if (MiniIconIndex)
         *MiniIconIndex = iconIndex;
+
     ret = TRUE;
 
 cleanup:
-    if (hKey != INVALID_HANDLE_VALUE)
-        RegCloseKey(hKey);
 
-    if (Buffer != NULL)
-        MyFree(Buffer);
+    if(DllName)
+        MyFree(DllName);
 
     TRACE("Returning %d\n", ret);
     return ret;
@@ -802,6 +870,7 @@ SetupDiInstallClassExW(
         InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
         if (!SetupDiGetDeviceInstallParamsW(hDeviceInfo, NULL, &InstallParams))
             goto cleanup;
+
         InstallParams.Flags &= ~(DI_NOVCP | DI_NOBROWSE | DI_QUIETINSTALL);
         InstallParams.Flags |= Flags & (DI_NOVCP | DI_NOBROWSE | DI_QUIETINSTALL);
         if (Flags & DI_NOVCP)
@@ -881,9 +950,9 @@ SetupDiInstallClassExW(
             if (!ret)
                 goto cleanup;
 
-            /* Install .Services section */
+            /* OPTIONAL: Install .Services section */
             lstrcatW(SectionName, DotServices);
-            ret = SetupInstallServicesFromInfSectionExW(
+            SetupInstallServicesFromInfSectionExW(
                 hInf,
                 SectionName,
                 0,
@@ -891,9 +960,6 @@ SetupDiInstallClassExW(
                 NULL,
                 NULL,
                 NULL);
-            if (!ret)
-                goto cleanup;
-
             ret = TRUE;
         }
 
@@ -1114,9 +1180,12 @@ SetupDiGetClassDevPropertySheetsA(
         PropertySheetHeader, PropertySheetHeaderPageListSize,
         RequiredSize, PropertySheetType);
 
-    psh.dwFlags = PropertySheetHeader->dwFlags;
-    psh.phpage = PropertySheetHeader->phpage;
-    psh.nPages = PropertySheetHeader->nPages;
+    if(PropertySheetHeader)
+    {
+        psh.dwFlags = PropertySheetHeader->dwFlags;
+        psh.phpage = PropertySheetHeader->phpage;
+        psh.nPages = PropertySheetHeader->nPages;
+    }
 
     ret = SetupDiGetClassDevPropertySheetsW(DeviceInfoSet, DeviceInfoData, PropertySheetHeader ? &psh : NULL,
                                             PropertySheetHeaderPageListSize, RequiredSize,
@@ -1159,6 +1228,138 @@ SETUP_GetClassDevPropertySheetsCallback(
     return PropPageData->DontCancel;
 }
 
+static DWORD
+SETUP_GetValueString(
+    IN HKEY hKey,
+    IN LPWSTR lpValueName,
+    OUT LPWSTR *lpString)
+{
+    LPWSTR lpBuffer;
+    DWORD dwLength = 0;
+    DWORD dwRegType;
+    DWORD rc;
+
+    *lpString = NULL;
+
+    RegQueryValueExW(hKey, lpValueName, NULL, &dwRegType, NULL, &dwLength);
+
+    if (dwLength == 0 || dwRegType != REG_SZ)
+        return ERROR_FILE_NOT_FOUND;
+
+    lpBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength + sizeof(WCHAR));
+    if (lpBuffer == NULL)
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    rc = RegQueryValueExW(hKey, lpValueName, NULL, NULL, (LPBYTE)lpBuffer, &dwLength);
+    if (rc != ERROR_SUCCESS)
+    {
+        HeapFree(GetProcessHeap(), 0, lpBuffer);
+        return rc;
+    }
+
+    lpBuffer[dwLength / sizeof(WCHAR)] = UNICODE_NULL;
+
+    *lpString = lpBuffer;
+
+    return ERROR_SUCCESS;
+}
+
+static
+BOOL
+SETUP_CallInstaller(
+    IN DI_FUNCTION InstallFunction,
+    IN HDEVINFO DeviceInfoSet,
+    IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
+    IN PSP_ADDPROPERTYPAGE_DATA PageData)
+{
+    PSP_CLASSINSTALL_HEADER pClassInstallParams = NULL;
+    DWORD dwSize = 0;
+    DWORD dwError;
+    BOOL ret = TRUE;
+
+    /* Get the size of the old class install parameters */
+    if (!SetupDiGetClassInstallParams(DeviceInfoSet,
+                                      DeviceInfoData,
+                                      NULL,
+                                      0,
+                                      &dwSize))
+    {
+        dwError = GetLastError();
+        if (dwError != ERROR_INSUFFICIENT_BUFFER)
+        {
+            ERR("SetupDiGetClassInstallParams failed (Error %lu)\n", dwError);
+            return FALSE;
+        }
+    }
+
+    /* Allocate a buffer for the old class install parameters */
+    pClassInstallParams = HeapAlloc(GetProcessHeap(), 0, dwSize);
+    if (pClassInstallParams == NULL)
+    {
+        ERR("Failed to allocate the parameters buffer!\n");
+        return FALSE;
+    }
+
+    /* Save the old class install parameters */
+    if (!SetupDiGetClassInstallParams(DeviceInfoSet,
+                                      DeviceInfoData,
+                                      pClassInstallParams,
+                                      dwSize,
+                                      &dwSize))
+    {
+        ERR("SetupDiGetClassInstallParams failed (Error %lu)\n", GetLastError());
+        ret = FALSE;
+        goto done;
+    }
+
+    /* Set the new class install parameters */
+    if (!SetupDiSetClassInstallParams(DeviceInfoSet,
+                                      DeviceInfoData,
+                                      &PageData->ClassInstallHeader,
+                                      sizeof(SP_ADDPROPERTYPAGE_DATA)))
+    {
+        ERR("SetupDiSetClassInstallParams failed (Error %lu)\n", dwError);
+        ret = FALSE;
+        goto done;
+    }
+
+    /* Call the installer */
+    ret = SetupDiCallClassInstaller(InstallFunction,
+                                    DeviceInfoSet,
+                                    DeviceInfoData);
+    if (ret == FALSE)
+    {
+        ERR("SetupDiCallClassInstaller failed\n");
+        goto done;
+    }
+
+    /* Read the new class installer parameters */
+    if (!SetupDiGetClassInstallParams(DeviceInfoSet,
+                                      DeviceInfoData,
+                                      &PageData->ClassInstallHeader,
+                                      sizeof(SP_ADDPROPERTYPAGE_DATA),
+                                      NULL))
+    {
+        ERR("SetupDiGetClassInstallParams failed (Error %lu)\n", GetLastError());
+        ret = FALSE;
+        goto done;
+    }
+
+done:
+    /* Restore and free the old class install parameters */
+    if (pClassInstallParams != NULL)
+    {
+        SetupDiSetClassInstallParams(DeviceInfoSet,
+                                     DeviceInfoData,
+                                     pClassInstallParams,
+                                     dwSize);
+
+        HeapFree(GetProcessHeap(), 0, pClassInstallParams);
+    }
+
+    return ret;
+}
+
 /***********************************************************************
  *             SetupDiGetClassDevPropertySheetsW(SETUPAPI.@)
  */
@@ -1171,7 +1372,8 @@ SetupDiGetClassDevPropertySheetsW(
     OUT PDWORD RequiredSize OPTIONAL,
     IN DWORD PropertySheetType)
 {
-    struct DeviceInfoSet *list;
+    struct DeviceInfoSet *devInfoSet = NULL;
+    struct DeviceInfo *devInfo = NULL;
     BOOL ret = FALSE;
 
     TRACE("%p %p %p 0%lx %p 0x%lx\n", DeviceInfoSet, DeviceInfoData,
@@ -1182,7 +1384,7 @@ SetupDiGetClassDevPropertySheetsW(
         SetLastError(ERROR_INVALID_HANDLE);
     else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
         SetLastError(ERROR_INVALID_HANDLE);
-    else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    else if ((devInfoSet = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
         SetLastError(ERROR_INVALID_HANDLE);
     else if (!PropertySheetHeader)
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -1190,7 +1392,7 @@ SetupDiGetClassDevPropertySheetsW(
         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))
+    else if (!DeviceInfoData && IsEqualIID(&devInfoSet->ClassGuid, &GUID_NULL))
         SetLastError(ERROR_INVALID_PARAMETER);
     else if (PropertySheetType != DIGCDP_FLAG_ADVANCED
           && PropertySheetType != DIGCDP_FLAG_BASIC
@@ -1205,54 +1407,68 @@ SetupDiGetClassDevPropertySheetsW(
         HMODULE hModule = NULL;
         PROPERTY_PAGE_PROVIDER pPropPageProvider = NULL;
         struct ClassDevPropertySheetsData PropPageData;
-        DWORD dwLength, dwRegType;
-        DWORD InitialNumberOfPages;
+        SP_ADDPROPERTYPAGE_DATA InstallerPropPageData;
+        DWORD InitialNumberOfPages, i;
         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;
+            devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
 
-        rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, &dwRegType, NULL, &dwLength);
-        if (rc == ERROR_FILE_NOT_FOUND)
+        /* Get the class property page provider */
+        if (devInfoSet->hmodClassPropPageProvider == NULL)
         {
-            /* 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;
-        }
+            hKey = SetupDiOpenClassRegKeyExW(devInfo ? &devInfo->ClassGuid : &devInfoSet->ClassGuid, KEY_QUERY_VALUE,
+                    DIOCR_INSTALLER, NULL/*devInfoSet->MachineName + 2*/, NULL);
+            if (hKey != INVALID_HANDLE_VALUE)
+            {
+                rc = SETUP_GetValueString(hKey, REGSTR_VAL_ENUMPROPPAGES_32, &PropPageProvider);
+                if (rc == ERROR_SUCCESS)
+                {
+                    rc = GetFunctionPointer(PropPageProvider, &hModule, (PVOID*)&pPropPageProvider);
+                    if (rc != ERROR_SUCCESS)
+                    {
+                        SetLastError(ERROR_INVALID_PROPPAGE_PROVIDER);
+                        goto cleanup;
+                    }
 
-        PropPageProvider = HeapAlloc(GetProcessHeap(), 0, dwLength + sizeof(WCHAR));
-        if (!PropPageProvider)
-        {
-            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-            goto cleanup;
-        }
-        rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, NULL, (LPBYTE)PropPageProvider, &dwLength);
-        if (rc != ERROR_SUCCESS)
-        {
-            SetLastError(rc);
-            goto cleanup;
+                    devInfoSet->hmodClassPropPageProvider = hModule;
+                    devInfoSet->pClassPropPageProvider = pPropPageProvider;
+
+                    HeapFree(GetProcessHeap(), 0, PropPageProvider);
+                    PropPageProvider = NULL;
+                }
+
+                RegCloseKey(hKey);
+                hKey = INVALID_HANDLE_VALUE;
+            }
         }
-        PropPageProvider[dwLength / sizeof(WCHAR)] = 0;
 
-        rc = GetFunctionPointer(PropPageProvider, &hModule, (PVOID*)&pPropPageProvider);
-        if (rc != ERROR_SUCCESS)
+        /* Get the device property page provider */
+        if (devInfo != NULL && devInfo->hmodDevicePropPageProvider == NULL)
         {
-            SetLastError(ERROR_INVALID_PROPPAGE_PROVIDER);
-            goto cleanup;
+            hKey = SETUPDI_OpenDrvKey(devInfoSet->HKLM, devInfo, KEY_QUERY_VALUE);
+            if (hKey != INVALID_HANDLE_VALUE)
+            {
+                rc = SETUP_GetValueString(hKey, REGSTR_VAL_ENUMPROPPAGES_32, &PropPageProvider);
+                if (rc == ERROR_SUCCESS)
+                {
+                    rc = GetFunctionPointer(PropPageProvider, &hModule, (PVOID*)&pPropPageProvider);
+                    if (rc != ERROR_SUCCESS)
+                    {
+                        SetLastError(ERROR_INVALID_PROPPAGE_PROVIDER);
+                        goto cleanup;
+                    }
+
+                    devInfo->hmodDevicePropPageProvider = hModule;
+                    devInfo->pDevicePropPageProvider = pPropPageProvider;
+
+                    HeapFree(GetProcessHeap(), 0, PropPageProvider);
+                    PropPageProvider = NULL;
+                }
+
+                RegCloseKey(hKey);
+                hKey = INVALID_HANDLE_VALUE;
+            }
         }
 
         InitialNumberOfPages = PropertySheetHeader->nPages;
@@ -1267,7 +1483,37 @@ SetupDiGetClassDevPropertySheetsW(
         PropPageData.NumberOfPages = 0;
         PropPageData.DontCancel = (RequiredSize != NULL) ? TRUE : FALSE;
 
-        pPropPageProvider(&Request, SETUP_GetClassDevPropertySheetsCallback, (LPARAM)&PropPageData);
+        /* Call the class property page provider */
+        if (devInfoSet->pClassPropPageProvider != NULL)
+            ((PROPERTY_PAGE_PROVIDER)devInfoSet->pClassPropPageProvider)(&Request, SETUP_GetClassDevPropertySheetsCallback, (LPARAM)&PropPageData);
+
+        /* Call the device property page provider */
+        if (devInfo != NULL && devInfo->pDevicePropPageProvider != NULL)
+            ((PROPERTY_PAGE_PROVIDER)devInfo->pDevicePropPageProvider)(&Request, SETUP_GetClassDevPropertySheetsCallback, (LPARAM)&PropPageData);
+
+        /* Call the class installer and add the returned pages */
+        ZeroMemory(&InstallerPropPageData, sizeof(SP_ADDPROPERTYPAGE_DATA));
+        InstallerPropPageData.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
+        InstallerPropPageData.ClassInstallHeader.InstallFunction = DIF_ADDPROPERTYPAGE_ADVANCED;
+        InstallerPropPageData.hwndWizardDlg = PropertySheetHeader->hwndParent;
+
+        if (SETUP_CallInstaller(DIF_ADDPROPERTYPAGE_ADVANCED,
+                                DeviceInfoSet,
+                                DeviceInfoData,
+                                &InstallerPropPageData))
+        {
+            for (i = 0; i < InstallerPropPageData.NumDynamicPages; i++)
+            {
+                if (PropPageData.PropertySheetHeader->nPages < PropertySheetHeaderPageListSize)
+                {
+                    PropPageData.PropertySheetHeader->phpage[PropPageData.PropertySheetHeader->nPages] = 
+                        InstallerPropPageData.DynamicPages[i];
+                    PropPageData.PropertySheetHeader->nPages++;
+                }
+            }
+
+            PropPageData.NumberOfPages += InstallerPropPageData.NumDynamicPages;
+        }
 
         if (RequiredSize)
             *RequiredSize = PropPageData.NumberOfPages;
@@ -1284,8 +1530,9 @@ SetupDiGetClassDevPropertySheetsW(
 cleanup:
         if (hKey != INVALID_HANDLE_VALUE)
             RegCloseKey(hKey);
-        HeapFree(GetProcessHeap(), 0, PropPageProvider);
-        FreeFunctionPointer(hModule, pPropPageProvider);
+
+        if (PropPageProvider != NULL)
+            HeapFree(GetProcessHeap(), 0, PropPageProvider);
     }
 
     TRACE("Returning %d\n", ret);