xref: /reactos/dll/win32/setupapi/devclass.c (revision 50cf16b3)
1 /*
2  * SetupAPI device class-related functions
3  *
4  * Copyright 2000 Andreas Mohr for CodeWeavers
5  *           2005-2006 Herv� Poussineau (hpoussin@reactos.org)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "setupapi_private.h"
23 
24 #include <wingdi.h>
25 #include <shellapi.h>
26 #include <strsafe.h>
27 
28 /* Unicode constants */
29 static const WCHAR BackSlash[] = {'\\',0};
30 static const WCHAR ClassGUID[]  = {'C','l','a','s','s','G','U','I','D',0};
31 static const WCHAR ClassInstall32[]  = {'C','l','a','s','s','I','n','s','t','a','l','l','3','2',0};
32 static const WCHAR DotServices[]  = {'.','S','e','r','v','i','c','e','s',0};
33 static const WCHAR InterfaceInstall32[]  = {'I','n','t','e','r','f','a','c','e','I','n','s','t','a','l','l','3','2',0};
34 static const WCHAR SetupapiDll[]  = {'s','e','t','u','p','a','p','i','.','d','l','l',0};
35 
36 typedef BOOL
37 (WINAPI* PROPERTY_PAGE_PROVIDER) (
38     IN PSP_PROPSHEETPAGE_REQUEST PropPageRequest,
39     IN LPFNADDPROPSHEETPAGE fAddFunc,
40     IN LPARAM lParam);
41 typedef BOOL
42 (*UPDATE_CLASS_PARAM_HANDLER) (
43     IN HDEVINFO DeviceInfoSet,
44     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
45     IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
46     IN DWORD ClassInstallParamsSize);
47 
48 static BOOL
49 SETUP_PropertyChangeHandler(
50     IN HDEVINFO DeviceInfoSet,
51     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
52     IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
53     IN DWORD ClassInstallParamsSize);
54 
55 static BOOL
56 SETUP_PropertyAddPropertyAdvancedHandler(
57     IN HDEVINFO DeviceInfoSet,
58     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
59     IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
60     IN DWORD ClassInstallParamsSize);
61 
62 typedef struct _INSTALL_PARAMS_DATA
63 {
64     DI_FUNCTION Function;
65     UPDATE_CLASS_PARAM_HANDLER UpdateHandler;
66     ULONG ParamsSize;
67     LONG FieldOffset;
68 } INSTALL_PARAMS_DATA;
69 
70 #define ADD_PARAM_HANDLER(Function, UpdateHandler, ParamsType, ParamsField) \
71     { Function, UpdateHandler, sizeof(ParamsType), FIELD_OFFSET(struct ClassInstallParams, ParamsField) },
72 
73 static const INSTALL_PARAMS_DATA InstallParamsData[] = {
74     ADD_PARAM_HANDLER(DIF_PROPERTYCHANGE, SETUP_PropertyChangeHandler, SP_PROPCHANGE_PARAMS, PropChangeParams)
75     ADD_PARAM_HANDLER(DIF_ADDPROPERTYPAGE_ADVANCED, SETUP_PropertyAddPropertyAdvancedHandler, SP_ADDPROPERTYPAGE_DATA, AddPropertyPageData)
76 };
77 #undef ADD_PARAM_HANDLER
78 
79 #define UNKNOWN_ICON_INDEX 18
80 
81 /***********************************************************************
82  *		SetupDiDestroyClassImageList(SETUPAPI.@)
83  */
84 BOOL WINAPI
85 SetupDiDestroyClassImageList(
86     IN PSP_CLASSIMAGELIST_DATA ClassImageListData)
87 {
88     struct ClassImageList *list;
89     BOOL ret = FALSE;
90 
91     TRACE("%p\n", ClassImageListData);
92 
93     if (!ClassImageListData)
94         SetLastError(ERROR_INVALID_PARAMETER);
95     else if (ClassImageListData->cbSize != sizeof(SP_CLASSIMAGELIST_DATA))
96         SetLastError(ERROR_INVALID_USER_BUFFER);
97     else if ((list = (struct ClassImageList *)ClassImageListData->Reserved) == NULL)
98         SetLastError(ERROR_INVALID_USER_BUFFER);
99     else if (list->magic != SETUP_CLASS_IMAGE_LIST_MAGIC)
100         SetLastError(ERROR_INVALID_USER_BUFFER);
101     else
102     {
103         /* If Reserved wasn't NULL, then this is valid too */
104         if (ClassImageListData->ImageList)
105         {
106             ImageList_Destroy(ClassImageListData->ImageList);
107             ClassImageListData->ImageList = NULL;
108         }
109 
110         MyFree(list);
111         ClassImageListData->Reserved = 0;
112 
113         ret = TRUE;
114     }
115 
116     TRACE("Returning %d\n", ret);
117     return ret;
118 }
119 
120 /***********************************************************************
121  *		SETUP_CreateDevicesListFromEnumerator
122  *
123  * PARAMS
124  *   list [IO] Device info set to fill with discovered devices.
125  *   pClassGuid [I] If specified, only devices which belong to this class will be added.
126  *   Enumerator [I] Location to search devices to add.
127  *   hEnumeratorKey [I] Registry key corresponding to Enumerator key. Must have KEY_ENUMERATE_SUB_KEYS right.
128  *
129  * RETURNS
130  *   Success: ERROR_SUCCESS.
131  *   Failure: an error code.
132  */
133 static LONG
134 SETUP_CreateDevicesListFromEnumerator(
135     IN OUT struct DeviceInfoSet *list,
136     IN CONST GUID *pClassGuid OPTIONAL,
137     IN LPCWSTR Enumerator,
138     IN HKEY hEnumeratorKey) /* handle to Enumerator registry key */
139 {
140     HKEY hDeviceIdKey = NULL, hInstanceIdKey;
141     WCHAR KeyBuffer[MAX_PATH];
142     WCHAR InstancePath[MAX_PATH];
143     LPWSTR pEndOfInstancePath; /* Pointer into InstancePath buffer */
144     struct DeviceInfo *deviceInfo;
145     DWORD i = 0, j;
146     DWORD dwLength, dwRegType;
147     DWORD rc;
148 
149     /* Enumerate device IDs (subkeys of hEnumeratorKey) */
150     while (TRUE)
151     {
152         dwLength = sizeof(KeyBuffer) / sizeof(KeyBuffer[0]);
153         rc = RegEnumKeyExW(hEnumeratorKey, i, KeyBuffer, &dwLength, NULL, NULL, NULL, NULL);
154         if (rc == ERROR_NO_MORE_ITEMS)
155             break;
156         if (rc != ERROR_SUCCESS)
157             goto cleanup;
158         i++;
159 
160         /* Open device id sub key */
161         if (hDeviceIdKey != NULL)
162             RegCloseKey(hDeviceIdKey);
163         rc = RegOpenKeyExW(hEnumeratorKey, KeyBuffer, 0, KEY_ENUMERATE_SUB_KEYS, &hDeviceIdKey);
164         if (rc != ERROR_SUCCESS)
165             goto cleanup;
166 
167         if (FAILED(StringCchCopyW(InstancePath, _countof(InstancePath), Enumerator)) ||
168             FAILED(StringCchCatW(InstancePath, _countof(InstancePath), BackSlash))  ||
169             FAILED(StringCchCatW(InstancePath, _countof(InstancePath), KeyBuffer))  ||
170             FAILED(StringCchCatW(InstancePath, _countof(InstancePath), BackSlash)))
171         {
172             rc = ERROR_GEN_FAILURE;
173             goto cleanup;
174         }
175 
176         pEndOfInstancePath = &InstancePath[strlenW(InstancePath)];
177 
178         /* Enumerate instance IDs (subkeys of hDeviceIdKey) */
179         j = 0;
180         while (TRUE)
181         {
182             GUID KeyGuid;
183 
184             dwLength = sizeof(KeyBuffer) / sizeof(KeyBuffer[0]);
185             rc = RegEnumKeyExW(hDeviceIdKey, j, KeyBuffer, &dwLength, NULL, NULL, NULL, NULL);
186             if (rc == ERROR_NO_MORE_ITEMS)
187                 break;
188             if (rc != ERROR_SUCCESS)
189                 goto cleanup;
190             j++;
191 
192             /* Open instance id sub key */
193             rc = RegOpenKeyExW(hDeviceIdKey, KeyBuffer, 0, KEY_QUERY_VALUE, &hInstanceIdKey);
194             if (rc != ERROR_SUCCESS)
195                 goto cleanup;
196             *pEndOfInstancePath = '\0';
197             strcatW(InstancePath, KeyBuffer);
198 
199             /* Read ClassGUID value */
200             dwLength = sizeof(KeyBuffer) - sizeof(WCHAR);
201             rc = RegQueryValueExW(hInstanceIdKey, ClassGUID, NULL, &dwRegType, (LPBYTE)KeyBuffer, &dwLength);
202             RegCloseKey(hInstanceIdKey);
203             if (rc == ERROR_FILE_NOT_FOUND)
204             {
205                 if (pClassGuid)
206                     /* Skip this bad entry as we can't verify it */
207                     continue;
208                 /* Set a default GUID for this device */
209                 memcpy(&KeyGuid, &GUID_NULL, sizeof(GUID));
210             }
211             else if (rc != ERROR_SUCCESS)
212             {
213                 goto cleanup;
214             }
215             else if (dwRegType != REG_SZ || dwLength < MAX_GUID_STRING_LEN * sizeof(WCHAR))
216             {
217                 rc = ERROR_GEN_FAILURE;
218                 goto cleanup;
219             }
220             else
221             {
222                 KeyBuffer[MAX_GUID_STRING_LEN - 2] = '\0'; /* Replace the } by a NULL character */
223                 if (UuidFromStringW(&KeyBuffer[1], &KeyGuid) != RPC_S_OK)
224                     /* Bad GUID, skip the entry */
225                     continue;
226             }
227 
228             if (pClassGuid && !IsEqualIID(&KeyGuid, pClassGuid))
229             {
230                 /* Skip this entry as it is not the right device class */
231                 continue;
232             }
233 
234             /* Add the entry to the list */
235             if (!CreateDeviceInfo(list, InstancePath, &KeyGuid, &deviceInfo))
236             {
237                 rc = GetLastError();
238                 goto cleanup;
239             }
240             TRACE("Adding '%s' to device info set %p\n", debugstr_w(InstancePath), list);
241             InsertTailList(&list->ListHead, &deviceInfo->ListEntry);
242         }
243     }
244 
245     rc = ERROR_SUCCESS;
246 
247 cleanup:
248     if (hDeviceIdKey != NULL)
249         RegCloseKey(hDeviceIdKey);
250     return rc;
251 }
252 
253 LONG
254 SETUP_CreateDevicesList(
255     IN OUT struct DeviceInfoSet *list,
256     IN PCWSTR MachineName OPTIONAL,
257     IN CONST GUID *Class OPTIONAL,
258     IN PCWSTR Enumerator OPTIONAL)
259 {
260     HKEY HKLM = HKEY_LOCAL_MACHINE;
261     HKEY hEnumKey = NULL;
262     HKEY hEnumeratorKey = NULL;
263     WCHAR KeyBuffer[MAX_PATH];
264     DWORD i;
265     DWORD dwLength;
266     DWORD rc;
267 
268     if (Class && IsEqualIID(Class, &GUID_NULL))
269         Class = NULL;
270 
271     /* Open Enum key (if applicable) */
272     if (MachineName != NULL)
273     {
274         rc = RegConnectRegistryW(MachineName, HKEY_LOCAL_MACHINE, &HKLM);
275         if (rc != ERROR_SUCCESS)
276             goto cleanup;
277     }
278 
279     rc = RegOpenKeyExW(
280         HKLM,
281         REGSTR_PATH_SYSTEMENUM,
282         0,
283         KEY_ENUMERATE_SUB_KEYS,
284         &hEnumKey);
285     if (rc != ERROR_SUCCESS)
286         goto cleanup;
287 
288     /* If enumerator is provided, call directly SETUP_CreateDevicesListFromEnumerator.
289      * Else, enumerate all enumerators and call SETUP_CreateDevicesListFromEnumerator
290      * for each one.
291      */
292     if (Enumerator)
293     {
294         rc = RegOpenKeyExW(
295             hEnumKey,
296             Enumerator,
297             0,
298             KEY_ENUMERATE_SUB_KEYS,
299             &hEnumeratorKey);
300         if (rc != ERROR_SUCCESS)
301         {
302             if (rc == ERROR_FILE_NOT_FOUND)
303                 rc = ERROR_INVALID_DATA;
304             goto cleanup;
305         }
306         rc = SETUP_CreateDevicesListFromEnumerator(list, Class, Enumerator, hEnumeratorKey);
307     }
308     else
309     {
310         /* Enumerate enumerators */
311         i = 0;
312         while (TRUE)
313         {
314             dwLength = sizeof(KeyBuffer) / sizeof(KeyBuffer[0]);
315             rc = RegEnumKeyExW(hEnumKey, i, KeyBuffer, &dwLength, NULL, NULL, NULL, NULL);
316             if (rc == ERROR_NO_MORE_ITEMS)
317                 break;
318             else if (rc != ERROR_SUCCESS)
319                 goto cleanup;
320             i++;
321 
322             /* Open sub key */
323             if (hEnumeratorKey != NULL)
324                 RegCloseKey(hEnumeratorKey);
325             rc = RegOpenKeyExW(hEnumKey, KeyBuffer, 0, KEY_ENUMERATE_SUB_KEYS, &hEnumeratorKey);
326             if (rc != ERROR_SUCCESS)
327                 goto cleanup;
328 
329             /* Call SETUP_CreateDevicesListFromEnumerator */
330             rc = SETUP_CreateDevicesListFromEnumerator(list, Class, KeyBuffer, hEnumeratorKey);
331             if (rc != ERROR_SUCCESS)
332                 goto cleanup;
333         }
334         rc = ERROR_SUCCESS;
335     }
336 
337 cleanup:
338     if (HKLM != HKEY_LOCAL_MACHINE)
339         RegCloseKey(HKLM);
340     if (hEnumKey != NULL)
341         RegCloseKey(hEnumKey);
342     if (hEnumeratorKey != NULL)
343         RegCloseKey(hEnumeratorKey);
344     return rc;
345 }
346 
347 static BOOL
348 SETUP_GetIconIndex(
349     IN HKEY hClassKey,
350     OUT PINT ImageIndex)
351 {
352     LPWSTR Buffer = NULL;
353     DWORD dwRegType, dwLength;
354     LONG rc;
355     BOOL ret = FALSE;
356 
357     /* Read icon registry key */
358     rc = RegQueryValueExW(hClassKey, REGSTR_VAL_INSICON, NULL, &dwRegType, NULL, &dwLength);
359     if (rc != ERROR_SUCCESS)
360     {
361         SetLastError(rc);
362         goto cleanup;
363     } else if (dwRegType != REG_SZ)
364     {
365         SetLastError(ERROR_INVALID_INDEX);
366         goto cleanup;
367     }
368     Buffer = MyMalloc(dwLength + sizeof(WCHAR));
369     if (!Buffer)
370     {
371         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
372         goto cleanup;
373     }
374     rc = RegQueryValueExW(hClassKey, REGSTR_VAL_INSICON, NULL, NULL, (LPBYTE)Buffer, &dwLength);
375     if (rc != ERROR_SUCCESS)
376     {
377         SetLastError(rc);
378         goto cleanup;
379     }
380     /* make sure the returned buffer is NULL-terminated */
381     Buffer[dwLength / sizeof(WCHAR)] = 0;
382 
383     /* Transform icon value to a INT */
384     *ImageIndex = atoiW(Buffer);
385     ret = TRUE;
386 
387 cleanup:
388     MyFree(Buffer);
389     return ret;
390 }
391 
392 /***********************************************************************
393  *		SetupDiGetClassImageIndex (SETUPAPI.@)
394  */
395 BOOL WINAPI
396 SetupDiGetClassImageIndex(
397     IN PSP_CLASSIMAGELIST_DATA ClassImageListData,
398     IN CONST GUID *ClassGuid,
399     OUT PINT ImageIndex)
400 {
401     struct ClassImageList *list;
402     BOOL ret = FALSE;
403 
404     TRACE("%p %s %p\n", ClassImageListData, debugstr_guid(ClassGuid), ImageIndex);
405 
406     if (!ClassImageListData || !ClassGuid || !ImageIndex)
407         SetLastError(ERROR_INVALID_PARAMETER);
408     else if (ClassImageListData->cbSize != sizeof(SP_CLASSIMAGELIST_DATA))
409         SetLastError(ERROR_INVALID_USER_BUFFER);
410     else if ((list = (struct ClassImageList *)ClassImageListData->Reserved) == NULL)
411         SetLastError(ERROR_INVALID_USER_BUFFER);
412     else if (list->magic != SETUP_CLASS_IMAGE_LIST_MAGIC)
413         SetLastError(ERROR_INVALID_USER_BUFFER);
414     else
415     {
416         DWORD i;
417 
418         for (i = 0; i < list->NumberOfGuids; i++)
419         {
420             if (IsEqualIID(ClassGuid, &list->Guids[i]))
421                 break;
422         }
423 
424         if (i == list->NumberOfGuids || list->IconIndexes[i] < 0)
425             SetLastError(ERROR_FILE_NOT_FOUND);
426         else
427         {
428             *ImageIndex = list->IconIndexes[i];
429             ret = TRUE;
430         }
431     }
432 
433     TRACE("Returning %d\n", ret);
434     return ret;
435 }
436 
437 /***********************************************************************
438  *		SetupDiGetClassImageList(SETUPAPI.@)
439  */
440 BOOL WINAPI
441 SetupDiGetClassImageList(
442     OUT PSP_CLASSIMAGELIST_DATA ClassImageListData)
443 {
444     return SetupDiGetClassImageListExW(ClassImageListData, NULL, NULL);
445 }
446 
447 /***********************************************************************
448  *		SetupDiGetClassImageListExA(SETUPAPI.@)
449  */
450 BOOL WINAPI
451 SetupDiGetClassImageListExA(
452     OUT PSP_CLASSIMAGELIST_DATA ClassImageListData,
453     IN PCSTR MachineName OPTIONAL,
454     IN PVOID Reserved)
455 {
456     PWSTR MachineNameW = NULL;
457     BOOL ret;
458 
459     if (MachineName)
460     {
461         MachineNameW = pSetupMultiByteToUnicode(MachineName, CP_ACP);
462         if (MachineNameW == NULL)
463             return FALSE;
464     }
465 
466     ret = SetupDiGetClassImageListExW(ClassImageListData, MachineNameW, Reserved);
467 
468     MyFree(MachineNameW);
469 
470     return ret;
471 }
472 
473 static BOOL WINAPI
474 SETUP_GetClassIconInfo(IN CONST GUID *ClassGuid, OUT PINT OutIndex, OUT LPWSTR *OutDllName)
475 {
476     LPWSTR Buffer = NULL;
477     INT iconIndex = -UNKNOWN_ICON_INDEX;
478     HKEY hKey = INVALID_HANDLE_VALUE;
479     BOOL ret = FALSE;
480 
481     if (ClassGuid)
482     {
483         hKey = SetupDiOpenClassRegKey(ClassGuid, KEY_QUERY_VALUE);
484         if (hKey != INVALID_HANDLE_VALUE)
485         {
486             SETUP_GetIconIndex(hKey, &iconIndex);
487         }
488     }
489 
490     if (iconIndex > 0)
491     {
492         /* Look up icon in dll specified by Installer32 or EnumPropPages32 key */
493         PWCHAR Comma;
494         LONG rc;
495         DWORD dwRegType, dwLength;
496         rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, &dwRegType, NULL, &dwLength);
497         if (rc == ERROR_SUCCESS && dwRegType == REG_SZ)
498         {
499             Buffer = MyMalloc(dwLength + sizeof(WCHAR));
500             if (Buffer == NULL)
501             {
502                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
503                 goto cleanup;
504             }
505             rc = RegQueryValueExW(hKey, REGSTR_VAL_INSTALLER_32, NULL, NULL, (LPBYTE)Buffer, &dwLength);
506             if (rc != ERROR_SUCCESS)
507             {
508                 SetLastError(rc);
509                 goto cleanup;
510             }
511             /* make sure the returned buffer is NULL-terminated */
512             Buffer[dwLength / sizeof(WCHAR)] = 0;
513         }
514         else if
515             (ERROR_SUCCESS == (rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, &dwRegType, NULL, &dwLength))
516             && dwRegType == REG_SZ)
517         {
518             Buffer = MyMalloc(dwLength + sizeof(WCHAR));
519             if (Buffer == NULL)
520             {
521                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
522                 goto cleanup;
523             }
524             rc = RegQueryValueExW(hKey, REGSTR_VAL_ENUMPROPPAGES_32, NULL, NULL, (LPBYTE)Buffer, &dwLength);
525             if (rc != ERROR_SUCCESS)
526             {
527                 SetLastError(rc);
528                 goto cleanup;
529             }
530             /* make sure the returned buffer is NULL-terminated */
531             Buffer[dwLength / sizeof(WCHAR)] = 0;
532         }
533         else
534         {
535             /* Unable to find where to load the icon */
536             SetLastError(ERROR_FILE_NOT_FOUND);
537             goto cleanup;
538         }
539         Comma = strchrW(Buffer, ',');
540         if (!Comma)
541         {
542             SetLastError(ERROR_GEN_FAILURE);
543             goto cleanup;
544         }
545         *Comma = '\0';
546         *OutDllName = Buffer;
547     }
548     else
549     {
550         /* Look up icon in setupapi.dll */
551         iconIndex = -iconIndex;
552         *OutDllName = NULL;
553     }
554 
555     *OutIndex = iconIndex;
556     ret = TRUE;
557 
558     TRACE("Icon index %d, dll name %s\n", iconIndex, debugstr_w(*OutDllName ? *OutDllName : SetupapiDll));
559 
560 cleanup:
561 
562     if (hKey != INVALID_HANDLE_VALUE)
563         RegCloseKey(hKey);
564 
565     if (Buffer && !ret)
566         MyFree(Buffer);
567 
568     return ret;
569 }
570 
571 
572 /***********************************************************************
573  *		SetupDiGetClassImageListExW(SETUPAPI.@)
574  */
575 BOOL WINAPI
576 SetupDiGetClassImageListExW(
577     OUT PSP_CLASSIMAGELIST_DATA ClassImageListData,
578     IN PCWSTR MachineName OPTIONAL,
579     IN PVOID Reserved)
580 {
581     BOOL ret = FALSE;
582 
583     TRACE("%p %p %p\n", ClassImageListData, debugstr_w(MachineName), Reserved);
584 
585     if (!ClassImageListData)
586         SetLastError(ERROR_INVALID_PARAMETER);
587     else if (ClassImageListData->cbSize != sizeof(SP_CLASSIMAGELIST_DATA))
588         SetLastError(ERROR_INVALID_USER_BUFFER);
589     else if (Reserved)
590         SetLastError(ERROR_INVALID_PARAMETER);
591     else
592     {
593         struct ClassImageList *list = NULL;
594         HDC hDC;
595         DWORD RequiredSize;
596         DWORD ilMask, bkColor;
597         HICON hIcon;
598         DWORD size;
599         INT i, bpp;
600         UINT idx;
601 
602         /* Get list of all class GUIDs in given computer */
603         ret = SetupDiBuildClassInfoListExW(
604             0,
605             NULL,
606             0,
607             &RequiredSize,
608             MachineName,
609             NULL);
610         if (!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
611             goto cleanup;
612 
613         size = sizeof(struct ClassImageList)
614             + (sizeof(GUID) + sizeof(INT)) * RequiredSize;
615         list = HeapAlloc(GetProcessHeap(), 0, size);
616         if (!list)
617         {
618             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
619             goto cleanup;
620         }
621         list->magic = SETUP_CLASS_IMAGE_LIST_MAGIC;
622         list->NumberOfGuids = RequiredSize;
623         list->Guids = (GUID*)(list + 1);
624         list->IconIndexes = (INT*)((ULONG_PTR)(list + 1) + sizeof(GUID) * RequiredSize);
625 
626         ret = SetupDiBuildClassInfoListExW(
627             0,
628             list->Guids,
629             list->NumberOfGuids,
630             &RequiredSize,
631             MachineName,
632             NULL);
633         if (!ret)
634             goto cleanup;
635         else if (RequiredSize != list->NumberOfGuids)
636         {
637             /* Hm. Class list changed since last call. Ignore
638              * this case as it should be very rare */
639             SetLastError(ERROR_GEN_FAILURE);
640             ret = FALSE;
641             goto cleanup;
642         }
643 
644         /* Prepare a HIMAGELIST */
645         InitCommonControls();
646 
647         hDC = GetDC(NULL);
648         if (!hDC)
649             goto cleanup;
650 
651         bpp = GetDeviceCaps(hDC, BITSPIXEL);
652         ReleaseDC(NULL, hDC);
653 
654         if (bpp <= 4)
655             ilMask = ILC_COLOR4;
656         else if (bpp <= 8)
657             ilMask = ILC_COLOR8;
658         else if (bpp <= 16)
659             ilMask = ILC_COLOR16;
660         else if (bpp <= 24)
661             ilMask = ILC_COLOR24;
662         else if (bpp <= 32)
663             ilMask = ILC_COLOR32;
664         else
665             ilMask = ILC_COLOR;
666 
667         ilMask |= ILC_MASK;
668 
669         ClassImageListData->ImageList = ImageList_Create(16, 16, ilMask, 100, 10);
670         if (!ClassImageListData->ImageList)
671             goto cleanup;
672 
673         ClassImageListData->Reserved = (ULONG_PTR)list;
674 
675         /* For some reason, Windows sets the list background to COLOR_WINDOW */
676         bkColor = GetSysColor(COLOR_WINDOW);
677         ImageList_SetBkColor(ClassImageListData->ImageList, bkColor);
678 
679         /* Now, we "simply" need to load icons associated with all class guids,
680          * and put their index in the image list in the IconIndexes array */
681         for (i = 0; i < list->NumberOfGuids; i++)
682         {
683             INT miniIconIndex;
684             LPWSTR DllName = NULL;
685 
686             if (SETUP_GetClassIconInfo(&list->Guids[i], &miniIconIndex, &DllName))
687             {
688                 if (DllName && ExtractIconExW(DllName, -miniIconIndex, NULL, &hIcon, 1) == 1)
689                 {
690                     list->IconIndexes[i] = ImageList_AddIcon(ClassImageListData->ImageList, hIcon);
691                 }
692                 else if(!DllName)
693                 {
694                     hIcon = LoadImage(hInstance, MAKEINTRESOURCE(miniIconIndex), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
695                     list->IconIndexes[i] = ImageList_AddIcon(ClassImageListData->ImageList, hIcon);
696                 }
697 
698                 if(hIcon)
699                     DestroyIcon(hIcon);
700                 else
701                     list->IconIndexes[i] = -1;
702 
703                 if(DllName)
704                     MyFree(DllName);
705             }
706             else
707             {
708                 list->IconIndexes[i] = -1; /* Special value to indicate that the icon is unavailable */
709             }
710         }
711 
712         /* Finally, add the overlay icons to the image list */
713         for (i = 0; i <= 2; i++)
714         {
715             hIcon = LoadImage(hInstance, MAKEINTRESOURCE(500 + i), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
716             if (hIcon)
717             {
718                 idx = ImageList_AddIcon(ClassImageListData->ImageList, hIcon);
719                 if (idx != -1)
720                     ImageList_SetOverlayImage(ClassImageListData->ImageList, idx, i + 1);
721                 DestroyIcon(hIcon);
722             }
723         }
724 
725         ret = TRUE;
726 
727 cleanup:
728         if (!ret)
729         {
730             if (ClassImageListData->Reserved)
731                 SetupDiDestroyClassImageList(ClassImageListData);
732             else if (list)
733                 MyFree(list);
734         }
735     }
736 
737     TRACE("Returning %d\n", ret);
738     return ret;
739 }
740 
741 /***********************************************************************
742  *		SetupDiGetClassInstallParamsA(SETUPAPI.@)
743  */
744 BOOL WINAPI
745 SetupDiGetClassInstallParamsA(
746     IN HDEVINFO DeviceInfoSet,
747     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
748     OUT PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
749     IN DWORD ClassInstallParamsSize,
750     OUT PDWORD RequiredSize OPTIONAL)
751 {
752     FIXME("SetupDiGetClassInstallParamsA(%p %p %p %lu %p) Stub\n",
753         DeviceInfoSet, DeviceInfoData, ClassInstallParams, ClassInstallParamsSize, RequiredSize);
754     return FALSE;
755 }
756 
757 /***********************************************************************
758  *		SetupDiGetClassInstallParamsW(SETUPAPI.@)
759  */
760 BOOL WINAPI
761 SetupDiGetClassInstallParamsW(
762     IN HDEVINFO DeviceInfoSet,
763     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
764     OUT PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
765     IN DWORD ClassInstallParamsSize,
766     OUT PDWORD RequiredSize OPTIONAL)
767 {
768     FIXME("SetupDiGetClassInstallParamsW(%p %p %p %lu %p) Stub\n",
769         DeviceInfoSet, DeviceInfoData, ClassInstallParams, ClassInstallParamsSize, RequiredSize);
770     return FALSE;
771 }
772 
773 /***********************************************************************
774  *		SetupDiLoadClassIcon(SETUPAPI.@)
775  */
776 BOOL WINAPI
777 SetupDiLoadClassIcon(
778     IN CONST GUID *ClassGuid,
779     OUT HICON *LargeIcon OPTIONAL,
780     OUT PINT MiniIconIndex OPTIONAL)
781 {
782     INT iconIndex = 0;
783     LPWSTR DllName = NULL;
784     BOOL ret = FALSE;
785     HICON hIcon = NULL;
786 
787     if (LargeIcon)
788     {
789         if(!SETUP_GetClassIconInfo(ClassGuid, &iconIndex, &DllName))
790             goto cleanup;
791 
792         if (!DllName || ExtractIconExW(DllName, -iconIndex, &hIcon, NULL, 1) != 1 || hIcon == NULL)
793         {
794             /* load the default unknown device icon if ExtractIcon failed */
795             if(DllName)
796                 iconIndex = UNKNOWN_ICON_INDEX;
797 
798             hIcon = LoadImage(hInstance, MAKEINTRESOURCE(iconIndex), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
799 
800             if(!hIcon)
801                 goto cleanup;
802         }
803 
804         *LargeIcon = hIcon;
805     }
806 
807     if (MiniIconIndex)
808         *MiniIconIndex = iconIndex;
809 
810     ret = TRUE;
811 
812 cleanup:
813 
814     if(DllName)
815         MyFree(DllName);
816 
817     TRACE("Returning %d\n", ret);
818     return ret;
819 }
820 
821 /***********************************************************************
822  *		SetupDiInstallClassExW (SETUPAPI.@)
823  */
824 HKEY
825 SETUP_CreateClassKey(HINF hInf);
826 BOOL WINAPI
827 SetupDiInstallClassExW(
828     IN HWND hwndParent OPTIONAL,
829     IN PCWSTR InfFileName OPTIONAL,
830     IN DWORD Flags,
831     IN HSPFILEQ FileQueue OPTIONAL,
832     IN CONST GUID *InterfaceClassGuid OPTIONAL,
833     IN PVOID Reserved1,
834     IN PVOID Reserved2)
835 {
836     BOOL ret = FALSE;
837 
838     TRACE("%p %s 0x%lx %p %s %p %p\n", hwndParent, debugstr_w(InfFileName), Flags,
839         FileQueue, debugstr_guid(InterfaceClassGuid), Reserved1, Reserved2);
840 
841     if (!InfFileName)
842     {
843         FIXME("Case not implemented: InfFileName NULL\n");
844         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
845     }
846     else if (Flags & ~(DI_NOVCP | DI_NOBROWSE | DI_FORCECOPY | DI_QUIETINSTALL))
847     {
848         TRACE("Unknown flags: 0x%08lx\n", Flags & ~(DI_NOVCP | DI_NOBROWSE | DI_FORCECOPY | DI_QUIETINSTALL));
849         SetLastError(ERROR_INVALID_FLAGS);
850     }
851     else if ((Flags & DI_NOVCP) && FileQueue == NULL)
852         SetLastError(ERROR_INVALID_PARAMETER);
853     else if (Reserved1 != NULL)
854         SetLastError(ERROR_INVALID_PARAMETER);
855     else if (Reserved2 != NULL)
856         SetLastError(ERROR_INVALID_PARAMETER);
857     else
858     {
859         HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
860         SP_DEVINSTALL_PARAMS_W InstallParams;
861         WCHAR SectionName[MAX_PATH];
862         HINF hInf = INVALID_HANDLE_VALUE;
863         HKEY hRootKey = INVALID_HANDLE_VALUE;
864         PVOID callback_context = NULL;
865 
866         hDeviceInfo = SetupDiCreateDeviceInfoList(NULL, NULL);
867         if (hDeviceInfo == INVALID_HANDLE_VALUE)
868             goto cleanup;
869 
870         InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
871         if (!SetupDiGetDeviceInstallParamsW(hDeviceInfo, NULL, &InstallParams))
872             goto cleanup;
873 
874         InstallParams.Flags &= ~(DI_NOVCP | DI_NOBROWSE | DI_QUIETINSTALL);
875         InstallParams.Flags |= Flags & (DI_NOVCP | DI_NOBROWSE | DI_QUIETINSTALL);
876         if (Flags & DI_NOVCP)
877             InstallParams.FileQueue = FileQueue;
878         if (!SetupDiSetDeviceInstallParamsW(hDeviceInfo, NULL, &InstallParams))
879             goto cleanup;
880 
881         /* Open the .inf file */
882         hInf = SetupOpenInfFileW(
883             InfFileName,
884             NULL,
885             INF_STYLE_WIN4,
886             NULL);
887         if (hInf == INVALID_HANDLE_VALUE)
888             goto cleanup;
889 
890         /* Try to append a layout file */
891         SetupOpenAppendInfFileW(NULL, hInf, NULL);
892 
893         if (InterfaceClassGuid)
894         {
895             /* Retrieve the actual section name */
896             ret = SetupDiGetActualSectionToInstallW(
897                 hInf,
898                 InterfaceInstall32,
899                 SectionName,
900                 MAX_PATH,
901                 NULL,
902                 NULL);
903             if (!ret)
904                 goto cleanup;
905 
906             /* Open registry key related to this interface */
907             /* FIXME: What happens if the key doesn't exist? */
908             hRootKey = SetupDiOpenClassRegKeyExW(InterfaceClassGuid, KEY_ENUMERATE_SUB_KEYS, DIOCR_INTERFACE, NULL, NULL);
909             if (hRootKey == INVALID_HANDLE_VALUE)
910                 goto cleanup;
911 
912             /* SetupDiCreateDeviceInterface??? */
913             FIXME("Installing an interface is not implemented\n");
914             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
915         }
916         else
917         {
918             /* Create or open the class registry key 'HKLM\CurrentControlSet\Class\{GUID}' */
919             hRootKey = SETUP_CreateClassKey(hInf);
920             if (hRootKey == INVALID_HANDLE_VALUE)
921                 goto cleanup;
922 
923             /* Retrieve the actual section name */
924             ret = SetupDiGetActualSectionToInstallW(
925                 hInf,
926                 ClassInstall32,
927                 SectionName,
928                 MAX_PATH - strlenW(DotServices),
929                 NULL,
930                 NULL);
931             if (!ret)
932                 goto cleanup;
933 
934             callback_context = SetupInitDefaultQueueCallback(hwndParent);
935             if (!callback_context)
936                 goto cleanup;
937 
938             ret = SetupInstallFromInfSectionW(
939                 hwndParent,
940                 hInf,
941                 SectionName,
942                 SPINST_REGISTRY | SPINST_FILES | SPINST_BITREG | SPINST_INIFILES | SPINST_INI2REG,
943                 hRootKey,
944                 NULL, /* FIXME: SourceRootPath */
945                 !(Flags & DI_NOVCP) && (Flags & DI_FORCECOPY) ? SP_COPY_FORCE_IN_USE : 0, /* CopyFlags */
946                 SetupDefaultQueueCallbackW,
947                 callback_context,
948                 hDeviceInfo,
949                 NULL);
950             if (!ret)
951                 goto cleanup;
952 
953             /* OPTIONAL: Install .Services section */
954             lstrcatW(SectionName, DotServices);
955             SetupInstallServicesFromInfSectionExW(
956                 hInf,
957                 SectionName,
958                 0,
959                 hDeviceInfo,
960                 NULL,
961                 NULL,
962                 NULL);
963             ret = TRUE;
964         }
965 
966 cleanup:
967         if (hDeviceInfo != INVALID_HANDLE_VALUE)
968             SetupDiDestroyDeviceInfoList(hDeviceInfo);
969         if (hInf != INVALID_HANDLE_VALUE)
970             SetupCloseInfFile(hInf);
971         if (hRootKey != INVALID_HANDLE_VALUE)
972             RegCloseKey(hRootKey);
973         SetupTermDefaultQueueCallback(callback_context);
974     }
975 
976     TRACE("Returning %d\n", ret);
977     return ret;
978 }
979 
980 /***********************************************************************
981  *		Helper functions for SetupDiSetClassInstallParamsW
982  */
983 static BOOL
984 SETUP_PropertyChangeHandler(
985     IN HDEVINFO DeviceInfoSet,
986     IN PSP_DEVINFO_DATA DeviceInfoData,
987     IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
988     IN DWORD ClassInstallParamsSize)
989 {
990     PSP_PROPCHANGE_PARAMS PropChangeParams = (PSP_PROPCHANGE_PARAMS)ClassInstallParams;
991     BOOL ret = FALSE;
992 
993     if (!DeviceInfoData)
994         SetLastError(ERROR_INVALID_PARAMETER);
995     else if (ClassInstallParamsSize != sizeof(SP_PROPCHANGE_PARAMS))
996         SetLastError(ERROR_INVALID_PARAMETER);
997     else if (PropChangeParams && PropChangeParams->StateChange != DICS_ENABLE
998         && PropChangeParams->StateChange != DICS_DISABLE && PropChangeParams->StateChange != DICS_PROPCHANGE
999         && PropChangeParams->StateChange != DICS_START && PropChangeParams->StateChange != DICS_STOP)
1000         SetLastError(ERROR_INVALID_FLAGS);
1001     else if (PropChangeParams && PropChangeParams->Scope != DICS_FLAG_GLOBAL
1002         && PropChangeParams->Scope != DICS_FLAG_CONFIGSPECIFIC)
1003         SetLastError(ERROR_INVALID_FLAGS);
1004     else if (PropChangeParams
1005         && (PropChangeParams->StateChange == DICS_START || PropChangeParams->StateChange == DICS_STOP)
1006         && PropChangeParams->Scope != DICS_FLAG_CONFIGSPECIFIC)
1007         SetLastError(ERROR_INVALID_USER_BUFFER);
1008     else
1009     {
1010         PSP_PROPCHANGE_PARAMS *CurrentPropChangeParams;
1011         struct DeviceInfo *deviceInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
1012         CurrentPropChangeParams = &deviceInfo->ClassInstallParams.PropChangeParams;
1013 
1014         if (*CurrentPropChangeParams)
1015         {
1016             MyFree(*CurrentPropChangeParams);
1017             *CurrentPropChangeParams = NULL;
1018         }
1019         if (PropChangeParams)
1020         {
1021             *CurrentPropChangeParams = MyMalloc(ClassInstallParamsSize);
1022             if (!*CurrentPropChangeParams)
1023             {
1024                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1025                 goto done;
1026             }
1027             memcpy(*CurrentPropChangeParams, PropChangeParams, ClassInstallParamsSize);
1028         }
1029         ret = TRUE;
1030     }
1031 
1032 done:
1033     return ret;
1034 }
1035 
1036 static BOOL
1037 SETUP_PropertyAddPropertyAdvancedHandler(
1038     IN HDEVINFO DeviceInfoSet,
1039     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
1040     IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
1041     IN DWORD ClassInstallParamsSize)
1042 {
1043     PSP_ADDPROPERTYPAGE_DATA AddPropertyPageData = (PSP_ADDPROPERTYPAGE_DATA)ClassInstallParams;
1044     BOOL ret = FALSE;
1045 
1046     if (ClassInstallParamsSize != sizeof(SP_PROPCHANGE_PARAMS))
1047         SetLastError(ERROR_INVALID_PARAMETER);
1048     else if (AddPropertyPageData && AddPropertyPageData->Flags != 0)
1049         SetLastError(ERROR_INVALID_FLAGS);
1050     else if (AddPropertyPageData && AddPropertyPageData->NumDynamicPages >= MAX_INSTALLWIZARD_DYNAPAGES)
1051         SetLastError(ERROR_INVALID_USER_BUFFER);
1052     else
1053     {
1054         PSP_ADDPROPERTYPAGE_DATA *CurrentAddPropertyPageData;
1055         if (!DeviceInfoData)
1056         {
1057             struct DeviceInfoSet *list = (struct DeviceInfoSet *)DeviceInfoSet;
1058             CurrentAddPropertyPageData = &list->ClassInstallParams.AddPropertyPageData;
1059         }
1060         else
1061         {
1062             struct DeviceInfo *deviceInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
1063             CurrentAddPropertyPageData = &deviceInfo->ClassInstallParams.AddPropertyPageData;
1064         }
1065         if (*CurrentAddPropertyPageData)
1066         {
1067             MyFree(*CurrentAddPropertyPageData);
1068             *CurrentAddPropertyPageData = NULL;
1069         }
1070         if (AddPropertyPageData)
1071         {
1072             *CurrentAddPropertyPageData = MyMalloc(ClassInstallParamsSize);
1073             if (!*CurrentAddPropertyPageData)
1074             {
1075                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1076                 goto done;
1077             }
1078             memcpy(*CurrentAddPropertyPageData, AddPropertyPageData, ClassInstallParamsSize);
1079         }
1080         ret = TRUE;
1081     }
1082 
1083 done:
1084     return ret;
1085 }
1086 
1087 /***********************************************************************
1088  *		SetupDiSetClassInstallParamsW (SETUPAPI.@)
1089  */
1090 BOOL WINAPI
1091 SetupDiSetClassInstallParamsW(
1092     IN HDEVINFO DeviceInfoSet,
1093     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
1094     IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL,
1095     IN DWORD ClassInstallParamsSize)
1096 {
1097     struct DeviceInfoSet *list;
1098     BOOL ret = FALSE;
1099 
1100     TRACE("%p %p %p %lu\n", DeviceInfoSet, DeviceInfoData,
1101         ClassInstallParams, ClassInstallParamsSize);
1102 
1103     if (!DeviceInfoSet)
1104         SetLastError(ERROR_INVALID_PARAMETER);
1105     else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
1106         SetLastError(ERROR_INVALID_HANDLE);
1107     else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
1108         SetLastError(ERROR_INVALID_HANDLE);
1109     else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
1110         SetLastError(ERROR_INVALID_USER_BUFFER);
1111     else if (ClassInstallParams && ClassInstallParams->cbSize != sizeof(SP_CLASSINSTALL_HEADER))
1112         SetLastError(ERROR_INVALID_USER_BUFFER);
1113     else if (ClassInstallParams && ClassInstallParamsSize < sizeof(SP_CLASSINSTALL_HEADER))
1114         SetLastError(ERROR_INVALID_PARAMETER);
1115     else if (!ClassInstallParams && ClassInstallParamsSize != 0)
1116         SetLastError(ERROR_INVALID_PARAMETER);
1117     else
1118     {
1119         SP_DEVINSTALL_PARAMS_W InstallParams;
1120         BOOL Result;
1121 
1122         InstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W);
1123         Result = SetupDiGetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
1124         if (!Result)
1125             goto done;
1126 
1127         if (ClassInstallParams)
1128         {
1129             DWORD i;
1130             /* Check parameters in ClassInstallParams */
1131             for (i = 0; i < sizeof(InstallParamsData) / sizeof(InstallParamsData[0]); i++)
1132             {
1133                 if (InstallParamsData[i].Function == ClassInstallParams->InstallFunction)
1134                 {
1135                     ret = InstallParamsData[i].UpdateHandler(
1136                         DeviceInfoSet,
1137                         DeviceInfoData,
1138                         ClassInstallParams,
1139                         ClassInstallParamsSize);
1140                     if (ret)
1141                     {
1142                         InstallParams.Flags |= DI_CLASSINSTALLPARAMS;
1143                         ret = SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
1144                     }
1145                     goto done;
1146                 }
1147             }
1148             ERR("InstallFunction %u has no associated update handler\n", ClassInstallParams->InstallFunction);
1149             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1150             goto done;
1151         }
1152         else
1153         {
1154             InstallParams.Flags &= ~DI_CLASSINSTALLPARAMS;
1155             ret = SetupDiSetDeviceInstallParamsW(DeviceInfoSet, DeviceInfoData, &InstallParams);
1156         }
1157     }
1158 
1159 done:
1160     TRACE("Returning %d\n", ret);
1161     return ret;
1162 }
1163 
1164 /***********************************************************************
1165  *		SetupDiGetClassDevPropertySheetsA(SETUPAPI.@)
1166  */
1167 BOOL WINAPI
1168 SetupDiGetClassDevPropertySheetsA(
1169     IN HDEVINFO DeviceInfoSet,
1170     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
1171     IN LPPROPSHEETHEADERA PropertySheetHeader,
1172     IN DWORD PropertySheetHeaderPageListSize,
1173     OUT PDWORD RequiredSize OPTIONAL,
1174     IN DWORD PropertySheetType)
1175 {
1176     PROPSHEETHEADERW psh;
1177     BOOL ret = FALSE;
1178 
1179     TRACE("%p %p %p 0%lx %p 0x%lx\n", DeviceInfoSet, DeviceInfoData,
1180         PropertySheetHeader, PropertySheetHeaderPageListSize,
1181         RequiredSize, PropertySheetType);
1182 
1183     if(PropertySheetHeader)
1184     {
1185         psh.dwFlags = PropertySheetHeader->dwFlags;
1186         psh.phpage = PropertySheetHeader->phpage;
1187         psh.nPages = PropertySheetHeader->nPages;
1188     }
1189 
1190     ret = SetupDiGetClassDevPropertySheetsW(DeviceInfoSet, DeviceInfoData, PropertySheetHeader ? &psh : NULL,
1191                                             PropertySheetHeaderPageListSize, RequiredSize,
1192                                             PropertySheetType);
1193     if (ret)
1194     {
1195         PropertySheetHeader->nPages = psh.nPages;
1196     }
1197 
1198     TRACE("Returning %d\n", ret);
1199     return ret;
1200 }
1201 
1202 struct ClassDevPropertySheetsData
1203 {
1204     LPPROPSHEETHEADERW PropertySheetHeader;
1205     DWORD PropertySheetHeaderPageListSize;
1206     DWORD NumberOfPages;
1207     BOOL DontCancel;
1208 };
1209 
1210 static BOOL WINAPI
1211 SETUP_GetClassDevPropertySheetsCallback(
1212     IN HPROPSHEETPAGE hPropSheetPage,
1213     IN OUT LPARAM lParam)
1214 {
1215     struct ClassDevPropertySheetsData *PropPageData;
1216 
1217     PropPageData = (struct ClassDevPropertySheetsData *)lParam;
1218 
1219     PropPageData->NumberOfPages++;
1220 
1221     if (PropPageData->PropertySheetHeader->nPages < PropPageData->PropertySheetHeaderPageListSize)
1222     {
1223         PropPageData->PropertySheetHeader->phpage[PropPageData->PropertySheetHeader->nPages] = hPropSheetPage;
1224         PropPageData->PropertySheetHeader->nPages++;
1225         return TRUE;
1226     }
1227 
1228     return PropPageData->DontCancel;
1229 }
1230 
1231 static DWORD
1232 SETUP_GetValueString(
1233     IN HKEY hKey,
1234     IN LPWSTR lpValueName,
1235     OUT LPWSTR *lpString)
1236 {
1237     LPWSTR lpBuffer;
1238     DWORD dwLength = 0;
1239     DWORD dwRegType;
1240     DWORD rc;
1241 
1242     *lpString = NULL;
1243 
1244     RegQueryValueExW(hKey, lpValueName, NULL, &dwRegType, NULL, &dwLength);
1245 
1246     if (dwLength == 0 || dwRegType != REG_SZ)
1247         return ERROR_FILE_NOT_FOUND;
1248 
1249     lpBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength + sizeof(WCHAR));
1250     if (lpBuffer == NULL)
1251         return ERROR_NOT_ENOUGH_MEMORY;
1252 
1253     rc = RegQueryValueExW(hKey, lpValueName, NULL, NULL, (LPBYTE)lpBuffer, &dwLength);
1254     if (rc != ERROR_SUCCESS)
1255     {
1256         HeapFree(GetProcessHeap(), 0, lpBuffer);
1257         return rc;
1258     }
1259 
1260     lpBuffer[dwLength / sizeof(WCHAR)] = UNICODE_NULL;
1261 
1262     *lpString = lpBuffer;
1263 
1264     return ERROR_SUCCESS;
1265 }
1266 
1267 static
1268 BOOL
1269 SETUP_CallInstaller(
1270     IN DI_FUNCTION InstallFunction,
1271     IN HDEVINFO DeviceInfoSet,
1272     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
1273     IN PSP_ADDPROPERTYPAGE_DATA PageData)
1274 {
1275     PSP_CLASSINSTALL_HEADER pClassInstallParams = NULL;
1276     DWORD dwSize = 0;
1277     DWORD dwError;
1278     BOOL ret = TRUE;
1279 
1280     /* Get the size of the old class install parameters */
1281     if (!SetupDiGetClassInstallParams(DeviceInfoSet,
1282                                       DeviceInfoData,
1283                                       NULL,
1284                                       0,
1285                                       &dwSize))
1286     {
1287         dwError = GetLastError();
1288         if (dwError != ERROR_INSUFFICIENT_BUFFER)
1289         {
1290             ERR("SetupDiGetClassInstallParams failed (Error %lu)\n", dwError);
1291             return FALSE;
1292         }
1293     }
1294 
1295     /* Allocate a buffer for the old class install parameters */
1296     pClassInstallParams = HeapAlloc(GetProcessHeap(), 0, dwSize);
1297     if (pClassInstallParams == NULL)
1298     {
1299         ERR("Failed to allocate the parameters buffer!\n");
1300         return FALSE;
1301     }
1302 
1303     /* Save the old class install parameters */
1304     if (!SetupDiGetClassInstallParams(DeviceInfoSet,
1305                                       DeviceInfoData,
1306                                       pClassInstallParams,
1307                                       dwSize,
1308                                       &dwSize))
1309     {
1310         ERR("SetupDiGetClassInstallParams failed (Error %lu)\n", GetLastError());
1311         ret = FALSE;
1312         goto done;
1313     }
1314 
1315     /* Set the new class install parameters */
1316     if (!SetupDiSetClassInstallParams(DeviceInfoSet,
1317                                       DeviceInfoData,
1318                                       &PageData->ClassInstallHeader,
1319                                       sizeof(SP_ADDPROPERTYPAGE_DATA)))
1320     {
1321         ERR("SetupDiSetClassInstallParams failed (Error %lu)\n", dwError);
1322         ret = FALSE;
1323         goto done;
1324     }
1325 
1326     /* Call the installer */
1327     ret = SetupDiCallClassInstaller(InstallFunction,
1328                                     DeviceInfoSet,
1329                                     DeviceInfoData);
1330     if (ret == FALSE)
1331     {
1332         ERR("SetupDiCallClassInstaller failed\n");
1333         goto done;
1334     }
1335 
1336     /* Read the new class installer parameters */
1337     if (!SetupDiGetClassInstallParams(DeviceInfoSet,
1338                                       DeviceInfoData,
1339                                       &PageData->ClassInstallHeader,
1340                                       sizeof(SP_ADDPROPERTYPAGE_DATA),
1341                                       NULL))
1342     {
1343         ERR("SetupDiGetClassInstallParams failed (Error %lu)\n", GetLastError());
1344         ret = FALSE;
1345         goto done;
1346     }
1347 
1348 done:
1349     /* Restore and free the old class install parameters */
1350     if (pClassInstallParams != NULL)
1351     {
1352         SetupDiSetClassInstallParams(DeviceInfoSet,
1353                                      DeviceInfoData,
1354                                      pClassInstallParams,
1355                                      dwSize);
1356 
1357         HeapFree(GetProcessHeap(), 0, pClassInstallParams);
1358     }
1359 
1360     return ret;
1361 }
1362 
1363 /***********************************************************************
1364  *		SetupDiGetClassDevPropertySheetsW(SETUPAPI.@)
1365  */
1366 BOOL WINAPI
1367 SetupDiGetClassDevPropertySheetsW(
1368     IN HDEVINFO DeviceInfoSet,
1369     IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
1370     IN OUT LPPROPSHEETHEADERW PropertySheetHeader,
1371     IN DWORD PropertySheetHeaderPageListSize,
1372     OUT PDWORD RequiredSize OPTIONAL,
1373     IN DWORD PropertySheetType)
1374 {
1375     struct DeviceInfoSet *devInfoSet = NULL;
1376     struct DeviceInfo *devInfo = NULL;
1377     BOOL ret = FALSE;
1378 
1379     TRACE("%p %p %p 0%lx %p 0x%lx\n", DeviceInfoSet, DeviceInfoData,
1380         PropertySheetHeader, PropertySheetHeaderPageListSize,
1381         RequiredSize, PropertySheetType);
1382 
1383     if (!DeviceInfoSet)
1384         SetLastError(ERROR_INVALID_HANDLE);
1385     else if (((struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
1386         SetLastError(ERROR_INVALID_HANDLE);
1387     else if ((devInfoSet = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
1388         SetLastError(ERROR_INVALID_HANDLE);
1389     else if (!PropertySheetHeader)
1390         SetLastError(ERROR_INVALID_PARAMETER);
1391     else if (PropertySheetHeader->dwFlags & PSH_PROPSHEETPAGE)
1392         SetLastError(ERROR_INVALID_FLAGS);
1393     else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
1394         SetLastError(ERROR_INVALID_USER_BUFFER);
1395     else if (!DeviceInfoData && IsEqualIID(&devInfoSet->ClassGuid, &GUID_NULL))
1396         SetLastError(ERROR_INVALID_PARAMETER);
1397     else if (PropertySheetType != DIGCDP_FLAG_ADVANCED
1398           && PropertySheetType != DIGCDP_FLAG_BASIC
1399           && PropertySheetType != DIGCDP_FLAG_REMOTE_ADVANCED
1400           && PropertySheetType != DIGCDP_FLAG_REMOTE_BASIC)
1401         SetLastError(ERROR_INVALID_PARAMETER);
1402     else
1403     {
1404         HKEY hKey = INVALID_HANDLE_VALUE;
1405         SP_PROPSHEETPAGE_REQUEST Request;
1406         LPWSTR PropPageProvider = NULL;
1407         HMODULE hModule = NULL;
1408         PROPERTY_PAGE_PROVIDER pPropPageProvider = NULL;
1409         struct ClassDevPropertySheetsData PropPageData;
1410         SP_ADDPROPERTYPAGE_DATA InstallerPropPageData;
1411         DWORD InitialNumberOfPages, i;
1412         DWORD rc;
1413 
1414         if (DeviceInfoData)
1415             devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
1416 
1417         /* Get the class property page provider */
1418         if (devInfoSet->hmodClassPropPageProvider == NULL)
1419         {
1420             hKey = SetupDiOpenClassRegKeyExW(devInfo ? &devInfo->ClassGuid : &devInfoSet->ClassGuid, KEY_QUERY_VALUE,
1421                     DIOCR_INSTALLER, NULL/*devInfoSet->MachineName + 2*/, NULL);
1422             if (hKey != INVALID_HANDLE_VALUE)
1423             {
1424                 rc = SETUP_GetValueString(hKey, REGSTR_VAL_ENUMPROPPAGES_32, &PropPageProvider);
1425                 if (rc == ERROR_SUCCESS)
1426                 {
1427                     rc = GetFunctionPointer(PropPageProvider, &hModule, (PVOID*)&pPropPageProvider);
1428                     if (rc != ERROR_SUCCESS)
1429                     {
1430                         SetLastError(ERROR_INVALID_PROPPAGE_PROVIDER);
1431                         goto cleanup;
1432                     }
1433 
1434                     devInfoSet->hmodClassPropPageProvider = hModule;
1435                     devInfoSet->pClassPropPageProvider = pPropPageProvider;
1436 
1437                     HeapFree(GetProcessHeap(), 0, PropPageProvider);
1438                     PropPageProvider = NULL;
1439                 }
1440 
1441                 RegCloseKey(hKey);
1442                 hKey = INVALID_HANDLE_VALUE;
1443             }
1444         }
1445 
1446         /* Get the device property page provider */
1447         if (devInfo != NULL && devInfo->hmodDevicePropPageProvider == NULL)
1448         {
1449             hKey = SETUPDI_OpenDrvKey(devInfoSet->HKLM, devInfo, KEY_QUERY_VALUE);
1450             if (hKey != INVALID_HANDLE_VALUE)
1451             {
1452                 rc = SETUP_GetValueString(hKey, REGSTR_VAL_ENUMPROPPAGES_32, &PropPageProvider);
1453                 if (rc == ERROR_SUCCESS)
1454                 {
1455                     rc = GetFunctionPointer(PropPageProvider, &hModule, (PVOID*)&pPropPageProvider);
1456                     if (rc != ERROR_SUCCESS)
1457                     {
1458                         SetLastError(ERROR_INVALID_PROPPAGE_PROVIDER);
1459                         goto cleanup;
1460                     }
1461 
1462                     devInfo->hmodDevicePropPageProvider = hModule;
1463                     devInfo->pDevicePropPageProvider = pPropPageProvider;
1464 
1465                     HeapFree(GetProcessHeap(), 0, PropPageProvider);
1466                     PropPageProvider = NULL;
1467                 }
1468 
1469                 RegCloseKey(hKey);
1470                 hKey = INVALID_HANDLE_VALUE;
1471             }
1472         }
1473 
1474         InitialNumberOfPages = PropertySheetHeader->nPages;
1475 
1476         Request.cbSize = sizeof(SP_PROPSHEETPAGE_REQUEST);
1477         Request.PageRequested = SPPSR_ENUM_ADV_DEVICE_PROPERTIES;
1478         Request.DeviceInfoSet = DeviceInfoSet;
1479         Request.DeviceInfoData = DeviceInfoData;
1480 
1481         PropPageData.PropertySheetHeader = PropertySheetHeader;
1482         PropPageData.PropertySheetHeaderPageListSize = PropertySheetHeaderPageListSize;
1483         PropPageData.NumberOfPages = 0;
1484         PropPageData.DontCancel = (RequiredSize != NULL) ? TRUE : FALSE;
1485 
1486         /* Call the class property page provider */
1487         if (devInfoSet->pClassPropPageProvider != NULL)
1488             ((PROPERTY_PAGE_PROVIDER)devInfoSet->pClassPropPageProvider)(&Request, SETUP_GetClassDevPropertySheetsCallback, (LPARAM)&PropPageData);
1489 
1490         /* Call the device property page provider */
1491         if (devInfo != NULL && devInfo->pDevicePropPageProvider != NULL)
1492             ((PROPERTY_PAGE_PROVIDER)devInfo->pDevicePropPageProvider)(&Request, SETUP_GetClassDevPropertySheetsCallback, (LPARAM)&PropPageData);
1493 
1494         /* Call the class installer and add the returned pages */
1495         ZeroMemory(&InstallerPropPageData, sizeof(SP_ADDPROPERTYPAGE_DATA));
1496         InstallerPropPageData.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
1497         InstallerPropPageData.ClassInstallHeader.InstallFunction = DIF_ADDPROPERTYPAGE_ADVANCED;
1498         InstallerPropPageData.hwndWizardDlg = PropertySheetHeader->hwndParent;
1499 
1500         if (SETUP_CallInstaller(DIF_ADDPROPERTYPAGE_ADVANCED,
1501                                 DeviceInfoSet,
1502                                 DeviceInfoData,
1503                                 &InstallerPropPageData))
1504         {
1505             for (i = 0; i < InstallerPropPageData.NumDynamicPages; i++)
1506             {
1507                 if (PropPageData.PropertySheetHeader->nPages < PropertySheetHeaderPageListSize)
1508                 {
1509                     PropPageData.PropertySheetHeader->phpage[PropPageData.PropertySheetHeader->nPages] =
1510                         InstallerPropPageData.DynamicPages[i];
1511                     PropPageData.PropertySheetHeader->nPages++;
1512                 }
1513             }
1514 
1515             PropPageData.NumberOfPages += InstallerPropPageData.NumDynamicPages;
1516         }
1517 
1518         if (RequiredSize)
1519             *RequiredSize = PropPageData.NumberOfPages;
1520 
1521         if (InitialNumberOfPages + PropPageData.NumberOfPages <= PropertySheetHeaderPageListSize)
1522         {
1523             ret = TRUE;
1524         }
1525         else
1526         {
1527             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1528         }
1529 
1530 cleanup:
1531         if (hKey != INVALID_HANDLE_VALUE)
1532             RegCloseKey(hKey);
1533 
1534         if (PropPageProvider != NULL)
1535             HeapFree(GetProcessHeap(), 0, PropPageProvider);
1536     }
1537 
1538     TRACE("Returning %d\n", ret);
1539     return ret;
1540 }
1541