Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / shlwapi / reg.c
1 /*
2  * SHLWAPI registry functions
3  *
4  * Copyright 1998 Juergen Schmied
5  * Copyright 2001 Guy Albertelli
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <string.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winerror.h"
28 #include "winnls.h"
29 #include "winreg.h"
30 #include "wine/debug.h"
31 #define NO_SHLWAPI_STREAM
32 #include "shlwapi.h"
33 #include "wine/unicode.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(shell);
36
37 static const char *lpszContentTypeA = "Content Type";
38 static const WCHAR lpszContentTypeW[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0'};
39
40 /* internal structure of what the HUSKEY points to */
41 typedef struct {
42     HKEY     HKCUkey;                  /* HKEY of opened HKCU key      */
43     HKEY     HKLMkey;                  /* HKEY of opened HKLM key      */
44     HKEY     start;                    /* HKEY of where to start       */
45     WCHAR    key_string[MAX_PATH];     /* additional path from 'start' */
46 } Internal_HUSKEY, *LPInternal_HUSKEY;
47
48
49 #define REG_HKCU  TRUE
50 #define REG_HKLM  FALSE
51 /*************************************************************************
52  * REG_GetHKEYFromHUSKEY
53  *
54  * Function:  Return the proper registry key from the HUSKEY structure
55  *            also allow special predefined values.
56  */
57 HKEY REG_GetHKEYFromHUSKEY(HUSKEY hUSKey, BOOL which)
58 {
59         HKEY test = (HKEY) hUSKey;
60         LPInternal_HUSKEY mihk = (LPInternal_HUSKEY) hUSKey;
61
62         if ((test == HKEY_CLASSES_ROOT)        ||
63             (test == HKEY_CURRENT_CONFIG)      ||
64             (test == HKEY_CURRENT_USER)        ||
65             (test == HKEY_DYN_DATA)            ||
66             (test == HKEY_LOCAL_MACHINE)       ||
67             (test == HKEY_PERFORMANCE_DATA)    ||
68 /* FIXME:  need to define for Win2k, ME, XP
69  *          (test == HKEY_PERFORMANCE_TEXT)    ||
70  *          (test == HKEY_PERFORMANCE_NLSTEXT) ||
71  */
72             (test == HKEY_USERS)) return test;
73         if (which == REG_HKCU) return mihk->HKCUkey;
74         return mihk->HKLMkey;
75 }
76
77
78 /*************************************************************************
79  * SHRegOpenUSKeyA      [SHLWAPI.@]
80  *
81  * Opens a user-specific registry key
82  */
83 LONG WINAPI SHRegOpenUSKeyA(
84         LPCSTR Path,
85         REGSAM AccessType,
86         HUSKEY hRelativeUSKey,
87         PHUSKEY phNewUSKey,
88         BOOL fIgnoreHKCU)
89 {
90     HKEY openHKCUkey=0;
91     HKEY openHKLMkey=0;
92     LONG ret2, ret1 = ~ERROR_SUCCESS;
93     LPInternal_HUSKEY ihky;
94
95     TRACE("(%s, 0x%lx, 0x%lx, %p, %s)\n", debugstr_a(Path),
96           (LONG)AccessType, (LONG)hRelativeUSKey, phNewUSKey,
97           (fIgnoreHKCU) ? "Ignoring HKCU" : "Process HKCU then HKLM");
98
99     /* now create the internal version of HUSKEY */
100     ihky = (LPInternal_HUSKEY)HeapAlloc(GetProcessHeap(), 0 ,
101                                         sizeof(Internal_HUSKEY));
102     MultiByteToWideChar(0, 0, Path, -1, ihky->key_string,
103                         sizeof(ihky->key_string)-1);
104
105     if (hRelativeUSKey) {
106         openHKCUkey = ((LPInternal_HUSKEY)hRelativeUSKey)->HKCUkey;
107         openHKLMkey = ((LPInternal_HUSKEY)hRelativeUSKey)->HKLMkey;
108     }
109     else {
110         openHKCUkey = HKEY_CURRENT_USER;
111         openHKLMkey = HKEY_LOCAL_MACHINE;
112     }
113
114     ihky->HKCUkey = 0;
115     ihky->HKLMkey = 0;
116     if (!fIgnoreHKCU) {
117         ret1 = RegOpenKeyExA(openHKCUkey, Path,
118                              0, AccessType, &ihky->HKCUkey);
119         /* if successful, then save real starting point */
120         if (ret1 != ERROR_SUCCESS)
121             ihky->HKCUkey = 0;
122     }
123     ret2 = RegOpenKeyExA(openHKLMkey, Path,
124                          0, AccessType, &ihky->HKLMkey);
125     if (ret2 != ERROR_SUCCESS)
126         ihky->HKLMkey = 0;
127
128     if ((ret1 != ERROR_SUCCESS) || (ret2 != ERROR_SUCCESS))
129         TRACE("one or more opens failed: HKCU=%ld HKLM=%ld\n", ret1, ret2);
130
131     /* if all attempts have failed then bail */
132     if ((ret1 != ERROR_SUCCESS) && (ret2 != ERROR_SUCCESS)) {
133         HeapFree(GetProcessHeap(), 0, ihky);
134         if (phNewUSKey)
135             *phNewUSKey = (HUSKEY)0;
136         return ret2;
137     }
138
139     TRACE("HUSKEY=0x%08lx\n", (LONG)ihky);
140     if (phNewUSKey)
141         *phNewUSKey = (HUSKEY)ihky;
142     return ERROR_SUCCESS;
143 }
144
145 /*************************************************************************
146  * SHRegOpenUSKeyW      [SHLWAPI.@]
147  *
148  * Opens a user-specific registry key
149  */
150 LONG WINAPI SHRegOpenUSKeyW(
151         LPCWSTR Path,
152         REGSAM AccessType,
153         HUSKEY hRelativeUSKey,
154         PHUSKEY phNewUSKey,
155         BOOL fIgnoreHKCU)
156 {
157     HKEY openHKCUkey=0;
158     HKEY openHKLMkey=0;
159     LONG ret2, ret1 = ~ERROR_SUCCESS;
160     LPInternal_HUSKEY ihky;
161
162     TRACE("(%s, 0x%lx, 0x%lx, %p, %s)\n", debugstr_w(Path),
163           (LONG)AccessType, (LONG)hRelativeUSKey, phNewUSKey,
164           (fIgnoreHKCU) ? "Ignoring HKCU" : "Process HKCU then HKLM");
165
166     /* now create the internal version of HUSKEY */
167     ihky = (LPInternal_HUSKEY)HeapAlloc(GetProcessHeap(), 0 ,
168                                         sizeof(Internal_HUSKEY));
169     lstrcpynW(ihky->key_string, Path, sizeof(ihky->key_string));
170
171     if (hRelativeUSKey) {
172         openHKCUkey = ((LPInternal_HUSKEY)hRelativeUSKey)->HKCUkey;
173         openHKLMkey = ((LPInternal_HUSKEY)hRelativeUSKey)->HKLMkey;
174     }
175     else {
176         openHKCUkey = HKEY_CURRENT_USER;
177         openHKLMkey = HKEY_LOCAL_MACHINE;
178     }
179
180     ihky->HKCUkey = 0;
181     ihky->HKLMkey = 0;
182     if (!fIgnoreHKCU) {
183         ret1 = RegOpenKeyExW(openHKCUkey, Path,
184                             0, AccessType, &ihky->HKCUkey);
185         /* if successful, then save real starting point */
186         if (ret1 != ERROR_SUCCESS)
187             ihky->HKCUkey = 0;
188     }
189     ret2 = RegOpenKeyExW(openHKLMkey, Path,
190                         0, AccessType, &ihky->HKLMkey);
191     if (ret2 != ERROR_SUCCESS)
192         ihky->HKLMkey = 0;
193
194     if ((ret1 != ERROR_SUCCESS) || (ret2 != ERROR_SUCCESS))
195         TRACE("one or more opens failed: HKCU=%ld HKLM=%ld\n", ret1, ret2);
196
197     /* if all attempts have failed then bail */
198     if ((ret1 != ERROR_SUCCESS) && (ret2 != ERROR_SUCCESS)) {
199         HeapFree(GetProcessHeap(), 0, ihky);
200         if (phNewUSKey)
201             *phNewUSKey = (HUSKEY)0;
202         return ret2;
203     }
204
205     TRACE("HUSKEY=0x%08lx\n", (LONG)ihky);
206     if (phNewUSKey)
207         *phNewUSKey = (HUSKEY)ihky;
208     return ERROR_SUCCESS;
209 }
210
211 /*************************************************************************
212  * SHRegCloseUSKey      [SHLWAPI.@]
213  *
214  * Closes a user-specific registry key
215  */
216 LONG WINAPI SHRegCloseUSKey(
217         HUSKEY hUSKey)
218 {
219     LPInternal_HUSKEY mihk = (LPInternal_HUSKEY)hUSKey;
220     LONG ret = ERROR_SUCCESS;
221
222     if (mihk->HKCUkey)
223         ret = RegCloseKey(mihk->HKCUkey);
224     if (mihk->HKLMkey)
225         ret = RegCloseKey(mihk->HKLMkey);
226     HeapFree(GetProcessHeap(), 0, mihk);
227     return ret;
228 }
229
230 /*************************************************************************
231  *      SHRegQueryUSValueA      [SHLWAPI.@]
232  */
233 LONG WINAPI SHRegQueryUSValueA(
234         HUSKEY hUSKey,             /* [in]  */
235         LPCSTR pszValue,
236         LPDWORD pdwType,
237         LPVOID pvData,
238         LPDWORD pcbData,
239         BOOL fIgnoreHKCU,
240         LPVOID pvDefaultData,
241         DWORD dwDefaultDataSize)
242 {
243         LONG ret = ~ERROR_SUCCESS;
244         LONG i, maxmove;
245         HKEY dokey;
246         CHAR *src, *dst;
247
248         /* if user wants HKCU, and it exists, then try it */
249         if (!fIgnoreHKCU && (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) {
250             ret = RegQueryValueExA(dokey,
251                                    pszValue, 0, pdwType, pvData, pcbData);
252             TRACE("HKCU RegQueryValue returned %08lx\n", ret);
253         }
254
255         /* if HKCU did not work and HKLM exists, then try it */
256         if ((ret != ERROR_SUCCESS) &&
257             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) {
258             ret = RegQueryValueExA(dokey,
259                                    pszValue, 0, pdwType, pvData, pcbData);
260             TRACE("HKLM RegQueryValue returned %08lx\n", ret);
261         }
262
263         /* if neither worked, and default data exists, then use it */
264         if (ret != ERROR_SUCCESS) {
265             if (pvDefaultData && (dwDefaultDataSize != 0)) {
266                 maxmove = (dwDefaultDataSize >= *pcbData) ? *pcbData : dwDefaultDataSize;
267                 src = (CHAR*)pvDefaultData;
268                 dst = (CHAR*)pvData;
269                 for(i=0; i<maxmove; i++) *dst++ = *src++;
270                 *pcbData = maxmove;
271                 TRACE("setting default data\n");
272                 ret = ERROR_SUCCESS;
273             }
274         }
275         return ret;
276 }
277
278
279 /*************************************************************************
280  *      SHRegQueryUSValueW      [SHLWAPI.@]
281  */
282 LONG WINAPI SHRegQueryUSValueW(
283         HUSKEY hUSKey,             /* [in]  */
284         LPCWSTR pszValue,
285         LPDWORD pdwType,
286         LPVOID pvData,
287         LPDWORD pcbData,
288         BOOL fIgnoreHKCU,
289         LPVOID pvDefaultData,
290         DWORD dwDefaultDataSize)
291 {
292         LONG ret = ~ERROR_SUCCESS;
293         LONG i, maxmove;
294         HKEY dokey;
295         CHAR *src, *dst;
296
297         /* if user wants HKCU, and it exists, then try it */
298         if (!fIgnoreHKCU && (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) {
299             ret = RegQueryValueExW(dokey,
300                                    pszValue, 0, pdwType, pvData, pcbData);
301             TRACE("HKCU RegQueryValue returned %08lx\n", ret);
302         }
303
304         /* if HKCU did not work and HKLM exists, then try it */
305         if ((ret != ERROR_SUCCESS) &&
306             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) {
307             ret = RegQueryValueExW(dokey,
308                                    pszValue, 0, pdwType, pvData, pcbData);
309             TRACE("HKLM RegQueryValue returned %08lx\n", ret);
310         }
311
312         /* if neither worked, and default data exists, then use it */
313         if (ret != ERROR_SUCCESS) {
314             if (pvDefaultData && (dwDefaultDataSize != 0)) {
315                 maxmove = (dwDefaultDataSize >= *pcbData) ? *pcbData : dwDefaultDataSize;
316                 src = (CHAR*)pvDefaultData;
317                 dst = (CHAR*)pvData;
318                 for(i=0; i<maxmove; i++) *dst++ = *src++;
319                 *pcbData = maxmove;
320                 TRACE("setting default data\n");
321                 ret = ERROR_SUCCESS;
322             }
323         }
324         return ret;
325 }
326
327 /*************************************************************************
328  * SHRegGetUSValueA     [SHLWAPI.@]
329  *
330  * Gets a user-specific registry value
331  *   Will open the key, query the value, and close the key
332  */
333 LONG WINAPI SHRegGetUSValueA(
334         LPCSTR   pSubKey,
335         LPCSTR   pValue,
336         LPDWORD  pwType,
337         LPVOID   pvData,
338         LPDWORD  pcbData,
339         BOOL     flagIgnoreHKCU,
340         LPVOID   pDefaultData,
341         DWORD    wDefaultDataSize)
342 {
343         HUSKEY myhuskey;
344         LONG ret;
345
346         if (!pvData || !pcbData) return ERROR_INVALID_FUNCTION; /* FIXME:wrong*/
347         TRACE("key '%s', value '%s', datalen %ld,  %s\n",
348               debugstr_a(pSubKey), debugstr_a(pValue), *pcbData,
349               (flagIgnoreHKCU) ? "Ignoring HKCU" : "Tries HKCU then HKLM");
350
351         ret = SHRegOpenUSKeyA(pSubKey, 0x1, 0, &myhuskey, flagIgnoreHKCU);
352         if (ret == ERROR_SUCCESS) {
353             ret = SHRegQueryUSValueA(myhuskey, pValue, pwType, pvData,
354                                      pcbData, flagIgnoreHKCU, pDefaultData,
355                                      wDefaultDataSize);
356             SHRegCloseUSKey(myhuskey);
357         }
358         return ret;
359 }
360
361 /*************************************************************************
362  * SHRegGetUSValueW     [SHLWAPI.@]
363  *
364  * Gets a user-specific registry value
365  *   Will open the key, query the value, and close the key
366  */
367 LONG WINAPI SHRegGetUSValueW(
368         LPCWSTR  pSubKey,
369         LPCWSTR  pValue,
370         LPDWORD  pwType,
371         LPVOID   pvData,
372         LPDWORD  pcbData,
373         BOOL     flagIgnoreHKCU,
374         LPVOID   pDefaultData,
375         DWORD    wDefaultDataSize)
376 {
377         HUSKEY myhuskey;
378         LONG ret;
379
380         if (!pvData || !pcbData) return ERROR_INVALID_FUNCTION; /* FIXME:wrong*/
381         TRACE("key '%s', value '%s', datalen %ld,  %s\n",
382               debugstr_w(pSubKey), debugstr_w(pValue), *pcbData,
383               (flagIgnoreHKCU) ? "Ignoring HKCU" : "Tries HKCU then HKLM");
384
385         ret = SHRegOpenUSKeyW(pSubKey, 0x1, 0, &myhuskey, flagIgnoreHKCU);
386         if (ret == ERROR_SUCCESS) {
387             ret = SHRegQueryUSValueW(myhuskey, pValue, pwType, pvData,
388                                      pcbData, flagIgnoreHKCU, pDefaultData,
389                                      wDefaultDataSize);
390             SHRegCloseUSKey(myhuskey);
391         }
392         return ret;
393 }
394
395 /*************************************************************************
396  * SHRegGetBoolUSValueA   [SHLWAPI.@]
397  */
398 BOOL WINAPI SHRegGetBoolUSValueA(
399         LPCSTR pszSubKey,
400         LPCSTR pszValue,
401         BOOL fIgnoreHKCU,
402         BOOL fDefault)
403 {
404         LONG retvalue;
405         DWORD type, datalen, work;
406         BOOL ret = fDefault;
407         CHAR data[10];
408
409         TRACE("key '%s', value '%s', %s\n",
410               debugstr_a(pszSubKey), debugstr_a(pszValue),
411               (fIgnoreHKCU) ? "Ignoring HKCU" : "Tries HKCU then HKLM");
412
413         datalen = sizeof(data)-1;
414         if (!(retvalue = SHRegGetUSValueA( pszSubKey, pszValue, &type,
415                                            data, &datalen,
416                                            fIgnoreHKCU, 0, 0))) {
417             /* process returned data via type into bool */
418             switch (type) {
419             case REG_SZ:
420                 data[9] = '\0';     /* set end of string */
421                 if (lstrcmpiA(data, "YES") == 0) ret = TRUE;
422                 if (lstrcmpiA(data, "TRUE") == 0) ret = TRUE;
423                 if (lstrcmpiA(data, "NO") == 0) ret = FALSE;
424                 if (lstrcmpiA(data, "FALSE") == 0) ret = FALSE;
425                 break;
426             case REG_DWORD:
427                 work = *(LPDWORD)data;
428                 ret = (work != 0);
429                 break;
430             case REG_BINARY:
431                 if (datalen == 1) {
432                     ret = (data[0] != '\0');
433                     break;
434                 }
435             default:
436                 FIXME("Unsupported registry data type %ld\n", type);
437                 ret = FALSE;
438             }
439             TRACE("got value (type=%ld), returing <%s>\n", type,
440                   (ret) ? "TRUE" : "FALSE");
441         }
442         else {
443             ret = fDefault;
444             TRACE("returning default data <%s>\n",
445                   (ret) ? "TRUE" : "FALSE");
446         }
447         return ret;
448 }
449
450 /*************************************************************************
451  * SHRegGetBoolUSValueW   [SHLWAPI.@]
452  */
453 BOOL WINAPI SHRegGetBoolUSValueW(
454         LPCWSTR pszSubKey,
455         LPCWSTR pszValue,
456         BOOL fIgnoreHKCU,
457         BOOL fDefault)
458 {
459         static const WCHAR wYES[]=  {'Y','E','S','\0'};
460         static const WCHAR wTRUE[]= {'T','R','U','E','\0'};
461         static const WCHAR wNO[]=   {'N','O','\0'};
462         static const WCHAR wFALSE[]={'F','A','L','S','E','\0'};
463         LONG retvalue;
464         DWORD type, datalen, work;
465         BOOL ret = fDefault;
466         WCHAR data[10];
467
468         TRACE("key '%s', value '%s', %s\n",
469               debugstr_w(pszSubKey), debugstr_w(pszValue),
470               (fIgnoreHKCU) ? "Ignoring HKCU" : "Tries HKCU then HKLM");
471
472         datalen = (sizeof(data)-1) * sizeof(WCHAR);
473         if (!(retvalue = SHRegGetUSValueW( pszSubKey, pszValue, &type,
474                                            data, &datalen,
475                                            fIgnoreHKCU, 0, 0))) {
476             /* process returned data via type into bool */
477             switch (type) {
478             case REG_SZ:
479                 data[9] = L'\0';     /* set end of string */
480                 if (lstrcmpiW(data, wYES)==0 || lstrcmpiW(data, wTRUE)==0)
481                     ret = TRUE;
482                 else if (lstrcmpiW(data, wNO)==0 || lstrcmpiW(data, wFALSE)==0)
483                     ret = FALSE;
484                 break;
485             case REG_DWORD:
486                 work = *(LPDWORD)data;
487                 ret = (work != 0);
488                 break;
489             case REG_BINARY:
490                 if (datalen == 1) {
491                     ret = (data[0] != L'\0');
492                     break;
493                 }
494             default:
495                 FIXME("Unsupported registry data type %ld\n", type);
496                 ret = FALSE;
497             }
498             TRACE("got value (type=%ld), returing <%s>\n", type,
499                   (ret) ? "TRUE" : "FALSE");
500         }
501         else {
502             ret = fDefault;
503             TRACE("returning default data <%s>\n",
504                   (ret) ? "TRUE" : "FALSE");
505         }
506         return ret;
507 }
508
509 /*************************************************************************
510  *      SHRegQueryInfoUSKeyA    [SHLWAPI.@]
511  */
512 LONG WINAPI SHRegQueryInfoUSKeyA(
513         HUSKEY hUSKey,             /* [in]  */
514         LPDWORD pcSubKeys,
515         LPDWORD pcchMaxSubKeyLen,
516         LPDWORD pcValues,
517         LPDWORD pcchMaxValueNameLen,
518         SHREGENUM_FLAGS enumRegFlags)
519 {
520         HKEY dokey;
521         LONG ret;
522
523         TRACE("(0x%lx,%p,%p,%p,%p,%d)\n",
524               (LONG)hUSKey,pcSubKeys,pcchMaxSubKeyLen,pcValues,
525               pcchMaxValueNameLen,enumRegFlags);
526
527         /* if user wants HKCU, and it exists, then try it */
528         if (((enumRegFlags == SHREGENUM_HKCU) ||
529              (enumRegFlags == SHREGENUM_DEFAULT)) &&
530             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) {
531             ret = RegQueryInfoKeyA(dokey, 0, 0, 0,
532                                    pcSubKeys, pcchMaxSubKeyLen, 0,
533                                    pcValues, pcchMaxValueNameLen, 0, 0, 0);
534             if ((ret == ERROR_SUCCESS) ||
535                 (enumRegFlags == SHREGENUM_HKCU))
536                 return ret;
537         }
538         if (((enumRegFlags == SHREGENUM_HKLM) ||
539              (enumRegFlags == SHREGENUM_DEFAULT)) &&
540             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) {
541             return RegQueryInfoKeyA(dokey, 0, 0, 0,
542                                     pcSubKeys, pcchMaxSubKeyLen, 0,
543                                     pcValues, pcchMaxValueNameLen, 0, 0, 0);
544         }
545         return ERROR_INVALID_FUNCTION;
546 }
547
548 /*************************************************************************
549  *      SHRegQueryInfoUSKeyW    [SHLWAPI.@]
550  */
551 LONG WINAPI SHRegQueryInfoUSKeyW(
552         HUSKEY hUSKey,             /* [in]  */
553         LPDWORD pcSubKeys,
554         LPDWORD pcchMaxSubKeyLen,
555         LPDWORD pcValues,
556         LPDWORD pcchMaxValueNameLen,
557         SHREGENUM_FLAGS enumRegFlags)
558 {
559         HKEY dokey;
560         LONG ret;
561
562         TRACE("(0x%lx,%p,%p,%p,%p,%d)\n",
563               (LONG)hUSKey,pcSubKeys,pcchMaxSubKeyLen,pcValues,
564               pcchMaxValueNameLen,enumRegFlags);
565
566         /* if user wants HKCU, and it exists, then try it */
567         if (((enumRegFlags == SHREGENUM_HKCU) ||
568              (enumRegFlags == SHREGENUM_DEFAULT)) &&
569             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) {
570             ret = RegQueryInfoKeyW(dokey, 0, 0, 0,
571                                    pcSubKeys, pcchMaxSubKeyLen, 0,
572                                    pcValues, pcchMaxValueNameLen, 0, 0, 0);
573             if ((ret == ERROR_SUCCESS) ||
574                 (enumRegFlags == SHREGENUM_HKCU))
575                 return ret;
576         }
577         if (((enumRegFlags == SHREGENUM_HKLM) ||
578              (enumRegFlags == SHREGENUM_DEFAULT)) &&
579             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) {
580             return RegQueryInfoKeyW(dokey, 0, 0, 0,
581                                     pcSubKeys, pcchMaxSubKeyLen, 0,
582                                     pcValues, pcchMaxValueNameLen, 0, 0, 0);
583         }
584         return ERROR_INVALID_FUNCTION;
585 }
586
587 /*************************************************************************
588  *      SHRegEnumUSKeyA         [SHLWAPI.@]
589  */
590 LONG WINAPI SHRegEnumUSKeyA(
591         HUSKEY hUSKey,                 /* [in]  */
592         DWORD dwIndex,                 /* [in]  */
593         LPSTR pszName,                 /* [out] */
594         LPDWORD pcchValueNameLen,      /* [in/out] */
595         SHREGENUM_FLAGS enumRegFlags)  /* [in]  */
596 {
597         HKEY dokey;
598
599         TRACE("(0x%lx,%ld,%p,%p(%ld),%d)\n",
600               (LONG)hUSKey, dwIndex, pszName, pcchValueNameLen,
601               *pcchValueNameLen, enumRegFlags);
602
603         if (((enumRegFlags == SHREGENUM_HKCU) ||
604              (enumRegFlags == SHREGENUM_DEFAULT)) &&
605             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) {
606             return RegEnumKeyExA(dokey, dwIndex, pszName, pcchValueNameLen,
607                                 0, 0, 0, 0);
608         }
609
610         if (((enumRegFlags == SHREGENUM_HKLM) ||
611              (enumRegFlags == SHREGENUM_DEFAULT)) &&
612             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) {
613             return RegEnumKeyExA(dokey, dwIndex, pszName, pcchValueNameLen,
614                                 0, 0, 0, 0);
615         }
616         FIXME("no support for SHREGNUM_BOTH\n");
617         return ERROR_INVALID_FUNCTION;
618 }
619
620 /*************************************************************************
621  *      SHRegEnumUSKeyW         [SHLWAPI.@]
622  */
623 LONG WINAPI SHRegEnumUSKeyW(
624         HUSKEY hUSKey,                 /* [in]  */
625         DWORD dwIndex,                 /* [in]  */
626         LPWSTR pszName,                /* [out] */
627         LPDWORD pcchValueNameLen,      /* [in/out] */
628         SHREGENUM_FLAGS enumRegFlags)  /* [in]  */
629 {
630         HKEY dokey;
631
632         TRACE("(0x%lx,%ld,%p,%p(%ld),%d)\n",
633               (LONG)hUSKey, dwIndex, pszName, pcchValueNameLen,
634               *pcchValueNameLen, enumRegFlags);
635
636         if (((enumRegFlags == SHREGENUM_HKCU) ||
637              (enumRegFlags == SHREGENUM_DEFAULT)) &&
638             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) {
639             return RegEnumKeyExW(dokey, dwIndex, pszName, pcchValueNameLen,
640                                 0, 0, 0, 0);
641         }
642
643         if (((enumRegFlags == SHREGENUM_HKLM) ||
644              (enumRegFlags == SHREGENUM_DEFAULT)) &&
645             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) {
646             return RegEnumKeyExW(dokey, dwIndex, pszName, pcchValueNameLen,
647                                 0, 0, 0, 0);
648         }
649         FIXME("no support for SHREGNUM_BOTH\n");
650         return ERROR_INVALID_FUNCTION;
651 }
652
653 /*************************************************************************
654  *      SHRegWriteUSValueA      [SHLWAPI.@]
655  */
656 LONG  WINAPI SHRegWriteUSValueA(HUSKEY hUSKey, LPCSTR pszValue, DWORD dwType,
657                                 LPVOID pvData, DWORD cbData, DWORD dwFlags)
658 {
659     HKEY dokey;
660
661     TRACE("(0x%lx,%s,%ld,%p,%ld,%ld)\n",
662           (LONG)hUSKey, debugstr_a(pszValue), dwType, pvData, cbData, dwFlags);
663
664     if ((dwFlags & SHREGSET_FORCE_HKCU) &&
665             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) {
666         RegSetValueExA(dokey, pszValue, 0, dwType, pvData, cbData);
667     }
668
669     if ((dwFlags & SHREGSET_FORCE_HKLM) &&
670             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) {
671         RegSetValueExA(dokey, pszValue, 0, dwType, pvData, cbData);
672     }
673
674     if (dwFlags & (SHREGSET_FORCE_HKCU | SHREGSET_FORCE_HKLM))
675         return ERROR_SUCCESS;
676
677     FIXME("SHREGSET_HKCU or SHREGSET_HKLM not supported\n");
678     return ERROR_SUCCESS;
679 }
680
681 /*************************************************************************
682  *      SHRegWriteUSValueW      [SHLWAPI.@]
683  */
684 LONG  WINAPI SHRegWriteUSValueW(HUSKEY hUSKey, LPCWSTR pszValue, DWORD dwType,
685                                 LPVOID pvData, DWORD cbData, DWORD dwFlags)
686 {
687     HKEY dokey;
688
689     TRACE("(0x%lx,%s,%ld,%p,%ld,%ld)\n",
690           (LONG)hUSKey, debugstr_w(pszValue), dwType, pvData, cbData, dwFlags);
691
692     if ((dwFlags & SHREGSET_FORCE_HKCU) &&
693             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) {
694         RegSetValueExW(dokey, pszValue, 0, dwType, pvData, cbData);
695     }
696
697     if ((dwFlags & SHREGSET_FORCE_HKLM) &&
698             (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) {
699         RegSetValueExW(dokey, pszValue, 0, dwType, pvData, cbData);
700     }
701
702     if (dwFlags & (SHREGSET_FORCE_HKCU | SHREGSET_FORCE_HKLM))
703         return ERROR_SUCCESS;
704
705     FIXME("SHREGSET_HKCU or SHREGSET_HKLM not supported\n");
706     return ERROR_SUCCESS;
707 }
708
709 /*************************************************************************
710  * SHRegGetPathA   [SHLWAPI.@]
711  *
712  * Get a path from the registry.
713  *
714  * PARAMS
715  *   hKey       [I] Handle to registry key
716  *   lpszSubKey [I] Name of sub key containing path to get
717  *   lpszValue  [I] Name of value containing path to get
718  *   lpszPath   [O] Buffer for returned path
719  *   dwFlags    [I] Reserved
720  *
721  * RETURNS
722  *   Success: ERROR_SUCCESS. lpszPath contains the path.
723  *   Failure: An error code from RegOpenKeyExA or SHQueryValueExA.
724  */
725 DWORD WINAPI SHRegGetPathA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue,
726                            LPSTR lpszPath, DWORD dwFlags)
727 {
728   DWORD dwSize = MAX_PATH;
729
730   TRACE("(hkey=0x%08x,%s,%s,%p,%ld)\n", hKey, debugstr_a(lpszSubKey),
731         debugstr_a(lpszValue), lpszPath, dwFlags);
732
733   return SHGetValueA(hKey, lpszSubKey, lpszValue, 0, lpszPath, &dwSize);
734 }
735
736 /*************************************************************************
737  * SHRegGetPathW   [SHLWAPI.@]
738  *
739  * See SHRegGetPathA.
740  */
741 DWORD WINAPI SHRegGetPathW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue,
742                            LPWSTR lpszPath, DWORD dwFlags)
743 {
744   DWORD dwSize = MAX_PATH;
745
746   TRACE("(hkey=0x%08x,%s,%s,%p,%ld)\n", hKey, debugstr_w(lpszSubKey),
747         debugstr_w(lpszValue), lpszPath, dwFlags);
748
749   return SHGetValueW(hKey, lpszSubKey, lpszValue, 0, lpszPath, &dwSize);
750 }
751
752
753 /*************************************************************************
754  * SHRegSetPathA   [SHLWAPI.@]
755  *
756  * Write a path to the registry.
757  *
758  * PARAMS
759  *   hKey       [I] Handle to registry key
760  *   lpszSubKey [I] Name of sub key containing path to set
761  *   lpszValue  [I] Name of value containing path to set
762  *   lpszPath   [O] Path to write
763  *   dwFlags    [I] Reserved
764  *
765  * RETURNS
766  *   Success: ERROR_SUCCESS.
767  *   Failure: An error code from SHSetValueA.
768  */
769 DWORD WINAPI SHRegSetPathA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue,
770                            LPCSTR lpszPath, DWORD dwFlags)
771 {
772   char szBuff[MAX_PATH];
773
774   FIXME("(hkey=0x%08x,%s,%s,%p,%ld) - semi-stub\n",hKey, debugstr_a(lpszSubKey),
775         debugstr_a(lpszValue), lpszPath, dwFlags);
776
777   lstrcpyA(szBuff, lpszPath);
778
779   /* FIXME: PathUnExpandEnvStringsA(szBuff); */
780
781   return SHSetValueA(hKey,lpszSubKey, lpszValue, REG_SZ, szBuff,
782                      lstrlenA(szBuff));
783 }
784
785 /*************************************************************************
786  * SHRegSetPathW   [SHLWAPI.@]
787  *
788  * See SHRegSetPathA.
789  */
790 DWORD WINAPI SHRegSetPathW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue,
791                            LPCWSTR lpszPath, DWORD dwFlags)
792 {
793   WCHAR szBuff[MAX_PATH];
794
795   FIXME("(hkey=0x%08x,%s,%s,%p,%ld) - semi-stub\n",hKey, debugstr_w(lpszSubKey),
796         debugstr_w(lpszValue), lpszPath, dwFlags);
797
798   lstrcpyW(szBuff, lpszPath);
799
800   /* FIXME: PathUnExpandEnvStringsW(szBuff); */
801
802   return SHSetValueW(hKey,lpszSubKey, lpszValue, REG_SZ, szBuff,
803                      lstrlenW(szBuff));
804 }
805
806 /*************************************************************************
807  * SHGetValueA   [SHLWAPI.@]
808  *
809  * Get a value from the registry.
810  *
811  * PARAMS
812  *   hKey       [I] Handle to registry key
813  *   lpszSubKey [I] Name of sub key containing value to get
814  *   lpszValue  [I] Name of value to get
815  *   pwType     [O] Pointer to the values type
816  *   pvData     [O] Pointer to the values data
817  *   pcbData    [O] Pointer to the values size
818  *
819  * RETURNS
820  *   Success: ERROR_SUCCESS. Output parameters contain the details read.
821  *   Failure: An error code from RegOpenKeyExA or SHQueryValueExA.
822  */
823 DWORD WINAPI SHGetValueA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue,
824                          LPDWORD pwType, LPVOID pvData, LPDWORD pcbData)
825 {
826   DWORD dwRet = 0;
827   HKEY hSubKey = 0;
828
829   TRACE("(hkey=0x%08x,%s,%s,%p,%p,%p)\n", hKey, debugstr_a(lpszSubKey),
830         debugstr_a(lpszValue), pwType, pvData, pcbData);
831
832   /*   lpszSubKey can be 0. In this case the value is taken from the
833    *   current key.
834    */
835   if(lpszSubKey)
836     dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_QUERY_VALUE, &hSubKey);
837
838   if (! dwRet)
839   {
840     /* SHQueryValueEx expands Environment strings */
841     dwRet = SHQueryValueExA(hSubKey ? hSubKey : hKey, lpszValue, 0, pwType, pvData, pcbData);
842     if (hSubKey) RegCloseKey(hSubKey);
843   }
844   return dwRet;
845 }
846
847 /*************************************************************************
848  * SHGetValueW   [SHLWAPI.@]
849  *
850  * See SHGetValueA.
851  */
852 DWORD WINAPI SHGetValueW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue,
853                          LPDWORD pwType, LPVOID pvData, LPDWORD pcbData)
854 {
855   DWORD dwRet = 0;
856   HKEY hSubKey = 0;
857
858   TRACE("(hkey=0x%08x,%s,%s,%p,%p,%p)\n", hKey, debugstr_w(lpszSubKey),
859         debugstr_w(lpszValue), pwType, pvData, pcbData);
860
861   if(lpszSubKey)
862     dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_QUERY_VALUE, &hSubKey);
863
864   if (! dwRet)
865   {
866     dwRet = SHQueryValueExW(hSubKey ? hSubKey : hKey, lpszValue, 0, pwType, pvData, pcbData);
867     if (hSubKey) RegCloseKey(hSubKey);
868   }
869   return dwRet;
870 }
871
872 /*************************************************************************
873  * SHSetValueA   [SHLWAPI.@]
874  *
875  * Set a value in the registry.
876  *
877  * PARAMS
878  *   hKey       [I] Handle to registry key
879  *   lpszSubKey [I] Name of sub key under hKey
880  *   lpszValue  [I] Name of value to set
881  *   dwType     [I] Type of the value
882  *   pvData     [I] Data of the value
883  *   cbData     [I] Size of the value
884  *
885  * RETURNS
886  *   Success: ERROR_SUCCESS. The value is set with the data given.
887  *   Failure: An error code from RegCreateKeyExA or RegSetValueExA
888  *
889  * NOTES
890  *   If the sub key does not exist, it is created before the value is set. If
891  *   The sub key is NULL or an empty string, then the value is added directly
892  *   to hKey instead.
893  */
894 DWORD WINAPI SHSetValueA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue,
895                          DWORD dwType, LPCVOID pvData, DWORD cbData)
896 {
897   DWORD dwRet = ERROR_SUCCESS, dwDummy;
898   HKEY  hSubKey;
899   LPSTR szEmpty = "";
900
901   TRACE("(hkey=0x%08x,%s,%s,%ld,%p,%ld)\n", hKey, debugstr_a(lpszSubKey),
902           debugstr_a(lpszValue), dwType, pvData, cbData);
903
904   if (lpszSubKey && *lpszSubKey)
905     dwRet = RegCreateKeyExA(hKey, lpszSubKey, 0, szEmpty,
906                             0, KEY_SET_VALUE, NULL, &hSubKey, &dwDummy);
907   else
908     hSubKey = hKey;
909   if (!dwRet)
910   {
911     dwRet = RegSetValueExA(hSubKey, lpszValue, 0, dwType, pvData, cbData);
912     if (hSubKey != hKey)
913       RegCloseKey(hSubKey);
914   }
915   return dwRet;
916 }
917
918 /*************************************************************************
919  * SHSetValueW   [SHLWAPI.@]
920  *
921  * See SHSetValueA.
922  */
923 DWORD WINAPI SHSetValueW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue,
924                          DWORD dwType, LPCVOID pvData, DWORD cbData)
925 {
926   DWORD dwRet = ERROR_SUCCESS, dwDummy;
927   HKEY  hSubKey;
928   WCHAR szEmpty[] = { '\0' };
929
930   TRACE("(hkey=0x%08x,%s,%s,%ld,%p,%ld)\n", hKey, debugstr_w(lpszSubKey),
931         debugstr_w(lpszValue), dwType, pvData, cbData);
932
933   if (lpszSubKey && *lpszSubKey)
934     dwRet = RegCreateKeyExW(hKey, lpszSubKey, 0, szEmpty,
935                             0, KEY_SET_VALUE, NULL, &hSubKey, &dwDummy);
936   else
937     hSubKey = hKey;
938   if (!dwRet)
939   {
940     dwRet = RegSetValueExW(hSubKey, lpszValue, 0, dwType, pvData, cbData);
941     if (hSubKey != hKey)
942       RegCloseKey(hSubKey);
943   }
944   return dwRet;
945 }
946
947 /*************************************************************************
948  * SHQueryInfoKeyA   [SHLWAPI.@]
949  *
950  * Get information about a registry key. See RegQueryInfoKeyA.
951  */
952 LONG WINAPI SHQueryInfoKeyA(HKEY hKey, LPDWORD pwSubKeys, LPDWORD pwSubKeyMax,
953                             LPDWORD pwValues, LPDWORD pwValueMax)
954 {
955   TRACE("(hkey=0x%08x,%p,%p,%p,%p)\n", hKey, pwSubKeys, pwSubKeyMax,
956         pwValues, pwValueMax);
957   return RegQueryInfoKeyA(hKey, NULL, NULL, NULL, pwSubKeys, pwSubKeyMax,
958                           NULL, pwValues, pwValueMax, NULL, NULL, NULL);
959 }
960
961 /*************************************************************************
962  * SHQueryInfoKeyW   [SHLWAPI.@]
963  *
964  * See SHQueryInfoKeyA
965  */
966 LONG WINAPI SHQueryInfoKeyW(HKEY hKey, LPDWORD pwSubKeys, LPDWORD pwSubKeyMax,
967                             LPDWORD pwValues, LPDWORD pwValueMax)
968 {
969   TRACE("(hkey=0x%08x,%p,%p,%p,%p)\n", hKey, pwSubKeys, pwSubKeyMax,
970         pwValues, pwValueMax);
971   return RegQueryInfoKeyW(hKey, NULL, NULL, NULL, pwSubKeys, pwSubKeyMax,
972                           NULL, pwValues, pwValueMax, NULL, NULL, NULL);
973 }
974
975 /*
976   DWORD dwRet, dwType, dwDataLen;
977
978   FIXME("(hkey=0x%08x,%s,%p,%p,%p,%p=%ld)\n", hKey, debugstr_a(lpszValue),
979         lpReserved, pwType, pvData, pcbData, pcbData ? *pcbData : 0);
980
981   if (pcbData) dwDataLen = *pcbData;
982
983   dwRet = RegQueryValueExA(hKey, lpszValue, lpReserved, &dwType, pvData, &dwDataLen);
984   if (!dwRet)
985   {
986     if (dwType == REG_EXPAND_SZ)
987     {
988       LPSTR szExpand;
989       LPBYTE pData = pvData;
990
991       if (!pData)
992       {
993         if (!pcbData || !(pData = (LPBYTE) LocalAlloc(GMEM_ZEROINIT, *pcbData)))
994           return ERROR_OUTOFMEMORY;
995
996         if ((dwRet = RegQueryValueExA (hKey, lpszValue, lpReserved, &dwType, pData, &dwDataLen)))
997           return dwRet;
998       }
999
1000       if (!pcbData && pData != pvData)
1001       {
1002         WARN("Invalid pcbData would crash under Win32!\n");
1003         return ERROR_OUTOFMEMORY;
1004       }
1005
1006       szExpand = (LPBYTE) LocalAlloc(GMEM_ZEROINIT, *pcbData);
1007       if (!szExpand)
1008       {
1009         if ( pData != pvData ) LocalFree((HLOCAL)pData);
1010         return ERROR_OUTOFMEMORY;
1011       }
1012       if ((ExpandEnvironmentStringsA(pvData, szExpand, *pcbData) > 0))
1013       {
1014         dwDataLen = strlen(szExpand) + 1;
1015         strncpy(pvData, szExpand, *pcbData);
1016       }
1017       else
1018       {
1019         if ( pData != pvData ) LocalFree((HLOCAL)pData);
1020         LocalFree((HLOCAL)szExpand);
1021         return GetLastError();
1022       }
1023       if (pData != pvData) LocalFree((HLOCAL)pData);
1024       LocalFree((HLOCAL)szExpand);
1025       dwType = REG_SZ;
1026     }
1027     if (dwType == REG_SZ && pvData && pcbData && dwDataLen >= *pcbData)
1028     {
1029       ((LPBYTE) pvData)[*pcbData] = '\0';
1030     }
1031   }
1032   if ( pwType ) *pwType = dwType;
1033   if ( pcbData ) *pcbData = dwDataLen;
1034   return dwRet;
1035 */
1036
1037 /*************************************************************************
1038  * SHQueryValueExA   [SHLWAPI.@]
1039  *
1040  * Get a value from the registry, expanding environment variable strings.
1041  *
1042  * PARAMS
1043  *   hKey       [I] Handle to registry key
1044  *   lpszValue  [I] Name of value to query
1045  *   lpReserved [O] Reserved for future use; must be NULL
1046  *   pwType     [O] Optional pointer updated with the values type
1047  *   pvData     [O] Optional pointer updated with the values data
1048  *   pcbData    [O] Optional pointer updated with the values size
1049  *
1050  * RETURNS
1051  *   Success: ERROR_SUCCESS. Any non-NULL output parameters are updated with
1052  *            information about the value.
1053  *   Failure: ERROR_OUTOFMEMORY if memory allocation fails, or the type of the
1054  *            data is REG_EXPAND_SZ and pcbData is NULL. Otherwise an error
1055  *            code from RegQueryValueExA or ExpandEnvironmentStringsA.
1056  *
1057  * NOTES
1058  *   Either pwType, pvData or pcbData may be NULL if the caller doesn't want
1059  *   the type, data or size information for the value.
1060  *
1061  *   If the type of the data is REG_EXPAND_SZ, it is expanded to REG_SZ. The
1062  *   value returned will be truncated if it is of type REG_SZ and bigger than
1063  *   the buffer given to store it.
1064  */
1065 DWORD WINAPI SHQueryValueExA( HKEY hKey, LPCSTR lpszValue,
1066                               LPDWORD lpReserved, LPDWORD pwType,
1067                               LPVOID pvData, LPDWORD pcbData)
1068 {
1069   DWORD dwRet, dwType, dwUnExpDataLen = 0, dwExpDataLen;
1070
1071   TRACE("(hkey=0x%08x,%s,%p,%p,%p,%p=%ld)\n", hKey, debugstr_a(lpszValue),
1072         lpReserved, pwType, pvData, pcbData, pcbData ? *pcbData : 0);
1073
1074   if (pcbData) dwUnExpDataLen = *pcbData;
1075
1076   dwRet = RegQueryValueExA(hKey, lpszValue, lpReserved, &dwType, pvData, &dwUnExpDataLen);
1077
1078   if (pcbData && (dwType == REG_EXPAND_SZ))
1079   {
1080     DWORD nBytesToAlloc;
1081
1082     /* Expand type REG_EXPAND_SZ into REG_SZ */
1083     LPSTR szData;
1084
1085     /* If the caller didn't supply a buffer or the buffer is to small we have
1086      * to allocate our own
1087      */
1088     if ((!pvData) || (dwRet == ERROR_MORE_DATA) )
1089     {
1090       char cNull = '\0';
1091       nBytesToAlloc = (!pvData || (dwRet == ERROR_MORE_DATA)) ? dwUnExpDataLen : *pcbData;
1092
1093       szData = (LPSTR) LocalAlloc(GMEM_ZEROINIT, nBytesToAlloc);
1094       RegQueryValueExA (hKey, lpszValue, lpReserved, NULL, (LPBYTE)szData, &nBytesToAlloc);
1095       dwExpDataLen = ExpandEnvironmentStringsA(szData, &cNull, 1);
1096       dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen);
1097       LocalFree((HLOCAL) szData);
1098     }
1099     else
1100     {
1101       nBytesToAlloc = lstrlenA(pvData) * sizeof (CHAR);
1102       szData = (LPSTR) LocalAlloc(GMEM_ZEROINIT, nBytesToAlloc + 1);
1103       lstrcpyA(szData, pvData);
1104       dwExpDataLen = ExpandEnvironmentStringsA(szData, pvData, *pcbData / sizeof(CHAR));
1105       if (dwExpDataLen > *pcbData) dwRet = ERROR_MORE_DATA;
1106       dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen);
1107       LocalFree((HLOCAL) szData);
1108     }
1109   }
1110
1111   /* Update the type and data size if the caller wanted them */
1112   if ( dwType == REG_EXPAND_SZ ) dwType = REG_SZ;
1113   if ( pwType ) *pwType = dwType;
1114   if ( pcbData ) *pcbData = dwUnExpDataLen;
1115   return dwRet;
1116 }
1117
1118
1119 /*************************************************************************
1120  * SHQueryValueExW   [SHLWAPI.@]
1121  *
1122  * See SHQueryValueExA.
1123  */
1124 DWORD WINAPI SHQueryValueExW(HKEY hKey, LPCWSTR lpszValue,
1125                              LPDWORD lpReserved, LPDWORD pwType,
1126                              LPVOID pvData, LPDWORD pcbData)
1127 {
1128   DWORD dwRet, dwType, dwUnExpDataLen = 0, dwExpDataLen;
1129
1130   TRACE("(hkey=0x%08x,%s,%p,%p,%p,%p=%ld)\n", hKey, debugstr_w(lpszValue),
1131         lpReserved, pwType, pvData, pcbData, pcbData ? *pcbData : 0);
1132
1133   if (pcbData) dwUnExpDataLen = *pcbData;
1134
1135   dwRet = RegQueryValueExW(hKey, lpszValue, lpReserved, &dwType, pvData, &dwUnExpDataLen);
1136
1137   if (pcbData && (dwType == REG_EXPAND_SZ))
1138   {
1139     DWORD nBytesToAlloc;
1140
1141     /* Expand type REG_EXPAND_SZ into REG_SZ */
1142     LPWSTR szData;
1143
1144     /* If the caller didn't supply a buffer or the buffer is to small we have
1145      * to allocate our own
1146      */
1147     if ((!pvData) || (dwRet == ERROR_MORE_DATA) )
1148     {
1149       WCHAR cNull = '\0';
1150       nBytesToAlloc = (!pvData || (dwRet == ERROR_MORE_DATA)) ? dwUnExpDataLen : *pcbData;
1151
1152       szData = (LPWSTR) LocalAlloc(GMEM_ZEROINIT, nBytesToAlloc);
1153       RegQueryValueExW (hKey, lpszValue, lpReserved, NULL, (LPBYTE)szData, &nBytesToAlloc);
1154       dwExpDataLen = ExpandEnvironmentStringsW(szData, &cNull, 1);
1155       dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen);
1156       LocalFree((HLOCAL) szData);
1157     }
1158     else
1159     {
1160       nBytesToAlloc = lstrlenW(pvData) * sizeof(WCHAR);
1161       szData = (LPWSTR) LocalAlloc(GMEM_ZEROINIT, nBytesToAlloc + 1);
1162       lstrcpyW(szData, pvData);
1163       dwExpDataLen = ExpandEnvironmentStringsW(szData, pvData, *pcbData/sizeof(WCHAR) );
1164       if (dwExpDataLen > *pcbData) dwRet = ERROR_MORE_DATA;
1165       dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen);
1166       LocalFree((HLOCAL) szData);
1167     }
1168   }
1169
1170   /* Update the type and data size if the caller wanted them */
1171   if ( dwType == REG_EXPAND_SZ ) dwType = REG_SZ;
1172   if ( pwType ) *pwType = dwType;
1173   if ( pcbData ) *pcbData = dwUnExpDataLen;
1174   return dwRet;
1175 }
1176
1177 /*************************************************************************
1178  * SHDeleteKeyA   [SHLWAPI.@]
1179  *
1180  * Delete a registry key and any sub keys/values present
1181  *
1182  * PARAMS
1183  *   hKey       [I] Handle to registry key
1184  *   lpszSubKey [I] Name of sub key to delete
1185  *
1186  * RETURNS
1187  *   Success: ERROR_SUCCESS. The key is deleted.
1188  *   Failure: An error code from RegOpenKeyExA, RegQueryInfoKeyA,
1189  *          RegEnumKeyExA or RegDeleteKeyA.
1190  */
1191 DWORD WINAPI SHDeleteKeyA(HKEY hKey, LPCSTR lpszSubKey)
1192 {
1193   DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
1194   CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
1195   HKEY hSubKey = 0;
1196
1197   TRACE("(hkey=0x%08x,%s)\n", hKey, debugstr_a(lpszSubKey));
1198
1199   dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1200   if(!dwRet)
1201   {
1202     /* Find how many subkeys there are */
1203     dwRet = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1204                              &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
1205     if(!dwRet)
1206     {
1207       dwMaxSubkeyLen++;
1208       if (dwMaxSubkeyLen > sizeof(szNameBuf))
1209         /* Name too big: alloc a buffer for it */
1210         lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(CHAR));
1211
1212       if(!lpszName)
1213         dwRet = ERROR_NOT_ENOUGH_MEMORY;
1214       else
1215       {
1216         /* Recursively delete all the subkeys */
1217         for(i = 0; i < dwKeyCount && !dwRet; i++)
1218         {
1219           dwSize = dwMaxSubkeyLen;
1220           dwRet = RegEnumKeyExA(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
1221           if(!dwRet)
1222             dwRet = SHDeleteKeyA(hSubKey, lpszName);
1223         }
1224         if (lpszName != szNameBuf)
1225           HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
1226       }
1227     }
1228
1229     RegCloseKey(hSubKey);
1230     if(!dwRet)
1231       dwRet = RegDeleteKeyA(hKey, lpszSubKey);
1232   }
1233   return dwRet;
1234 }
1235
1236 /*************************************************************************
1237  * SHDeleteKeyW   [SHLWAPI.@]
1238  *
1239  * See SHDeleteKeyA.
1240  */
1241 DWORD WINAPI SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey)
1242 {
1243   DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
1244   WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
1245   HKEY hSubKey = 0;
1246
1247   TRACE("(hkey=0x%08x,%s)\n", hKey, debugstr_w(lpszSubKey));
1248
1249   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1250   if(!dwRet)
1251   {
1252     /* Find how many subkeys there are */
1253     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1254                              &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
1255     if(!dwRet)
1256     {
1257       dwMaxSubkeyLen++;
1258       if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
1259         /* Name too big: alloc a buffer for it */
1260         lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
1261
1262       if(!lpszName)
1263         dwRet = ERROR_NOT_ENOUGH_MEMORY;
1264       else
1265       {
1266         /* Recursively delete all the subkeys */
1267         for(i = 0; i < dwKeyCount && !dwRet; i++)
1268         {
1269           dwSize = dwMaxSubkeyLen;
1270           dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
1271           if(!dwRet)
1272             dwRet = SHDeleteKeyW(hSubKey, lpszName);
1273         }
1274
1275         if (lpszName != szNameBuf)
1276           HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
1277       }
1278     }
1279
1280     RegCloseKey(hSubKey);
1281     if(!dwRet)
1282       dwRet = RegDeleteKeyW(hKey, lpszSubKey);
1283   }
1284   return dwRet;
1285 }
1286
1287 /*************************************************************************
1288  * SHDeleteEmptyKeyA   [SHLWAPI.@]
1289  *
1290  * Delete a registry key with no sub keys.
1291  *
1292  * PARAMS
1293  *   hKey       [I] Handle to registry key
1294  *   lpszSubKey [I] Name of sub key to delete
1295  *
1296  * RETURNS
1297  *   Success: ERROR_SUCCESS. The key is deleted.
1298  *   Failure: If the key is not empty, returns ERROR_KEY_HAS_CHILDREN. Otherwise
1299  *          returns an error code from RegOpenKeyExA, RegQueryInfoKeyA or
1300  *          RegDeleteKeyA.
1301  */
1302 DWORD WINAPI SHDeleteEmptyKeyA(HKEY hKey, LPCSTR lpszSubKey)
1303 {
1304   DWORD dwRet, dwKeyCount = 0;
1305   HKEY hSubKey = 0;
1306
1307   TRACE("(hkey=0x%08x,%s)\n", hKey, debugstr_a(lpszSubKey));
1308
1309   dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1310   if(!dwRet)
1311   {
1312     dwRet = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1313                              NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1314     RegCloseKey(hSubKey);
1315     if(!dwRet)
1316     {
1317       if (!dwKeyCount)
1318         dwRet = RegDeleteKeyA(hKey, lpszSubKey);
1319       else
1320         dwRet = ERROR_KEY_HAS_CHILDREN;
1321     }
1322   }
1323   return dwRet;
1324 }
1325
1326 /*************************************************************************
1327  * SHDeleteEmptyKeyW   [SHLWAPI.@]
1328  *
1329  * See SHDeleteEmptyKeyA.
1330  */
1331 DWORD WINAPI SHDeleteEmptyKeyW(HKEY hKey, LPCWSTR lpszSubKey)
1332 {
1333   DWORD dwRet, dwKeyCount = 0;
1334   HKEY hSubKey = 0;
1335
1336   TRACE("(hkey=0x%08x, %s)\n", hKey, debugstr_w(lpszSubKey));
1337
1338   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1339   if(!dwRet)
1340   {
1341     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1342                              NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1343     RegCloseKey(hSubKey);
1344     if(!dwRet)
1345     {
1346       if (!dwKeyCount)
1347         dwRet = RegDeleteKeyW(hKey, lpszSubKey);
1348       else
1349         dwRet = ERROR_KEY_HAS_CHILDREN;
1350     }
1351   }
1352   return dwRet;
1353 }
1354
1355 /*************************************************************************
1356  * SHDeleteOrphanKeyA   [SHLWAPI.@]
1357  *
1358  * Delete a registry key with no sub keys or values.
1359  *
1360  * PARAMS
1361  *   hKey       [I] Handle to registry key
1362  *   lpszSubKey [I] Name of sub key to possibly delete
1363  *
1364  * RETURNS
1365  *   Success: ERROR_SUCCESS. The key has been deleted if it was an orphan.
1366  *   Failure: An error from RegOpenKeyExA, RegQueryValueExA, or RegDeleteKeyA.
1367  */
1368 DWORD WINAPI SHDeleteOrphanKeyA(HKEY hKey, LPCSTR lpszSubKey)
1369 {
1370   HKEY hSubKey;
1371   DWORD dwKeyCount = 0, dwValueCount = 0, dwRet;
1372
1373   TRACE("(hkey=0x%08x,%s)\n", hKey, debugstr_a(lpszSubKey));
1374
1375   dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1376
1377   if(!dwRet)
1378   {
1379     /* Get subkey and value count */
1380     dwRet = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1381                              NULL, NULL, &dwValueCount, NULL, NULL, NULL, NULL);
1382
1383     if(!dwRet && !dwKeyCount && !dwValueCount)
1384     {
1385       dwRet = RegDeleteKeyA(hKey, lpszSubKey);
1386     }
1387     RegCloseKey(hSubKey);
1388   }
1389   return dwRet;
1390 }
1391
1392 /*************************************************************************
1393  * SHDeleteOrphanKeyW   [SHLWAPI.@]
1394  *
1395  * See SHDeleteOrphanKeyA.
1396  */
1397 DWORD WINAPI SHDeleteOrphanKeyW(HKEY hKey, LPCWSTR lpszSubKey)
1398 {
1399   HKEY hSubKey;
1400   DWORD dwKeyCount = 0, dwValueCount = 0, dwRet;
1401
1402   TRACE("(hkey=0x%08x,%s)\n", hKey, debugstr_w(lpszSubKey));
1403
1404   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1405
1406   if(!dwRet)
1407   {
1408     /* Get subkey and value count */
1409     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1410                              NULL, NULL, &dwValueCount, NULL, NULL, NULL, NULL);
1411
1412     if(!dwRet && !dwKeyCount && !dwValueCount)
1413     {
1414       dwRet = RegDeleteKeyW(hKey, lpszSubKey);
1415     }
1416     RegCloseKey(hSubKey);
1417   }
1418   return dwRet;
1419 }
1420
1421 /*************************************************************************
1422  * SHDeleteValueA   [SHLWAPI.@]
1423  *
1424  * Delete a value from the registry.
1425  *
1426  * PARAMS
1427  *   hKey       [I] Handle to registry key
1428  *   lpszSubKey [I] Name of sub key containing value to delete
1429  *   lpszValue  [I] Name of value to delete
1430  *
1431  * RETURNS
1432  *   Success: ERROR_SUCCESS. The value is deleted.
1433  *   Failure: An error code from RegOpenKeyExA or RegDeleteValueA.
1434  */
1435 DWORD WINAPI SHDeleteValueA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue)
1436 {
1437   DWORD dwRet;
1438   HKEY hSubKey;
1439
1440   TRACE("(hkey=0x%08x,%s,%s)\n", hKey, debugstr_a(lpszSubKey), debugstr_a(lpszValue));
1441
1442   dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_SET_VALUE, &hSubKey);
1443   if (!dwRet)
1444   {
1445     dwRet = RegDeleteValueA(hSubKey, lpszValue);
1446     RegCloseKey(hSubKey);
1447   }
1448   return dwRet;
1449 }
1450
1451 /*************************************************************************
1452  * SHDeleteValueW   [SHLWAPI.@]
1453  *
1454  * See SHDeleteValueA.
1455  */
1456 DWORD WINAPI SHDeleteValueW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue)
1457 {
1458   DWORD dwRet;
1459   HKEY hSubKey;
1460
1461   TRACE("(hkey=0x%08x,%s,%s)\n", hKey, debugstr_w(lpszSubKey), debugstr_w(lpszValue));
1462
1463   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_SET_VALUE, &hSubKey);
1464   if (!dwRet)
1465   {
1466     dwRet = RegDeleteValueW(hSubKey, lpszValue);
1467     RegCloseKey(hSubKey);
1468   }
1469   return dwRet;
1470 }
1471
1472 /*************************************************************************
1473  * SHEnumKeyExA   [SHLWAPI.@]
1474  *
1475  * Enumerate sub keys in a registry key.
1476  *
1477  * PARAMS
1478  *   hKey       [I] Handle to registry key
1479  *   dwIndex    [I] Index of key to enumerate
1480  *   lpszSubKey [O] Pointer updated with the subkey name
1481  *   pwLen      [O] Pointer updated with the subkey length
1482  *
1483  * RETURN
1484  *   Success: ERROR_SUCCESS. lpszSubKey and pwLen are updated.
1485  *   Failure: An error code from RegEnumKeyExA.
1486  */
1487 LONG WINAPI SHEnumKeyExA(HKEY hKey, DWORD dwIndex, LPSTR lpszSubKey,
1488                          LPDWORD pwLen)
1489 {
1490   TRACE("(hkey=0x%08x,%ld,%s,%p)\n", hKey, dwIndex, debugstr_a(lpszSubKey), pwLen);
1491
1492   return RegEnumKeyExA(hKey, dwIndex, lpszSubKey, pwLen, NULL, NULL, NULL, NULL);
1493 }
1494
1495 /*************************************************************************
1496  * SHEnumKeyExW   [SHLWAPI.@]
1497  *
1498  * See SHEnumKeyExA.
1499  */
1500 LONG WINAPI SHEnumKeyExW(HKEY hKey, DWORD dwIndex, LPWSTR lpszSubKey,
1501                          LPDWORD pwLen)
1502 {
1503   TRACE("(hkey=0x%08x,%ld,%s,%p)\n", hKey, dwIndex, debugstr_w(lpszSubKey), pwLen);
1504
1505   return RegEnumKeyExW(hKey, dwIndex, lpszSubKey, pwLen, NULL, NULL, NULL, NULL);
1506 }
1507
1508 /*************************************************************************
1509  * SHEnumValueA   [SHLWAPI.@]
1510  *
1511  * Enumerate values in a registry key.
1512  *
1513  * PARAMS
1514  *   hKey      [I] Handle to registry key
1515  *   dwIndex   [I] Index of key to enumerate
1516  *   lpszValue [O] Pointer updated with the values name
1517  *   pwLen     [O] Pointer updated with the values length
1518  *   pwType    [O] Pointer updated with the values type
1519  *   pvData    [O] Pointer updated with the values data
1520  *   pcbData   [O] Pointer updated with the values size
1521  *
1522  * RETURNS
1523  *   Success: ERROR_SUCCESS. Output parameters are updated.
1524  *   Failure: An error code from RegEnumValueExA.
1525  */
1526 LONG WINAPI SHEnumValueA(HKEY hKey, DWORD dwIndex, LPSTR lpszValue,
1527                          LPDWORD pwLen, LPDWORD pwType,
1528                          LPVOID pvData, LPDWORD pcbData)
1529 {
1530   TRACE("(hkey=0x%08x,%ld,%s,%p,%p,%p,%p)\n", hKey, dwIndex,
1531         debugstr_a(lpszValue), pwLen, pwType, pvData, pcbData);
1532
1533  return RegEnumValueA(hKey, dwIndex, lpszValue, pwLen, NULL,
1534                       pwType, pvData, pcbData);
1535 }
1536
1537 /*************************************************************************
1538  * SHEnumValueW   [SHLWAPI.@]
1539  *
1540  * See SHEnumValueA.
1541  */
1542 LONG WINAPI SHEnumValueW(HKEY hKey, DWORD dwIndex, LPWSTR lpszValue,
1543                          LPDWORD pwLen, LPDWORD pwType,
1544                          LPVOID pvData, LPDWORD pcbData)
1545 {
1546   TRACE("(hkey=0x%08x,%ld,%s,%p,%p,%p,%p)\n", hKey, dwIndex,
1547         debugstr_w(lpszValue), pwLen, pwType, pvData, pcbData);
1548
1549   return RegEnumValueW(hKey, dwIndex, lpszValue, pwLen, NULL,
1550                        pwType, pvData, pcbData);
1551 }
1552
1553 /*************************************************************************
1554  * @   [SHLWAPI.205]
1555  *
1556  * Wrapper for SHGetValueA in case machine is in safe mode.
1557  */
1558 DWORD WINAPI SHLWAPI_205(HKEY hkey, LPCSTR pSubKey, LPCSTR pValue,
1559                          LPDWORD pwType, LPVOID pvData, LPDWORD pbData)
1560 {
1561   if (GetSystemMetrics(SM_CLEANBOOT))
1562     return ERROR_INVALID_FUNCTION;
1563   return SHGetValueA(hkey, pSubKey, pValue, pwType, pvData, pbData);
1564 }
1565
1566 /*************************************************************************
1567  * @   [SHLWAPI.206]
1568  *
1569  * Unicode version of SHLWAPI_205.
1570  */
1571 DWORD WINAPI SHLWAPI_206(HKEY hkey, LPCWSTR pSubKey, LPCWSTR pValue,
1572                          LPDWORD pwType, LPVOID pvData, LPDWORD pbData)
1573 {
1574   if (GetSystemMetrics(SM_CLEANBOOT))
1575     return ERROR_INVALID_FUNCTION;
1576   return SHGetValueW(hkey, pSubKey, pValue, pwType, pvData, pbData);
1577 }
1578
1579 /*************************************************************************
1580  * @   [SHLWAPI.320]
1581  *
1582  * Set a content type in the registry.
1583  *
1584  * PARAMS
1585  *   hKey       [I] Handle to registry key
1586  *   lpszSubKey [I] Name of sub key under hKey
1587  *   lpszValue  [I] Value to set
1588  *
1589  * RETURNS
1590  *   Success: TRUE
1591  *   Failure: FALSE
1592  */
1593 BOOL WINAPI SHLWAPI_320(LPCSTR lpszSubKey, LPCSTR lpszValue)
1594 {
1595   DWORD dwRet;
1596
1597   if (!lpszValue)
1598   {
1599     WARN("Invalid lpszValue would crash under Win32!\n");
1600     return FALSE;
1601   }
1602
1603   dwRet = SHSetValueA(HKEY_CLASSES_ROOT, lpszSubKey, lpszContentTypeA,
1604                       REG_SZ, lpszValue, strlen(lpszValue));
1605   return dwRet ? FALSE : TRUE;
1606 }
1607
1608 /*************************************************************************
1609  * @   [SHLWAPI.321]
1610  *
1611  * Unicode version of SHLWAPI_320.
1612  */
1613 BOOL WINAPI SHLWAPI_321(LPCWSTR lpszSubKey, LPCWSTR lpszValue)
1614 {
1615   DWORD dwRet;
1616
1617   if (!lpszValue)
1618   {
1619     WARN("Invalid lpszValue would crash under Win32!\n");
1620     return FALSE;
1621   }
1622
1623   dwRet = SHSetValueW(HKEY_CLASSES_ROOT, lpszSubKey, lpszContentTypeW,
1624                       REG_SZ, lpszValue, strlenW(lpszValue));
1625   return dwRet ? FALSE : TRUE;
1626 }
1627
1628 /*************************************************************************
1629  * @   [SHLWAPI.322]
1630  *
1631  * Delete a content type from the registry.
1632  *
1633  * PARAMS
1634  *   lpszSubKey [I] Name of sub key
1635  *
1636  * RETURNS
1637  *   Success: TRUE
1638  *   Failure: FALSE
1639  */
1640 BOOL WINAPI SHLWAPI_322(LPCSTR lpszSubKey)
1641 {
1642   HRESULT ret = SHDeleteValueA(HKEY_CLASSES_ROOT, lpszSubKey, lpszContentTypeA);
1643   return ret ? FALSE : TRUE;
1644 }
1645
1646 /*************************************************************************
1647  * @   [SHLWAPI.323]
1648  *
1649  * Unicode version of SHLWAPI_322.
1650  */
1651 BOOL WINAPI SHLWAPI_323(LPCWSTR lpszSubKey)
1652 {
1653   HRESULT ret = SHDeleteValueW(HKEY_CLASSES_ROOT, lpszSubKey, lpszContentTypeW);
1654   return ret ? FALSE : TRUE;
1655 }
1656
1657
1658 /*************************************************************************
1659  * SHRegDuplicateHKey   [SHLWAPI.@]
1660  */
1661 HKEY WINAPI SHRegDuplicateHKey(HKEY hKey)
1662 {
1663     HKEY newKey = 0;
1664
1665     RegOpenKeyExA(hKey, 0, 0, MAXIMUM_ALLOWED, &newKey);
1666     TRACE("new key is %08x\n", newKey);
1667     return newKey;
1668 }