2 * SetupAPI device class-related functions
4 * Copyright 2000 Andreas Mohr for CodeWeavers
5 * 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
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.
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.
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
22 #include "setupapi_private.h"
27 /* Unicode constants */
28 static const WCHAR BackSlash
[] = {'\\',0};
29 static const WCHAR Class
[] = {'C','l','a','s','s',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 static const WCHAR Version
[] = {'V','e','r','s','i','o','n',0};
38 (WINAPI
* PROPERTY_PAGE_PROVIDER
) (
39 IN PSP_PROPSHEETPAGE_REQUEST PropPageRequest
,
40 IN LPFNADDPROPSHEETPAGE fAddFunc
,
43 (*UPDATE_CLASS_PARAM_HANDLER
) (
44 IN HDEVINFO DeviceInfoSet
,
45 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
46 IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL
,
47 IN DWORD ClassInstallParamsSize
);
50 SETUP_PropertyChangeHandler(
51 IN HDEVINFO DeviceInfoSet
,
52 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
53 IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL
,
54 IN DWORD ClassInstallParamsSize
);
57 SETUP_PropertyAddPropertyAdvancedHandler(
58 IN HDEVINFO DeviceInfoSet
,
59 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
60 IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL
,
61 IN DWORD ClassInstallParamsSize
);
63 typedef struct _INSTALL_PARAMS_DATA
66 UPDATE_CLASS_PARAM_HANDLER UpdateHandler
;
69 } INSTALL_PARAMS_DATA
;
71 #define ADD_PARAM_HANDLER(Function, UpdateHandler, ParamsType, ParamsField) \
72 { Function, UpdateHandler, sizeof(ParamsType), FIELD_OFFSET(struct ClassInstallParams, ParamsField) },
74 static const INSTALL_PARAMS_DATA InstallParamsData
[] = {
75 ADD_PARAM_HANDLER(DIF_PROPERTYCHANGE
, SETUP_PropertyChangeHandler
, SP_PROPCHANGE_PARAMS
, PropChangeParams
)
76 ADD_PARAM_HANDLER(DIF_ADDPROPERTYPAGE_ADVANCED
, SETUP_PropertyAddPropertyAdvancedHandler
, SP_ADDPROPERTYPAGE_DATA
, AddPropertyPageData
)
78 #undef ADD_PARAM_HANDLER
80 #define UNKNOWN_ICON_INDEX 18
82 /***********************************************************************
83 * SetupDiDestroyClassImageList(SETUPAPI.@)
86 SetupDiDestroyClassImageList(
87 IN PSP_CLASSIMAGELIST_DATA ClassImageListData
)
89 struct ClassImageList
*list
;
92 TRACE("%p\n", ClassImageListData
);
94 if (!ClassImageListData
)
95 SetLastError(ERROR_INVALID_PARAMETER
);
96 else if (ClassImageListData
->cbSize
!= sizeof(SP_CLASSIMAGELIST_DATA
))
97 SetLastError(ERROR_INVALID_USER_BUFFER
);
98 else if ((list
= (struct ClassImageList
*)ClassImageListData
->Reserved
) == NULL
)
99 SetLastError(ERROR_INVALID_USER_BUFFER
);
100 else if (list
->magic
!= SETUP_CLASS_IMAGE_LIST_MAGIC
)
101 SetLastError(ERROR_INVALID_USER_BUFFER
);
105 //ImageList_Destroy();
106 FIXME("Stub %p\n", ClassImageListData
);
107 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
110 TRACE("Returning %d\n", ret
);
114 /***********************************************************************
115 * SETUP_CreateDevicesListFromEnumerator
118 * list [IO] Device info set to fill with discovered devices.
119 * pClassGuid [I] If specified, only devices which belong to this class will be added.
120 * Enumerator [I] Location to search devices to add.
121 * hEnumeratorKey [I] Registry key corresponding to Enumerator key. Must have KEY_ENUMERATE_SUB_KEYS right.
124 * Success: ERROR_SUCCESS.
125 * Failure: an error code.
128 SETUP_CreateDevicesListFromEnumerator(
129 IN OUT
struct DeviceInfoSet
*list
,
130 IN CONST GUID
*pClassGuid OPTIONAL
,
131 IN LPCWSTR Enumerator
,
132 IN HKEY hEnumeratorKey
) /* handle to Enumerator registry key */
134 HKEY hDeviceIdKey
= NULL
, hInstanceIdKey
;
135 WCHAR KeyBuffer
[MAX_PATH
];
136 WCHAR InstancePath
[MAX_PATH
];
137 LPWSTR pEndOfInstancePath
; /* Pointer into InstancePath buffer */
138 struct DeviceInfo
*deviceInfo
;
140 DWORD dwLength
, dwRegType
;
143 /* Enumerate device IDs (subkeys of hEnumeratorKey) */
146 dwLength
= sizeof(KeyBuffer
) / sizeof(KeyBuffer
[0]);
147 rc
= RegEnumKeyExW(hEnumeratorKey
, i
, KeyBuffer
, &dwLength
, NULL
, NULL
, NULL
, NULL
);
148 if (rc
== ERROR_NO_MORE_ITEMS
)
150 if (rc
!= ERROR_SUCCESS
)
154 /* Open device id sub key */
155 if (hDeviceIdKey
!= NULL
)
156 RegCloseKey(hDeviceIdKey
);
157 rc
= RegOpenKeyExW(hEnumeratorKey
, KeyBuffer
, 0, KEY_ENUMERATE_SUB_KEYS
, &hDeviceIdKey
);
158 if (rc
!= ERROR_SUCCESS
)
160 strcpyW(InstancePath
, Enumerator
);
161 strcatW(InstancePath
, BackSlash
);
162 strcatW(InstancePath
, KeyBuffer
);
163 strcatW(InstancePath
, BackSlash
);
164 pEndOfInstancePath
= &InstancePath
[strlenW(InstancePath
)];
166 /* Enumerate instance IDs (subkeys of hDeviceIdKey) */
172 dwLength
= sizeof(KeyBuffer
) / sizeof(KeyBuffer
[0]);
173 rc
= RegEnumKeyExW(hDeviceIdKey
, j
, KeyBuffer
, &dwLength
, NULL
, NULL
, NULL
, NULL
);
174 if (rc
== ERROR_NO_MORE_ITEMS
)
176 if (rc
!= ERROR_SUCCESS
)
180 /* Open instance id sub key */
181 rc
= RegOpenKeyExW(hDeviceIdKey
, KeyBuffer
, 0, KEY_QUERY_VALUE
, &hInstanceIdKey
);
182 if (rc
!= ERROR_SUCCESS
)
184 *pEndOfInstancePath
= '\0';
185 strcatW(InstancePath
, KeyBuffer
);
187 /* Read ClassGUID value */
188 dwLength
= sizeof(KeyBuffer
) - sizeof(WCHAR
);
189 rc
= RegQueryValueExW(hInstanceIdKey
, ClassGUID
, NULL
, &dwRegType
, (LPBYTE
)KeyBuffer
, &dwLength
);
190 RegCloseKey(hInstanceIdKey
);
191 if (rc
== ERROR_FILE_NOT_FOUND
)
194 /* Skip this bad entry as we can't verify it */
196 /* Set a default GUID for this device */
197 memcpy(&KeyGuid
, &GUID_NULL
, sizeof(GUID
));
199 else if (rc
!= ERROR_SUCCESS
)
203 else if (dwRegType
!= REG_SZ
|| dwLength
< MAX_GUID_STRING_LEN
* sizeof(WCHAR
))
205 rc
= ERROR_GEN_FAILURE
;
210 KeyBuffer
[MAX_GUID_STRING_LEN
- 2] = '\0'; /* Replace the } by a NULL character */
211 if (UuidFromStringW(&KeyBuffer
[1], &KeyGuid
) != RPC_S_OK
)
212 /* Bad GUID, skip the entry */
216 if (pClassGuid
&& !IsEqualIID(&KeyGuid
, pClassGuid
))
218 /* Skip this entry as it is not the right device class */
222 /* Add the entry to the list */
223 if (!CreateDeviceInfo(list
, InstancePath
, &KeyGuid
, &deviceInfo
))
228 TRACE("Adding '%s' to device info set %p\n", debugstr_w(InstancePath
), list
);
229 InsertTailList(&list
->ListHead
, &deviceInfo
->ListEntry
);
236 if (hDeviceIdKey
!= NULL
)
237 RegCloseKey(hDeviceIdKey
);
242 SETUP_CreateDevicesList(
243 IN OUT
struct DeviceInfoSet
*list
,
244 IN PCWSTR MachineName OPTIONAL
,
245 IN CONST GUID
*Class OPTIONAL
,
246 IN PCWSTR Enumerator OPTIONAL
)
248 HKEY HKLM
= HKEY_LOCAL_MACHINE
;
249 HKEY hEnumKey
= NULL
;
250 HKEY hEnumeratorKey
= NULL
;
251 WCHAR KeyBuffer
[MAX_PATH
];
256 if (Class
&& IsEqualIID(Class
, &GUID_NULL
))
259 /* Open Enum key (if applicable) */
260 if (MachineName
!= NULL
)
262 rc
= RegConnectRegistryW(MachineName
, HKEY_LOCAL_MACHINE
, &HKLM
);
263 if (rc
!= ERROR_SUCCESS
)
269 REGSTR_PATH_SYSTEMENUM
,
271 KEY_ENUMERATE_SUB_KEYS
,
273 if (rc
!= ERROR_SUCCESS
)
276 /* If enumerator is provided, call directly SETUP_CreateDevicesListFromEnumerator.
277 * Else, enumerate all enumerators and call SETUP_CreateDevicesListFromEnumerator
286 KEY_ENUMERATE_SUB_KEYS
,
288 if (rc
!= ERROR_SUCCESS
)
290 if (rc
== ERROR_FILE_NOT_FOUND
)
291 rc
= ERROR_INVALID_DATA
;
294 rc
= SETUP_CreateDevicesListFromEnumerator(list
, Class
, Enumerator
, hEnumeratorKey
);
298 /* Enumerate enumerators */
302 dwLength
= sizeof(KeyBuffer
) / sizeof(KeyBuffer
[0]);
303 rc
= RegEnumKeyExW(hEnumKey
, i
, KeyBuffer
, &dwLength
, NULL
, NULL
, NULL
, NULL
);
304 if (rc
== ERROR_NO_MORE_ITEMS
)
306 else if (rc
!= ERROR_SUCCESS
)
311 if (hEnumeratorKey
!= NULL
)
312 RegCloseKey(hEnumeratorKey
);
313 rc
= RegOpenKeyExW(hEnumKey
, KeyBuffer
, 0, KEY_ENUMERATE_SUB_KEYS
, &hEnumeratorKey
);
314 if (rc
!= ERROR_SUCCESS
)
317 /* Call SETUP_CreateDevicesListFromEnumerator */
318 rc
= SETUP_CreateDevicesListFromEnumerator(list
, Class
, KeyBuffer
, hEnumeratorKey
);
319 if (rc
!= ERROR_SUCCESS
)
326 if (HKLM
!= HKEY_LOCAL_MACHINE
)
328 if (hEnumKey
!= NULL
)
329 RegCloseKey(hEnumKey
);
330 if (hEnumeratorKey
!= NULL
)
331 RegCloseKey(hEnumeratorKey
);
340 LPWSTR Buffer
= NULL
;
341 DWORD dwRegType
, dwLength
;
345 /* Read icon registry key */
346 rc
= RegQueryValueExW(hClassKey
, REGSTR_VAL_INSICON
, NULL
, &dwRegType
, NULL
, &dwLength
);
347 if (rc
!= ERROR_SUCCESS
)
351 } else if (dwRegType
!= REG_SZ
)
353 SetLastError(ERROR_INVALID_INDEX
);
356 Buffer
= MyMalloc(dwLength
+ sizeof(WCHAR
));
359 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
362 rc
= RegQueryValueExW(hClassKey
, REGSTR_VAL_INSICON
, NULL
, NULL
, (LPBYTE
)Buffer
, &dwLength
);
363 if (rc
!= ERROR_SUCCESS
)
368 /* make sure the returned buffer is NULL-terminated */
369 Buffer
[dwLength
/ sizeof(WCHAR
)] = 0;
371 /* Transform icon value to a INT */
372 *ImageIndex
= atoiW(Buffer
);
380 /***********************************************************************
381 * SetupDiGetClassImageIndex (SETUPAPI.@)
384 SetupDiGetClassImageIndex(
385 IN PSP_CLASSIMAGELIST_DATA ClassImageListData
,
386 IN CONST GUID
*ClassGuid
,
389 struct ClassImageList
*list
;
392 TRACE("%p %s %p\n", ClassImageListData
, debugstr_guid(ClassGuid
), ImageIndex
);
394 if (!ClassImageListData
|| !ClassGuid
|| !ImageIndex
)
395 SetLastError(ERROR_INVALID_PARAMETER
);
396 else if (ClassImageListData
->cbSize
!= sizeof(SP_CLASSIMAGELIST_DATA
))
397 SetLastError(ERROR_INVALID_USER_BUFFER
);
398 else if ((list
= (struct ClassImageList
*)ClassImageListData
->Reserved
) == NULL
)
399 SetLastError(ERROR_INVALID_USER_BUFFER
);
400 else if (list
->magic
!= SETUP_CLASS_IMAGE_LIST_MAGIC
)
401 SetLastError(ERROR_INVALID_USER_BUFFER
);
402 else if (!ImageIndex
)
403 SetLastError(ERROR_INVALID_PARAMETER
);
408 for (i
= 0; i
< list
->NumberOfGuids
; i
++)
410 if (IsEqualIID(ClassGuid
, &list
->Guids
[i
]))
414 if (i
== list
->NumberOfGuids
|| list
->IconIndexes
[i
] < 0)
415 SetLastError(ERROR_FILE_NOT_FOUND
);
418 *ImageIndex
= list
->IconIndexes
[i
];
423 TRACE("Returning %d\n", ret
);
427 /***********************************************************************
428 * SetupDiGetClassImageList(SETUPAPI.@)
431 SetupDiGetClassImageList(
432 OUT PSP_CLASSIMAGELIST_DATA ClassImageListData
)
434 return SetupDiGetClassImageListExW(ClassImageListData
, NULL
, NULL
);
437 /***********************************************************************
438 * SetupDiGetClassImageListExA(SETUPAPI.@)
441 SetupDiGetClassImageListExA(
442 OUT PSP_CLASSIMAGELIST_DATA ClassImageListData
,
443 IN PCSTR MachineName OPTIONAL
,
446 PWSTR MachineNameW
= NULL
;
451 MachineNameW
= pSetupMultiByteToUnicode(MachineName
, CP_ACP
);
452 if (MachineNameW
== NULL
)
456 ret
= SetupDiGetClassImageListExW(ClassImageListData
, MachineNameW
, Reserved
);
458 MyFree(MachineNameW
);
464 SETUP_GetClassIconInfo(IN CONST GUID
*ClassGuid
, OUT PINT OutIndex
, OUT LPWSTR
*OutDllName
)
466 LPWSTR Buffer
= NULL
;
467 INT iconIndex
= -UNKNOWN_ICON_INDEX
;
468 HKEY hKey
= INVALID_HANDLE_VALUE
;
473 hKey
= SetupDiOpenClassRegKey(ClassGuid
, KEY_QUERY_VALUE
);
474 if (hKey
!= INVALID_HANDLE_VALUE
)
476 SETUP_GetIconIndex(hKey
, &iconIndex
);
482 /* Look up icon in dll specified by Installer32 or EnumPropPages32 key */
485 DWORD dwRegType
, dwLength
;
486 rc
= RegQueryValueExW(hKey
, REGSTR_VAL_INSTALLER_32
, NULL
, &dwRegType
, NULL
, &dwLength
);
487 if (rc
== ERROR_SUCCESS
&& dwRegType
== REG_SZ
)
489 Buffer
= MyMalloc(dwLength
+ sizeof(WCHAR
));
492 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
495 rc
= RegQueryValueExW(hKey
, REGSTR_VAL_INSTALLER_32
, NULL
, NULL
, (LPBYTE
)Buffer
, &dwLength
);
496 if (rc
!= ERROR_SUCCESS
)
501 /* make sure the returned buffer is NULL-terminated */
502 Buffer
[dwLength
/ sizeof(WCHAR
)] = 0;
505 (ERROR_SUCCESS
== (rc
= RegQueryValueExW(hKey
, REGSTR_VAL_ENUMPROPPAGES_32
, NULL
, &dwRegType
, NULL
, &dwLength
))
506 && dwRegType
== REG_SZ
)
508 Buffer
= MyMalloc(dwLength
+ sizeof(WCHAR
));
511 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
514 rc
= RegQueryValueExW(hKey
, REGSTR_VAL_ENUMPROPPAGES_32
, NULL
, NULL
, (LPBYTE
)Buffer
, &dwLength
);
515 if (rc
!= ERROR_SUCCESS
)
520 /* make sure the returned buffer is NULL-terminated */
521 Buffer
[dwLength
/ sizeof(WCHAR
)] = 0;
525 /* Unable to find where to load the icon */
526 SetLastError(ERROR_FILE_NOT_FOUND
);
529 Comma
= strchrW(Buffer
, ',');
532 SetLastError(ERROR_GEN_FAILURE
);
536 *OutDllName
= Buffer
;
540 /* Look up icon in setupapi.dll */
541 iconIndex
= -iconIndex
;
545 *OutIndex
= iconIndex
;
548 TRACE("Icon index %d, dll name %s\n", iconIndex
, debugstr_w(*OutDllName
? *OutDllName
: SetupapiDll
));
552 if (hKey
!= INVALID_HANDLE_VALUE
)
562 /***********************************************************************
563 * SetupDiGetClassImageListExW(SETUPAPI.@)
566 SetupDiGetClassImageListExW(
567 OUT PSP_CLASSIMAGELIST_DATA ClassImageListData
,
568 IN PCWSTR MachineName OPTIONAL
,
573 TRACE("%p %p %p\n", ClassImageListData
, debugstr_w(MachineName
), Reserved
);
575 if (!ClassImageListData
)
576 SetLastError(ERROR_INVALID_PARAMETER
);
577 else if (ClassImageListData
->cbSize
!= sizeof(SP_CLASSIMAGELIST_DATA
))
578 SetLastError(ERROR_INVALID_USER_BUFFER
);
580 SetLastError(ERROR_INVALID_PARAMETER
);
583 struct ClassImageList
*list
= NULL
;
586 DWORD ilMask
, bkColor
;
592 /* Get list of all class GUIDs in given computer */
593 ret
= SetupDiBuildClassInfoListExW(
600 if (!ret
&& GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
603 size
= sizeof(struct ClassImageList
)
604 + (sizeof(GUID
) + sizeof(INT
)) * RequiredSize
;
605 list
= HeapAlloc(GetProcessHeap(), 0, size
);
608 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
611 list
->magic
= SETUP_CLASS_IMAGE_LIST_MAGIC
;
612 list
->NumberOfGuids
= RequiredSize
;
613 list
->Guids
= (GUID
*)(list
+ 1);
614 list
->IconIndexes
= (INT
*)((ULONG_PTR
)(list
+ 1) + sizeof(GUID
) * RequiredSize
);
616 ret
= SetupDiBuildClassInfoListExW(
625 else if (RequiredSize
!= list
->NumberOfGuids
)
627 /* Hm. Class list changed since last call. Ignore
628 * this case as it should be very rare */
629 SetLastError(ERROR_GEN_FAILURE
);
634 /* Prepare a HIMAGELIST */
635 InitCommonControls();
641 bpp
= GetDeviceCaps(hDC
, BITSPIXEL
);
642 ReleaseDC(NULL
, hDC
);
649 ilMask
= ILC_COLOR16
;
651 ilMask
= ILC_COLOR24
;
653 ilMask
= ILC_COLOR32
;
659 ClassImageListData
->ImageList
= ImageList_Create(16, 16, ilMask
, 100, 10);
660 if (!ClassImageListData
->ImageList
)
663 ClassImageListData
->Reserved
= (ULONG_PTR
)list
;
665 /* For some reason, Windows sets the list background to COLOR_WINDOW */
666 bkColor
= GetSysColor(COLOR_WINDOW
);
667 ImageList_SetBkColor(ClassImageListData
->ImageList
, bkColor
);
669 /* Now, we "simply" need to load icons associated with all class guids,
670 * and put their index in the image list in the IconIndexes array */
671 for (i
= 0; i
< list
->NumberOfGuids
; i
++)
674 LPWSTR DllName
= NULL
;
676 if (SETUP_GetClassIconInfo(&list
->Guids
[i
], &miniIconIndex
, &DllName
))
678 if (DllName
&& ExtractIconExW(DllName
, -miniIconIndex
, NULL
, &hIcon
, 1) == 1)
680 list
->IconIndexes
[i
] = ImageList_AddIcon(ClassImageListData
->ImageList
, hIcon
);
684 hIcon
= LoadImage(hInstance
, MAKEINTRESOURCE(miniIconIndex
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
685 list
->IconIndexes
[i
] = ImageList_AddIcon(ClassImageListData
->ImageList
, hIcon
);
691 list
->IconIndexes
[i
] = -1;
698 list
->IconIndexes
[i
] = -1; /* Special value to indicate that the icon is unavailable */
702 /* Finally, add the overlay icons to the image list */
703 for (i
= 0; i
<= 2; i
++)
705 hIcon
= LoadImage(hInstance
, MAKEINTRESOURCE(500 + i
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
708 idx
= ImageList_AddIcon(ClassImageListData
->ImageList
, hIcon
);
710 ImageList_SetOverlayImage(ClassImageListData
->ImageList
, idx
, i
+ 1);
720 if (ClassImageListData
->Reserved
)
721 SetupDiDestroyClassImageList(ClassImageListData
);
727 TRACE("Returning %d\n", ret
);
731 /***********************************************************************
732 * SetupDiGetClassInstallParamsA(SETUPAPI.@)
735 SetupDiGetClassInstallParamsA(
736 IN HDEVINFO DeviceInfoSet
,
737 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
738 OUT PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL
,
739 IN DWORD ClassInstallParamsSize
,
740 OUT PDWORD RequiredSize OPTIONAL
)
742 FIXME("SetupDiGetClassInstallParamsA(%p %p %p %lu %p) Stub\n",
743 DeviceInfoSet
, DeviceInfoData
, ClassInstallParams
, ClassInstallParamsSize
, RequiredSize
);
747 /***********************************************************************
748 * SetupDiGetClassInstallParamsW(SETUPAPI.@)
751 SetupDiGetClassInstallParamsW(
752 IN HDEVINFO DeviceInfoSet
,
753 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
754 OUT PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL
,
755 IN DWORD ClassInstallParamsSize
,
756 OUT PDWORD RequiredSize OPTIONAL
)
758 FIXME("SetupDiGetClassInstallParamsW(%p %p %p %lu %p) Stub\n",
759 DeviceInfoSet
, DeviceInfoData
, ClassInstallParams
, ClassInstallParamsSize
, RequiredSize
);
763 /***********************************************************************
764 * SetupDiLoadClassIcon(SETUPAPI.@)
767 SetupDiLoadClassIcon(
768 IN CONST GUID
*ClassGuid
,
769 OUT HICON
*LargeIcon OPTIONAL
,
770 OUT PINT MiniIconIndex OPTIONAL
)
773 LPWSTR DllName
= NULL
;
779 if(!SETUP_GetClassIconInfo(ClassGuid
, &iconIndex
, &DllName
))
782 if (DllName
&& ExtractIconExW(DllName
, -iconIndex
, &hIcon
, NULL
, 1) == 1 && hIcon
!= NULL
)
788 /* load the default unknown device icon if ExtractIcon failed */
790 iconIndex
= UNKNOWN_ICON_INDEX
;
792 hIcon
= LoadImage(hInstance
, MAKEINTRESOURCE(iconIndex
), IMAGE_ICON
, 32, 32, LR_DEFAULTCOLOR
);
800 *MiniIconIndex
= iconIndex
;
810 TRACE("Returning %d\n", ret
);
814 /***********************************************************************
815 * SetupDiInstallClassExW (SETUPAPI.@)
818 SETUP_CreateClassKey(HINF hInf
);
820 SetupDiInstallClassExW(
821 IN HWND hwndParent OPTIONAL
,
822 IN PCWSTR InfFileName OPTIONAL
,
824 IN HSPFILEQ FileQueue OPTIONAL
,
825 IN CONST GUID
*InterfaceClassGuid OPTIONAL
,
831 TRACE("%p %s 0x%lx %p %s %p %p\n", hwndParent
, debugstr_w(InfFileName
), Flags
,
832 FileQueue
, debugstr_guid(InterfaceClassGuid
), Reserved1
, Reserved2
);
836 FIXME("Case not implemented: InfFileName NULL\n");
837 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
839 else if (Flags
& ~(DI_NOVCP
| DI_NOBROWSE
| DI_FORCECOPY
| DI_QUIETINSTALL
))
841 TRACE("Unknown flags: 0x%08lx\n", Flags
& ~(DI_NOVCP
| DI_NOBROWSE
| DI_FORCECOPY
| DI_QUIETINSTALL
));
842 SetLastError(ERROR_INVALID_FLAGS
);
844 else if ((Flags
& DI_NOVCP
) && FileQueue
== NULL
)
845 SetLastError(ERROR_INVALID_PARAMETER
);
846 else if (Reserved1
!= NULL
)
847 SetLastError(ERROR_INVALID_PARAMETER
);
848 else if (Reserved2
!= NULL
)
849 SetLastError(ERROR_INVALID_PARAMETER
);
852 HDEVINFO hDeviceInfo
= INVALID_HANDLE_VALUE
;
853 SP_DEVINSTALL_PARAMS_W InstallParams
;
854 WCHAR SectionName
[MAX_PATH
];
855 HINF hInf
= INVALID_HANDLE_VALUE
;
856 HKEY hRootKey
= INVALID_HANDLE_VALUE
;
857 PVOID callback_context
= NULL
;
859 hDeviceInfo
= SetupDiCreateDeviceInfoList(NULL
, NULL
);
860 if (hDeviceInfo
== INVALID_HANDLE_VALUE
)
863 InstallParams
.cbSize
= sizeof(SP_DEVINSTALL_PARAMS
);
864 if (!SetupDiGetDeviceInstallParamsW(hDeviceInfo
, NULL
, &InstallParams
))
867 InstallParams
.Flags
&= ~(DI_NOVCP
| DI_NOBROWSE
| DI_QUIETINSTALL
);
868 InstallParams
.Flags
|= Flags
& (DI_NOVCP
| DI_NOBROWSE
| DI_QUIETINSTALL
);
869 if (Flags
& DI_NOVCP
)
870 InstallParams
.FileQueue
= FileQueue
;
871 if (!SetupDiSetDeviceInstallParamsW(hDeviceInfo
, NULL
, &InstallParams
))
874 /* Open the .inf file */
875 hInf
= SetupOpenInfFileW(
880 if (hInf
== INVALID_HANDLE_VALUE
)
883 /* Try to append a layout file */
884 SetupOpenAppendInfFileW(NULL
, hInf
, NULL
);
886 if (InterfaceClassGuid
)
888 /* Retrieve the actual section name */
889 ret
= SetupDiGetActualSectionToInstallW(
899 /* Open registry key related to this interface */
900 /* FIXME: What happens if the key doesn't exist? */
901 hRootKey
= SetupDiOpenClassRegKeyExW(InterfaceClassGuid
, KEY_ENUMERATE_SUB_KEYS
, DIOCR_INTERFACE
, NULL
, NULL
);
902 if (hRootKey
== INVALID_HANDLE_VALUE
)
905 /* SetupDiCreateDeviceInterface??? */
906 FIXME("Installing an interface is not implemented\n");
907 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
911 /* Create or open the class registry key 'HKLM\CurrentControlSet\Class\{GUID}' */
912 hRootKey
= SETUP_CreateClassKey(hInf
);
913 if (hRootKey
== INVALID_HANDLE_VALUE
)
916 /* Retrieve the actual section name */
917 ret
= SetupDiGetActualSectionToInstallW(
921 MAX_PATH
- strlenW(DotServices
),
927 callback_context
= SetupInitDefaultQueueCallback(hwndParent
);
928 if (!callback_context
)
931 ret
= SetupInstallFromInfSectionW(
935 SPINST_REGISTRY
| SPINST_FILES
| SPINST_BITREG
| SPINST_INIFILES
| SPINST_INI2REG
,
937 NULL
, /* FIXME: SourceRootPath */
938 !(Flags
& DI_NOVCP
) && (Flags
& DI_FORCECOPY
) ? SP_COPY_FORCE_IN_USE
: 0, /* CopyFlags */
939 SetupDefaultQueueCallbackW
,
946 /* OPTIONAL: Install .Services section */
947 lstrcatW(SectionName
, DotServices
);
948 SetupInstallServicesFromInfSectionExW(
960 if (hDeviceInfo
!= INVALID_HANDLE_VALUE
)
961 SetupDiDestroyDeviceInfoList(hDeviceInfo
);
962 if (hInf
!= INVALID_HANDLE_VALUE
)
963 SetupCloseInfFile(hInf
);
964 if (hRootKey
!= INVALID_HANDLE_VALUE
)
965 RegCloseKey(hRootKey
);
966 SetupTermDefaultQueueCallback(callback_context
);
969 TRACE("Returning %d\n", ret
);
973 /***********************************************************************
974 * Helper functions for SetupDiSetClassInstallParamsW
977 SETUP_PropertyChangeHandler(
978 IN HDEVINFO DeviceInfoSet
,
979 IN PSP_DEVINFO_DATA DeviceInfoData
,
980 IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL
,
981 IN DWORD ClassInstallParamsSize
)
983 PSP_PROPCHANGE_PARAMS PropChangeParams
= (PSP_PROPCHANGE_PARAMS
)ClassInstallParams
;
987 SetLastError(ERROR_INVALID_PARAMETER
);
988 else if (ClassInstallParamsSize
!= sizeof(SP_PROPCHANGE_PARAMS
))
989 SetLastError(ERROR_INVALID_PARAMETER
);
990 else if (PropChangeParams
&& PropChangeParams
->StateChange
!= DICS_ENABLE
991 && PropChangeParams
->StateChange
!= DICS_DISABLE
&& PropChangeParams
->StateChange
!= DICS_PROPCHANGE
992 && PropChangeParams
->StateChange
!= DICS_START
&& PropChangeParams
->StateChange
!= DICS_STOP
)
993 SetLastError(ERROR_INVALID_FLAGS
);
994 else if (PropChangeParams
&& PropChangeParams
->Scope
!= DICS_FLAG_GLOBAL
995 && PropChangeParams
->Scope
!= DICS_FLAG_CONFIGSPECIFIC
)
996 SetLastError(ERROR_INVALID_FLAGS
);
997 else if (PropChangeParams
998 && (PropChangeParams
->StateChange
== DICS_START
|| PropChangeParams
->StateChange
== DICS_STOP
)
999 && PropChangeParams
->Scope
!= DICS_FLAG_CONFIGSPECIFIC
)
1000 SetLastError(ERROR_INVALID_USER_BUFFER
);
1003 PSP_PROPCHANGE_PARAMS
*CurrentPropChangeParams
;
1004 struct DeviceInfo
*deviceInfo
= (struct DeviceInfo
*)DeviceInfoData
->Reserved
;
1005 CurrentPropChangeParams
= &deviceInfo
->ClassInstallParams
.PropChangeParams
;
1007 if (*CurrentPropChangeParams
)
1009 MyFree(*CurrentPropChangeParams
);
1010 *CurrentPropChangeParams
= NULL
;
1012 if (PropChangeParams
)
1014 *CurrentPropChangeParams
= MyMalloc(ClassInstallParamsSize
);
1015 if (!*CurrentPropChangeParams
)
1017 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1020 memcpy(*CurrentPropChangeParams
, PropChangeParams
, ClassInstallParamsSize
);
1030 SETUP_PropertyAddPropertyAdvancedHandler(
1031 IN HDEVINFO DeviceInfoSet
,
1032 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
1033 IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL
,
1034 IN DWORD ClassInstallParamsSize
)
1036 PSP_ADDPROPERTYPAGE_DATA AddPropertyPageData
= (PSP_ADDPROPERTYPAGE_DATA
)ClassInstallParams
;
1039 if (ClassInstallParamsSize
!= sizeof(SP_PROPCHANGE_PARAMS
))
1040 SetLastError(ERROR_INVALID_PARAMETER
);
1041 else if (AddPropertyPageData
&& AddPropertyPageData
->Flags
!= 0)
1042 SetLastError(ERROR_INVALID_FLAGS
);
1043 else if (AddPropertyPageData
&& AddPropertyPageData
->NumDynamicPages
>= MAX_INSTALLWIZARD_DYNAPAGES
)
1044 SetLastError(ERROR_INVALID_USER_BUFFER
);
1047 PSP_ADDPROPERTYPAGE_DATA
*CurrentAddPropertyPageData
;
1048 if (!DeviceInfoData
)
1050 struct DeviceInfoSet
*list
= (struct DeviceInfoSet
*)DeviceInfoSet
;
1051 CurrentAddPropertyPageData
= &list
->ClassInstallParams
.AddPropertyPageData
;
1055 struct DeviceInfo
*deviceInfo
= (struct DeviceInfo
*)DeviceInfoData
->Reserved
;
1056 CurrentAddPropertyPageData
= &deviceInfo
->ClassInstallParams
.AddPropertyPageData
;
1058 if (*CurrentAddPropertyPageData
)
1060 MyFree(*CurrentAddPropertyPageData
);
1061 *CurrentAddPropertyPageData
= NULL
;
1063 if (AddPropertyPageData
)
1065 *CurrentAddPropertyPageData
= MyMalloc(ClassInstallParamsSize
);
1066 if (!*CurrentAddPropertyPageData
)
1068 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1071 memcpy(*CurrentAddPropertyPageData
, AddPropertyPageData
, ClassInstallParamsSize
);
1080 /***********************************************************************
1081 * SetupDiSetClassInstallParamsW (SETUPAPI.@)
1084 SetupDiSetClassInstallParamsW(
1085 IN HDEVINFO DeviceInfoSet
,
1086 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
1087 IN PSP_CLASSINSTALL_HEADER ClassInstallParams OPTIONAL
,
1088 IN DWORD ClassInstallParamsSize
)
1090 struct DeviceInfoSet
*list
;
1093 TRACE("%p %p %p %lu\n", DeviceInfoSet
, DeviceInfoData
,
1094 ClassInstallParams
, ClassInstallParamsSize
);
1097 SetLastError(ERROR_INVALID_PARAMETER
);
1098 else if (DeviceInfoSet
== (HDEVINFO
)INVALID_HANDLE_VALUE
)
1099 SetLastError(ERROR_INVALID_HANDLE
);
1100 else if ((list
= (struct DeviceInfoSet
*)DeviceInfoSet
)->magic
!= SETUP_DEVICE_INFO_SET_MAGIC
)
1101 SetLastError(ERROR_INVALID_HANDLE
);
1102 else if (DeviceInfoData
&& DeviceInfoData
->cbSize
!= sizeof(SP_DEVINFO_DATA
))
1103 SetLastError(ERROR_INVALID_USER_BUFFER
);
1104 else if (ClassInstallParams
&& ClassInstallParams
->cbSize
!= sizeof(SP_CLASSINSTALL_HEADER
))
1105 SetLastError(ERROR_INVALID_USER_BUFFER
);
1106 else if (ClassInstallParams
&& ClassInstallParamsSize
< sizeof(SP_CLASSINSTALL_HEADER
))
1107 SetLastError(ERROR_INVALID_PARAMETER
);
1108 else if (!ClassInstallParams
&& ClassInstallParamsSize
!= 0)
1109 SetLastError(ERROR_INVALID_PARAMETER
);
1112 SP_DEVINSTALL_PARAMS_W InstallParams
;
1115 InstallParams
.cbSize
= sizeof(SP_DEVINSTALL_PARAMS_W
);
1116 Result
= SetupDiGetDeviceInstallParamsW(DeviceInfoSet
, DeviceInfoData
, &InstallParams
);
1120 if (ClassInstallParams
)
1123 /* Check parameters in ClassInstallParams */
1124 for (i
= 0; i
< sizeof(InstallParamsData
) / sizeof(InstallParamsData
[0]); i
++)
1126 if (InstallParamsData
[i
].Function
== ClassInstallParams
->InstallFunction
)
1128 ret
= InstallParamsData
[i
].UpdateHandler(
1132 ClassInstallParamsSize
);
1135 InstallParams
.Flags
|= DI_CLASSINSTALLPARAMS
;
1136 ret
= SetupDiSetDeviceInstallParamsW(DeviceInfoSet
, DeviceInfoData
, &InstallParams
);
1141 ERR("InstallFunction %u has no associated update handler\n", ClassInstallParams
->InstallFunction
);
1142 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1147 InstallParams
.Flags
&= ~DI_CLASSINSTALLPARAMS
;
1148 ret
= SetupDiSetDeviceInstallParamsW(DeviceInfoSet
, DeviceInfoData
, &InstallParams
);
1153 TRACE("Returning %d\n", ret
);
1157 /***********************************************************************
1158 * SetupDiGetClassDevPropertySheetsA(SETUPAPI.@)
1161 SetupDiGetClassDevPropertySheetsA(
1162 IN HDEVINFO DeviceInfoSet
,
1163 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
1164 IN LPPROPSHEETHEADERA PropertySheetHeader
,
1165 IN DWORD PropertySheetHeaderPageListSize
,
1166 OUT PDWORD RequiredSize OPTIONAL
,
1167 IN DWORD PropertySheetType
)
1169 PROPSHEETHEADERW psh
;
1172 TRACE("%p %p %p 0%lx %p 0x%lx\n", DeviceInfoSet
, DeviceInfoData
,
1173 PropertySheetHeader
, PropertySheetHeaderPageListSize
,
1174 RequiredSize
, PropertySheetType
);
1176 psh
.dwFlags
= PropertySheetHeader
->dwFlags
;
1177 psh
.phpage
= PropertySheetHeader
->phpage
;
1178 psh
.nPages
= PropertySheetHeader
->nPages
;
1180 ret
= SetupDiGetClassDevPropertySheetsW(DeviceInfoSet
, DeviceInfoData
, PropertySheetHeader
? &psh
: NULL
,
1181 PropertySheetHeaderPageListSize
, RequiredSize
,
1185 PropertySheetHeader
->nPages
= psh
.nPages
;
1188 TRACE("Returning %d\n", ret
);
1192 struct ClassDevPropertySheetsData
1194 LPPROPSHEETHEADERW PropertySheetHeader
;
1195 DWORD PropertySheetHeaderPageListSize
;
1196 DWORD NumberOfPages
;
1201 SETUP_GetClassDevPropertySheetsCallback(
1202 IN HPROPSHEETPAGE hPropSheetPage
,
1203 IN OUT LPARAM lParam
)
1205 struct ClassDevPropertySheetsData
*PropPageData
;
1207 PropPageData
= (struct ClassDevPropertySheetsData
*)lParam
;
1209 PropPageData
->NumberOfPages
++;
1211 if (PropPageData
->PropertySheetHeader
->nPages
< PropPageData
->PropertySheetHeaderPageListSize
)
1213 PropPageData
->PropertySheetHeader
->phpage
[PropPageData
->PropertySheetHeader
->nPages
] = hPropSheetPage
;
1214 PropPageData
->PropertySheetHeader
->nPages
++;
1218 return PropPageData
->DontCancel
;
1221 /***********************************************************************
1222 * SetupDiGetClassDevPropertySheetsW(SETUPAPI.@)
1225 SetupDiGetClassDevPropertySheetsW(
1226 IN HDEVINFO DeviceInfoSet
,
1227 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
,
1228 IN OUT LPPROPSHEETHEADERW PropertySheetHeader
,
1229 IN DWORD PropertySheetHeaderPageListSize
,
1230 OUT PDWORD RequiredSize OPTIONAL
,
1231 IN DWORD PropertySheetType
)
1233 struct DeviceInfoSet
*list
;
1236 TRACE("%p %p %p 0%lx %p 0x%lx\n", DeviceInfoSet
, DeviceInfoData
,
1237 PropertySheetHeader
, PropertySheetHeaderPageListSize
,
1238 RequiredSize
, PropertySheetType
);
1241 SetLastError(ERROR_INVALID_HANDLE
);
1242 else if (((struct DeviceInfoSet
*)DeviceInfoSet
)->magic
!= SETUP_DEVICE_INFO_SET_MAGIC
)
1243 SetLastError(ERROR_INVALID_HANDLE
);
1244 else if ((list
= (struct DeviceInfoSet
*)DeviceInfoSet
)->magic
!= SETUP_DEVICE_INFO_SET_MAGIC
)
1245 SetLastError(ERROR_INVALID_HANDLE
);
1246 else if (!PropertySheetHeader
)
1247 SetLastError(ERROR_INVALID_PARAMETER
);
1248 else if (PropertySheetHeader
->dwFlags
& PSH_PROPSHEETPAGE
)
1249 SetLastError(ERROR_INVALID_FLAGS
);
1250 else if (DeviceInfoData
&& DeviceInfoData
->cbSize
!= sizeof(SP_DEVINFO_DATA
))
1251 SetLastError(ERROR_INVALID_USER_BUFFER
);
1252 else if (!DeviceInfoData
&& IsEqualIID(&list
->ClassGuid
, &GUID_NULL
))
1253 SetLastError(ERROR_INVALID_PARAMETER
);
1254 else if (PropertySheetType
!= DIGCDP_FLAG_ADVANCED
1255 && PropertySheetType
!= DIGCDP_FLAG_BASIC
1256 && PropertySheetType
!= DIGCDP_FLAG_REMOTE_ADVANCED
1257 && PropertySheetType
!= DIGCDP_FLAG_REMOTE_BASIC
)
1258 SetLastError(ERROR_INVALID_PARAMETER
);
1261 HKEY hKey
= INVALID_HANDLE_VALUE
;
1262 SP_PROPSHEETPAGE_REQUEST Request
;
1263 LPWSTR PropPageProvider
= NULL
;
1264 HMODULE hModule
= NULL
;
1265 PROPERTY_PAGE_PROVIDER pPropPageProvider
= NULL
;
1266 struct ClassDevPropertySheetsData PropPageData
;
1267 DWORD dwLength
, dwRegType
;
1268 DWORD InitialNumberOfPages
;
1272 hKey
= SetupDiOpenDevRegKey(DeviceInfoSet
, DeviceInfoData
, DICS_FLAG_GLOBAL
, 0, DIREG_DRV
, KEY_QUERY_VALUE
);
1275 hKey
= SetupDiOpenClassRegKeyExW(&list
->ClassGuid
, KEY_QUERY_VALUE
,
1276 DIOCR_INSTALLER
, list
->MachineName
+ 2, NULL
);
1278 if (hKey
== INVALID_HANDLE_VALUE
)
1281 rc
= RegQueryValueExW(hKey
, REGSTR_VAL_ENUMPROPPAGES_32
, NULL
, &dwRegType
, NULL
, &dwLength
);
1282 if (rc
== ERROR_FILE_NOT_FOUND
)
1284 /* No registry key. As it is optional, don't say it's a bad error */
1290 else if (rc
!= ERROR_SUCCESS
&& dwRegType
!= REG_SZ
)
1296 PropPageProvider
= HeapAlloc(GetProcessHeap(), 0, dwLength
+ sizeof(WCHAR
));
1297 if (!PropPageProvider
)
1299 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1302 rc
= RegQueryValueExW(hKey
, REGSTR_VAL_ENUMPROPPAGES_32
, NULL
, NULL
, (LPBYTE
)PropPageProvider
, &dwLength
);
1303 if (rc
!= ERROR_SUCCESS
)
1308 PropPageProvider
[dwLength
/ sizeof(WCHAR
)] = 0;
1310 rc
= GetFunctionPointer(PropPageProvider
, &hModule
, (PVOID
*)&pPropPageProvider
);
1311 if (rc
!= ERROR_SUCCESS
)
1313 SetLastError(ERROR_INVALID_PROPPAGE_PROVIDER
);
1319 struct DeviceInfo
*devInfo
= (struct DeviceInfo
*)DeviceInfoData
->Reserved
;
1321 if (devInfo
->hmodDevicePropPageProvider
== NULL
)
1323 devInfo
->hmodDevicePropPageProvider
= hModule
;
1324 devInfo
->pDevicePropPageProvider
= pPropPageProvider
;
1329 struct DeviceInfoSet
*devInfoSet
= (struct DeviceInfoSet
*)DeviceInfoSet
;
1331 if (devInfoSet
->hmodClassPropPageProvider
== NULL
)
1333 devInfoSet
->hmodClassPropPageProvider
= hModule
;
1334 devInfoSet
->pClassPropPageProvider
= pPropPageProvider
;
1338 InitialNumberOfPages
= PropertySheetHeader
->nPages
;
1340 Request
.cbSize
= sizeof(SP_PROPSHEETPAGE_REQUEST
);
1341 Request
.PageRequested
= SPPSR_ENUM_ADV_DEVICE_PROPERTIES
;
1342 Request
.DeviceInfoSet
= DeviceInfoSet
;
1343 Request
.DeviceInfoData
= DeviceInfoData
;
1345 PropPageData
.PropertySheetHeader
= PropertySheetHeader
;
1346 PropPageData
.PropertySheetHeaderPageListSize
= PropertySheetHeaderPageListSize
;
1347 PropPageData
.NumberOfPages
= 0;
1348 PropPageData
.DontCancel
= (RequiredSize
!= NULL
) ? TRUE
: FALSE
;
1350 pPropPageProvider(&Request
, SETUP_GetClassDevPropertySheetsCallback
, (LPARAM
)&PropPageData
);
1353 *RequiredSize
= PropPageData
.NumberOfPages
;
1355 if (InitialNumberOfPages
+ PropPageData
.NumberOfPages
<= PropertySheetHeaderPageListSize
)
1361 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1365 if (hKey
!= INVALID_HANDLE_VALUE
)
1367 HeapFree(GetProcessHeap(), 0, PropPageProvider
);
1370 TRACE("Returning %d\n", ret
);