[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / wine / shellpath.c
1 /*
2 * Path Functions
3 *
4 * Copyright 1998, 1999, 2000 Juergen Schmied
5 * Copyright 2004 Juan Lang
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 *
21 * NOTES:
22 *
23 * Many of these functions are in SHLWAPI.DLL also
24 *
25 */
26
27 #define WIN32_NO_STATUS
28 #define _INC_WINDOWS
29 #define COBJMACROS
30
31 #include <wine/config.h>
32
33 #include <windef.h>
34 #include <winbase.h>
35 #include <shlobj.h>
36 #include <undocshell.h>
37 #include <shlwapi.h>
38 #include <sddl.h>
39 #include <wine/debug.h>
40 #include <wine/unicode.h>
41
42 #include <userenv.h>
43
44 #include "pidl.h"
45 #include "shell32_main.h"
46 #include "shresdef.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(shell);
49
50 static const BOOL is_win64 = sizeof(void *) > sizeof(int);
51
52 typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
53
54 static LPFN_ISWOW64PROCESS fnIsWow64Process = NULL;
55
56 BOOL IsWow64()
57 {
58 BOOL bIsWow64 = FALSE;
59
60 if (!fnIsWow64Process)
61 {
62 fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
63 GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
64 }
65
66 if (!fnIsWow64Process)
67 return FALSE;
68
69 if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
70 return FALSE;
71
72 return bIsWow64;
73 }
74
75
76 /*
77 ########## Combining and Constructing paths ##########
78 */
79
80 /*************************************************************************
81 * PathAppend [SHELL32.36]
82 */
83 BOOL WINAPI PathAppendAW(
84 LPVOID lpszPath1,
85 LPCVOID lpszPath2)
86 {
87 if (SHELL_OsIsUnicode())
88 return PathAppendW(lpszPath1, lpszPath2);
89 return PathAppendA(lpszPath1, lpszPath2);
90 }
91
92 /*************************************************************************
93 * PathBuildRoot [SHELL32.30]
94 */
95 LPVOID WINAPI PathBuildRootAW(LPVOID lpszPath, int drive)
96 {
97 if(SHELL_OsIsUnicode())
98 return PathBuildRootW(lpszPath, drive);
99 return PathBuildRootA(lpszPath, drive);
100 }
101
102 /*************************************************************************
103 * PathGetExtensionA [internal]
104 *
105 * NOTES
106 * exported by ordinal
107 * return value points to the first char after the dot
108 */
109 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
110 {
111 TRACE("(%s)\n",lpszPath);
112
113 lpszPath = PathFindExtensionA(lpszPath);
114 return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
115 }
116
117 /*************************************************************************
118 * PathGetExtensionW [internal]
119 */
120 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
121 {
122 TRACE("(%s)\n",debugstr_w(lpszPath));
123
124 lpszPath = PathFindExtensionW(lpszPath);
125 return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
126 }
127
128 /*************************************************************************
129 * SHPathGetExtension [SHELL32.158]
130 */
131 EXTERN_C LPVOID WINAPI SHPathGetExtensionW(LPCWSTR lpszPath, DWORD void1, DWORD void2)
132 {
133 return PathGetExtensionW(lpszPath);
134 }
135
136 /*************************************************************************
137 * PathRemoveFileSpec [SHELL32.35]
138 */
139 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
140 {
141 if (SHELL_OsIsUnicode())
142 return PathRemoveFileSpecW(lpszPath);
143 return PathRemoveFileSpecA(lpszPath);
144 }
145
146 /*
147 Path Manipulations
148 */
149
150 /*************************************************************************
151 * PathGetShortPathA [internal]
152 */
153 static void PathGetShortPathA(LPSTR pszPath)
154 {
155 CHAR path[MAX_PATH];
156
157 TRACE("%s\n", pszPath);
158
159 if (GetShortPathNameA(pszPath, path, MAX_PATH))
160 {
161 lstrcpyA(pszPath, path);
162 }
163 }
164
165 /*************************************************************************
166 * PathGetShortPathW [internal]
167 */
168 static void PathGetShortPathW(LPWSTR pszPath)
169 {
170 WCHAR path[MAX_PATH];
171
172 TRACE("%s\n", debugstr_w(pszPath));
173
174 if (GetShortPathNameW(pszPath, path, MAX_PATH))
175 {
176 lstrcpyW(pszPath, path);
177 }
178 }
179
180 /*************************************************************************
181 * PathGetShortPath [SHELL32.92]
182 */
183 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
184 {
185 if(SHELL_OsIsUnicode())
186 PathGetShortPathW(pszPath);
187 PathGetShortPathA(pszPath);
188 }
189
190 /*
191 ########## Path Testing ##########
192 */
193
194 /*************************************************************************
195 * PathIsRoot [SHELL32.29]
196 */
197 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
198 {
199 if (SHELL_OsIsUnicode())
200 return PathIsRootW(lpszPath);
201 return PathIsRootA(lpszPath);
202 }
203
204 /*************************************************************************
205 * PathIsExeA [internal]
206 */
207 static BOOL PathIsExeA (LPCSTR lpszPath)
208 {
209 LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
210 int i;
211 static const char * const lpszExtensions[] =
212 {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
213
214 TRACE("path=%s\n",lpszPath);
215
216 for(i=0; lpszExtensions[i]; i++)
217 if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
218
219 return FALSE;
220 }
221
222 /*************************************************************************
223 * PathIsExeW [internal]
224 */
225 BOOL PathIsExeW (LPCWSTR lpszPath)
226 {
227 LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
228 int i;
229 static const WCHAR lpszExtensions[][4] =
230 {{'e','x','e','\0'}, {'c','o','m','\0'}, {'p','i','f','\0'},
231 {'c','m','d','\0'}, {'b','a','t','\0'}, {'s','c','f','\0'},
232 {'s','c','r','\0'}, {'\0'} };
233
234 TRACE("path=%s\n",debugstr_w(lpszPath));
235
236 for(i=0; lpszExtensions[i][0]; i++)
237 if (!strcmpiW(lpszExtension,lpszExtensions[i])) return TRUE;
238
239 return FALSE;
240 }
241
242 /*************************************************************************
243 * PathIsExe [SHELL32.43]
244 */
245 BOOL WINAPI PathIsExeAW (LPCVOID path)
246 {
247 if (SHELL_OsIsUnicode())
248 return PathIsExeW (path);
249 return PathIsExeA(path);
250 }
251
252 /*************************************************************************
253 * PathFileExists [SHELL32.45]
254 */
255 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
256 {
257 if (SHELL_OsIsUnicode())
258 return PathFileExistsW (lpszPath);
259 return PathFileExistsA (lpszPath);
260 }
261
262 /*************************************************************************
263 * PathIsSameRoot [SHELL32.650]
264 */
265 BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2)
266 {
267 if (SHELL_OsIsUnicode())
268 return PathIsSameRootW(lpszPath1, lpszPath2);
269 return PathIsSameRootA(lpszPath1, lpszPath2);
270 }
271
272 /*************************************************************************
273 * IsLFNDriveA [SHELL32.41]
274 */
275 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
276 {
277 DWORD fnlen;
278
279 if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
280 return FALSE;
281 return fnlen > 12;
282 }
283
284 /*************************************************************************
285 * IsLFNDriveW [SHELL32.42]
286 */
287 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
288 {
289 DWORD fnlen;
290
291 if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
292 return FALSE;
293 return fnlen > 12;
294 }
295
296 /*************************************************************************
297 * IsLFNDrive [SHELL32.119]
298 */
299 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
300 {
301 if (SHELL_OsIsUnicode())
302 return IsLFNDriveW(lpszPath);
303 return IsLFNDriveA(lpszPath);
304 }
305
306 /*
307 ########## Creating Something Unique ##########
308 */
309 /*************************************************************************
310 * PathMakeUniqueNameA [internal]
311 */
312 static BOOL PathMakeUniqueNameA(
313 LPSTR lpszBuffer,
314 DWORD dwBuffSize,
315 LPCSTR lpszShortName,
316 LPCSTR lpszLongName,
317 LPCSTR lpszPathName)
318 {
319 FIXME("%p %u %s %s %s stub\n",
320 lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
321 debugstr_a(lpszLongName), debugstr_a(lpszPathName));
322 return TRUE;
323 }
324
325 /*************************************************************************
326 * PathMakeUniqueNameW [internal]
327 */
328 static BOOL PathMakeUniqueNameW(
329 LPWSTR lpszBuffer,
330 DWORD dwBuffSize,
331 LPCWSTR lpszShortName,
332 LPCWSTR lpszLongName,
333 LPCWSTR lpszPathName)
334 {
335 FIXME("%p %u %s %s %s stub\n",
336 lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
337 debugstr_w(lpszLongName), debugstr_w(lpszPathName));
338 return TRUE;
339 }
340
341 /*************************************************************************
342 * PathMakeUniqueName [SHELL32.47]
343 */
344 BOOL WINAPI PathMakeUniqueNameAW(
345 LPVOID lpszBuffer,
346 DWORD dwBuffSize,
347 LPCVOID lpszShortName,
348 LPCVOID lpszLongName,
349 LPCVOID lpszPathName)
350 {
351 if (SHELL_OsIsUnicode())
352 return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
353 return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
354 }
355
356 /*************************************************************************
357 * PathYetAnotherMakeUniqueName [SHELL32.75]
358 */
359 BOOL WINAPI PathYetAnotherMakeUniqueName(LPWSTR buffer, LPCWSTR path, LPCWSTR shortname, LPCWSTR longname)
360 {
361 WCHAR pathW[MAX_PATH], retW[MAX_PATH];
362 const WCHAR *file, *ext;
363 int i = 2;
364
365 TRACE("(%p, %s, %s, %s)\n", buffer, debugstr_w(path), debugstr_w(shortname), debugstr_w(longname));
366
367 file = longname ? longname : shortname;
368 PathCombineW(pathW, path, file);
369 strcpyW(retW, pathW);
370 PathRemoveExtensionW(pathW);
371
372 ext = PathFindExtensionW(file);
373
374 /* now try to make it unique */
375 while (PathFileExistsW(retW))
376 {
377 static const WCHAR fmtW[] = {'%','s',' ','(','%','d',')','%','s',0};
378
379 sprintfW(retW, fmtW, pathW, i, ext);
380 i++;
381 }
382
383 strcpyW(buffer, retW);
384 TRACE("ret - %s\n", debugstr_w(buffer));
385
386 return TRUE;
387 }
388
389 /*
390 ########## cleaning and resolving paths ##########
391 */
392
393 /*************************************************************************
394 * PathCleanupSpec [SHELL32.171]
395 *
396 * lpszFile is changed in place.
397 */
398 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
399 {
400 int i = 0;
401 DWORD rc = 0;
402 int length = 0;
403
404 if (SHELL_OsIsUnicode())
405 {
406 LPWSTR p = lpszFileW;
407
408 TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
409
410 if (lpszPathW)
411 length = strlenW(lpszPathW);
412
413 while (*p)
414 {
415 int gct = PathGetCharTypeW(*p);
416 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
417 {
418 lpszFileW[i]='-';
419 rc |= PCS_REPLACEDCHAR;
420 }
421 else
422 lpszFileW[i]=*p;
423 i++;
424 p++;
425 if (length + i == MAX_PATH)
426 {
427 rc |= PCS_FATAL | PCS_PATHTOOLONG;
428 break;
429 }
430 }
431 lpszFileW[i]=0;
432 }
433 else
434 {
435 LPSTR lpszFileA = (LPSTR)lpszFileW;
436 LPCSTR lpszPathA = (LPCSTR)lpszPathW;
437 LPSTR p = lpszFileA;
438
439 TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
440
441 if (lpszPathA)
442 length = strlen(lpszPathA);
443
444 while (*p)
445 {
446 int gct = PathGetCharTypeA(*p);
447 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
448 {
449 lpszFileA[i]='-';
450 rc |= PCS_REPLACEDCHAR;
451 }
452 else
453 lpszFileA[i]=*p;
454 i++;
455 p++;
456 if (length + i == MAX_PATH)
457 {
458 rc |= PCS_FATAL | PCS_PATHTOOLONG;
459 break;
460 }
461 }
462 lpszFileA[i]=0;
463 }
464 return rc;
465 }
466
467 /*************************************************************************
468 * PathQualifyA [SHELL32]
469 */
470 static BOOL PathQualifyA(LPCSTR pszPath)
471 {
472 FIXME("%s\n",pszPath);
473 return FALSE;
474 }
475
476 /*************************************************************************
477 * PathQualifyW [SHELL32]
478 */
479 static BOOL PathQualifyW(LPCWSTR pszPath)
480 {
481 FIXME("%s\n",debugstr_w(pszPath));
482 return FALSE;
483 }
484
485 /*************************************************************************
486 * PathQualify [SHELL32.49]
487 */
488 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
489 {
490 if (SHELL_OsIsUnicode())
491 return PathQualifyW(pszPath);
492 return PathQualifyA(pszPath);
493 }
494
495 static BOOL PathResolveA(LPSTR path, LPCSTR *paths, DWORD flags)
496 {
497 FIXME("(%s,%p,0x%08x),stub!\n", debugstr_a(path), paths, flags);
498 return FALSE;
499 }
500
501 static BOOL PathResolveW(LPWSTR path, LPCWSTR *paths, DWORD flags)
502 {
503 FIXME("(%s,%p,0x%08x),stub!\n", debugstr_w(path), paths, flags);
504 return FALSE;
505 }
506
507 /*************************************************************************
508 * PathResolve [SHELL32.51]
509 */
510 BOOL WINAPI PathResolveAW(LPVOID path, LPCVOID *paths, DWORD flags)
511 {
512 if (SHELL_OsIsUnicode())
513 return PathResolveW(path, (LPCWSTR*)paths, flags);
514 else
515 return PathResolveA(path, (LPCSTR*)paths, flags);
516 }
517
518 /*************************************************************************
519 * PathProcessCommandA
520 */
521 static LONG PathProcessCommandA (
522 LPCSTR lpszPath,
523 LPSTR lpszBuff,
524 DWORD dwBuffSize,
525 DWORD dwFlags)
526 {
527 FIXME("%s %p 0x%04x 0x%04x stub\n",
528 lpszPath, lpszBuff, dwBuffSize, dwFlags);
529 if(!lpszPath) return -1;
530 if(lpszBuff) strcpy(lpszBuff, lpszPath);
531 return strlen(lpszPath);
532 }
533
534 /*************************************************************************
535 * PathProcessCommandW
536 */
537 static LONG PathProcessCommandW (
538 LPCWSTR lpszPath,
539 LPWSTR lpszBuff,
540 DWORD dwBuffSize,
541 DWORD dwFlags)
542 {
543 FIXME("(%s, %p, 0x%04x, 0x%04x) stub\n",
544 debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
545 if(!lpszPath) return -1;
546 if(lpszBuff) strcpyW(lpszBuff, lpszPath);
547 return strlenW(lpszPath);
548 }
549
550 /*************************************************************************
551 * PathProcessCommand (SHELL32.653)
552 */
553 LONG WINAPI PathProcessCommandAW (
554 LPCVOID lpszPath,
555 LPVOID lpszBuff,
556 DWORD dwBuffSize,
557 DWORD dwFlags)
558 {
559 if (SHELL_OsIsUnicode())
560 return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
561 return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
562 }
563
564 /*
565 ########## special ##########
566 */
567
568 static const WCHAR szCurrentVersion[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\0'};
569 static const WCHAR Administrative_ToolsW[] = {'A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
570 static const WCHAR AppDataW[] = {'A','p','p','D','a','t','a','\0'};
571 static const WCHAR AppData_LocalLowW[] = {'A','p','p','D','a','t','a','\\','L','o','c','a','l','L','o','w','\0'};
572 static const WCHAR Application_DataW[] = {'A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\0'};
573 static const WCHAR CacheW[] = {'C','a','c','h','e','\0'};
574 static const WCHAR CD_BurningW[] = {'C','D',' ','B','u','r','n','i','n','g','\0'};
575 static const WCHAR Common_Administrative_ToolsW[] = {'C','o','m','m','o','n',' ','A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
576 static const WCHAR Common_AppDataW[] = {'C','o','m','m','o','n',' ','A','p','p','D','a','t','a','\0'};
577 static const WCHAR Common_DesktopW[] = {'C','o','m','m','o','n',' ','D','e','s','k','t','o','p','\0'};
578 static const WCHAR Common_DocumentsW[] = {'C','o','m','m','o','n',' ','D','o','c','u','m','e','n','t','s','\0'};
579 static const WCHAR Common_FavoritesW[] = {'C','o','m','m','o','n',' ','F','a','v','o','r','i','t','e','s','\0'};
580 static const WCHAR CommonFilesDirW[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r','\0'};
581 static const WCHAR CommonFilesDirX86W[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',' ','(','x','8','6',')','\0'};
582 static const WCHAR CommonMusicW[] = {'C','o','m','m','o','n','M','u','s','i','c','\0'};
583 static const WCHAR CommonPicturesW[] = {'C','o','m','m','o','n','P','i','c','t','u','r','e','s','\0'};
584 static const WCHAR Common_ProgramsW[] = {'C','o','m','m','o','n',' ','P','r','o','g','r','a','m','s','\0'};
585 static const WCHAR Common_StartUpW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t','U','p','\0'};
586 static const WCHAR Common_Start_MenuW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t',' ','M','e','n','u','\0'};
587 static const WCHAR Common_TemplatesW[] = {'C','o','m','m','o','n',' ','T','e','m','p','l','a','t','e','s','\0'};
588 static const WCHAR CommonVideoW[] = {'C','o','m','m','o','n','V','i','d','e','o','\0'};
589 static const WCHAR ContactsW[] = {'C','o','n','t','a','c','t','s','\0'};
590 static const WCHAR CookiesW[] = {'C','o','o','k','i','e','s','\0'};
591 static const WCHAR DesktopW[] = {'D','e','s','k','t','o','p','\0'};
592 static const WCHAR DocumentsW[] = {'D','o','c','u','m','e','n','t','s','\0'};
593 static const WCHAR DownloadsW[] = {'D','o','w','n','l','o','a','d','s','\0'};
594 static const WCHAR FavoritesW[] = {'F','a','v','o','r','i','t','e','s','\0'};
595 static const WCHAR FontsW[] = {'F','o','n','t','s','\0'};
596 static const WCHAR HistoryW[] = {'H','i','s','t','o','r','y','\0'};
597 static const WCHAR LinksW[] = {'L','i','n','k','s','\0'};
598 static const WCHAR Local_AppDataW[] = {'L','o','c','a','l',' ','A','p','p','D','a','t','a','\0'};
599 static const WCHAR Local_Settings_Application_DataW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\','A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\0'};
600 static const WCHAR Local_Settings_CD_BurningW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\','A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\\','M','i','c','r','o','s','o','f','t','\\','C','D',' ','B','u','r','n','i','n','g','\0'};
601 static const WCHAR Local_Settings_HistoryW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\','H','i','s','t','o','r','y','\0'};
602 static const WCHAR Local_Settings_Temporary_Internet_FilesW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\','T','e','m','p','o','r','a','r','y',' ','I','n','t','e','r','n','e','t',' ','F','i','l','e','s','\0'};
603 static const WCHAR Microsoft_Windows_GameExplorerW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p','l','o','r','e','r','\0'};
604 static const WCHAR Microsoft_Windows_LibrariesW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','L','i','b','r','a','r','i','e','s','\0'};
605 static const WCHAR Microsoft_Windows_RingtonesW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','R','i','n','g','t','o','n','e','s','\0'};
606 static const WCHAR MusicW[] = {'M','u','s','i','c','\0'};
607 static const WCHAR Music_PlaylistsW[] = {'M','u','s','i','c','\\','P','l','a','y','l','i','s','t','s','\0'};
608 static const WCHAR Music_Sample_MusicW[] = {'M','u','s','i','c','\\','S','a','m','p','l','e',' ','M','u','s','i','c','\0'};
609 static const WCHAR Music_Sample_PlaylistsW[] = {'M','u','s','i','c','\\','S','a','m','p','l','e',' ','P','l','a','y','l','i','s','t','s','\0'};
610 static const WCHAR My_DocumentsW[] = {'M','y',' ','D','o','c','u','m','e','n','t','s','\0'};
611 static const WCHAR My_MusicW[] = {'M','y',' ','M','u','s','i','c','\0'};
612 static const WCHAR My_PicturesW[] = {'M','y',' ','P','i','c','t','u','r','e','s','\0'};
613 static const WCHAR My_VideoW[] = {'M','y',' ','V','i','d','e','o','\0'};
614 static const WCHAR NetHoodW[] = {'N','e','t','H','o','o','d','\0'};
615 static const WCHAR OEM_LinksW[] = {'O','E','M',' ','L','i','n','k','s','\0'};
616 static const WCHAR PersonalW[] = {'P','e','r','s','o','n','a','l','\0'};
617 static const WCHAR PicturesW[] = {'P','i','c','t','u','r','e','s','\0'};
618 static const WCHAR Pictures_Sample_PicturesW[] = {'P','i','c','t','u','r','e','s','\\','S','a','m','p','l','e',' ','P','i','c','t','u','r','e','s','\0'};
619 static const WCHAR Pictures_Slide_ShowsW[] = {'P','i','c','t','u','r','e','s','\\','S','l','i','d','e',' ','S','h','o','w','s','\0'};
620 static const WCHAR PrintHoodW[] = {'P','r','i','n','t','H','o','o','d','\0'};
621 static const WCHAR Program_FilesW[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s','\0'};
622 static const WCHAR Program_Files_Common_FilesW[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s','\\','C','o','m','m','o','n',' ','F','i','l','e','s','\0'};
623 static const WCHAR Program_Files_x86W[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s',' ','(','x','8','6',')','\0'};
624 static const WCHAR Program_Files_x86_Common_FilesW[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s',' ','(','x','8','6',')','\\','C','o','m','m','o','n',' ','F','i','l','e','s','\0'};
625 static const WCHAR ProgramFilesDirW[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r','\0'};
626 static const WCHAR ProgramFilesDirX86W[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',' ','(','x','8','6',')','\0'};
627 static const WCHAR ProgramsW[] = {'P','r','o','g','r','a','m','s','\0'};
628 static const WCHAR RecentW[] = {'R','e','c','e','n','t','\0'};
629 static const WCHAR ResourcesW[] = {'R','e','s','o','u','r','c','e','s','\0'};
630 static const WCHAR Saved_GamesW[] = {'S','a','v','e','d',' ','G','a','m','e','s','\0'};
631 static const WCHAR SearchesW[] = {'S','e','a','r','c','h','e','s','\0'};
632 static const WCHAR SendToW[] = {'S','e','n','d','T','o','\0'};
633 static const WCHAR StartUpW[] = {'S','t','a','r','t','U','p','\0'};
634 static const WCHAR Start_MenuW[] = {'S','t','a','r','t',' ','M','e','n','u','\0'};
635 static const WCHAR Start_Menu_ProgramsW[] = {'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\0'};
636 static const WCHAR Start_Menu_Admin_ToolsW[] = {'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\\','A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
637 static const WCHAR Start_Menu_StartupW[] = {'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\\','S','t','a','r','t','U','p','\0'};
638 static const WCHAR TemplatesW[] = {'T','e','m','p','l','a','t','e','s','\0'};
639 static const WCHAR UsersW[] = {'U','s','e','r','s','\0'};
640 static const WCHAR UsersPublicW[] = {'U','s','e','r','s','\\','P','u','b','l','i','c','\0'};
641 static const WCHAR VideosW[] = {'V','i','d','e','o','s','\0'};
642 static const WCHAR Videos_Sample_VideosW[] = {'V','i','d','e','o','s','\\','S','a','m','p','l','e',' ','V','i','d','e','o','s','\0'};
643 static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t','\0'};
644 static const WCHAR AllUsersProfileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%','\0'};
645 static const WCHAR UserProfileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%','\0'};
646 static const WCHAR SystemDriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%','\0'};
647 static const WCHAR ProfileListW[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','P','r','o','f','i','l','e','L','i','s','t',0};
648 static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
649 static const WCHAR AllUsersProfileValueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
650 static const WCHAR szSHFolders[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','S','h','e','l','l',' ','F','o','l','d','e','r','s','\0'};
651 static const WCHAR szSHUserFolders[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','U','s','e','r',' ','S','h','e','l','l',' ','F','o','l','d','e','r','s','\0'};
652 static const WCHAR szDefaultProfileDirW[] = {'u','s','e','r','s',0};
653 static const WCHAR szKnownFolderDescriptions[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','F','o','l','d','e','r','D','e','s','c','r','i','p','t','i','o','n','s','\0'};
654 static const WCHAR szKnownFolderRedirections[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','U','s','e','r',' ','S','h','e','l','l',' ','F','o','l','d','e','r','s',0};
655 static const WCHAR AllUsersW[] = {'P','u','b','l','i','c',0};
656
657 typedef enum _CSIDL_Type {
658 CSIDL_Type_User,
659 CSIDL_Type_AllUsers,
660 CSIDL_Type_CurrVer,
661 CSIDL_Type_Disallowed,
662 CSIDL_Type_NonExistent,
663 CSIDL_Type_WindowsPath,
664 CSIDL_Type_SystemPath,
665 CSIDL_Type_SystemX86Path,
666 } CSIDL_Type;
667
668 #if WIN32_WINNT >= 0x0600
669 #define CSIDL_CONTACTS 0x0043
670 #define CSIDL_DOWNLOADS 0x0047
671 #define CSIDL_LINKS 0x004d
672 #define CSIDL_APPDATA_LOCALLOW 0x004e
673 #define CSIDL_SAVED_GAMES 0x0062
674 #define CSIDL_SEARCHES 0x0063
675 #endif
676
677 typedef struct
678 {
679 const KNOWNFOLDERID *id;
680 CSIDL_Type type;
681 LPCWSTR szValueName;
682 LPCWSTR szDefaultPath; /* fallback string or resource ID */
683 } CSIDL_DATA;
684
685 static const CSIDL_DATA CSIDL_Data[] =
686 {
687 { /* 0x00 - CSIDL_DESKTOP */
688 &FOLDERID_Desktop,
689 CSIDL_Type_User,
690 DesktopW,
691 MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
692 },
693 { /* 0x01 - CSIDL_INTERNET */
694 &FOLDERID_InternetFolder,
695 CSIDL_Type_Disallowed,
696 NULL,
697 NULL
698 },
699 { /* 0x02 - CSIDL_PROGRAMS */
700 &FOLDERID_Programs,
701 CSIDL_Type_User,
702 ProgramsW,
703 MAKEINTRESOURCEW(IDS_PROGRAMS)
704 },
705 { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
706 &FOLDERID_ControlPanelFolder,
707 CSIDL_Type_SystemPath,
708 NULL,
709 NULL
710 },
711 { /* 0x04 - CSIDL_PRINTERS */
712 &FOLDERID_PrintersFolder,
713 CSIDL_Type_SystemPath,
714 NULL,
715 NULL
716 },
717 { /* 0x05 - CSIDL_PERSONAL */
718 &FOLDERID_Documents,
719 CSIDL_Type_User,
720 PersonalW,
721 MAKEINTRESOURCEW(IDS_PERSONAL)
722 },
723 { /* 0x06 - CSIDL_FAVORITES */
724 &FOLDERID_Favorites,
725 CSIDL_Type_User,
726 FavoritesW,
727 MAKEINTRESOURCEW(IDS_FAVORITES)
728 },
729 { /* 0x07 - CSIDL_STARTUP */
730 &FOLDERID_Startup,
731 CSIDL_Type_User,
732 StartUpW,
733 MAKEINTRESOURCEW(IDS_STARTUP)
734 },
735 { /* 0x08 - CSIDL_RECENT */
736 &FOLDERID_Recent,
737 CSIDL_Type_User,
738 RecentW,
739 MAKEINTRESOURCEW(IDS_RECENT)
740 },
741 { /* 0x09 - CSIDL_SENDTO */
742 &FOLDERID_SendTo,
743 CSIDL_Type_User,
744 SendToW,
745 MAKEINTRESOURCEW(IDS_SENDTO)
746 },
747 { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
748 &FOLDERID_RecycleBinFolder,
749 CSIDL_Type_Disallowed,
750 NULL,
751 NULL,
752 },
753 { /* 0x0b - CSIDL_STARTMENU */
754 &FOLDERID_StartMenu,
755 CSIDL_Type_User,
756 Start_MenuW,
757 MAKEINTRESOURCEW(IDS_STARTMENU)
758 },
759 { /* 0x0c - CSIDL_MYDOCUMENTS */
760 &GUID_NULL,
761 CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
762 NULL,
763 NULL
764 },
765 { /* 0x0d - CSIDL_MYMUSIC */
766 &FOLDERID_Music,
767 CSIDL_Type_User,
768 My_MusicW,
769 MAKEINTRESOURCEW(IDS_MYMUSIC)
770 },
771 { /* 0x0e - CSIDL_MYVIDEO */
772 &FOLDERID_Videos,
773 CSIDL_Type_User,
774 My_VideoW,
775 MAKEINTRESOURCEW(IDS_MYVIDEO)
776 },
777 { /* 0x0f - unassigned */
778 &GUID_NULL,
779 CSIDL_Type_Disallowed,
780 NULL,
781 NULL,
782 },
783 { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
784 &FOLDERID_Desktop,
785 CSIDL_Type_User,
786 DesktopW,
787 MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
788 },
789 { /* 0x11 - CSIDL_DRIVES */
790 &FOLDERID_ComputerFolder,
791 CSIDL_Type_Disallowed,
792 NULL,
793 NULL,
794 },
795 { /* 0x12 - CSIDL_NETWORK */
796 &FOLDERID_NetworkFolder,
797 CSIDL_Type_Disallowed,
798 NULL,
799 NULL,
800 },
801 { /* 0x13 - CSIDL_NETHOOD */
802 &FOLDERID_NetHood,
803 CSIDL_Type_User,
804 NetHoodW,
805 MAKEINTRESOURCEW(IDS_NETHOOD)
806 },
807 { /* 0x14 - CSIDL_FONTS */
808 &FOLDERID_Fonts,
809 CSIDL_Type_WindowsPath,
810 FontsW,
811 MAKEINTRESOURCEW(IDS_FONTS)
812 },
813 { /* 0x15 - CSIDL_TEMPLATES */
814 &FOLDERID_Templates,
815 CSIDL_Type_User,
816 TemplatesW,
817 MAKEINTRESOURCEW(IDS_TEMPLATES)
818 },
819 { /* 0x16 - CSIDL_COMMON_STARTMENU */
820 &FOLDERID_CommonStartMenu,
821 CSIDL_Type_AllUsers,
822 Common_Start_MenuW,
823 MAKEINTRESOURCEW(IDS_STARTMENU)
824 },
825 { /* 0x17 - CSIDL_COMMON_PROGRAMS */
826 &FOLDERID_CommonPrograms,
827 CSIDL_Type_AllUsers,
828 Common_ProgramsW,
829 MAKEINTRESOURCEW(IDS_PROGRAMS)
830 },
831 { /* 0x18 - CSIDL_COMMON_STARTUP */
832 &FOLDERID_CommonStartup,
833 CSIDL_Type_AllUsers,
834 Common_StartUpW,
835 MAKEINTRESOURCEW(IDS_STARTUP)
836 },
837 { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
838 &FOLDERID_PublicDesktop,
839 CSIDL_Type_AllUsers,
840 Common_DesktopW,
841 MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
842 },
843 { /* 0x1a - CSIDL_APPDATA */
844 &FOLDERID_RoamingAppData,
845 CSIDL_Type_User,
846 AppDataW,
847 MAKEINTRESOURCEW(IDS_APPDATA)
848 },
849 { /* 0x1b - CSIDL_PRINTHOOD */
850 &FOLDERID_PrintHood,
851 CSIDL_Type_User,
852 PrintHoodW,
853 MAKEINTRESOURCEW(IDS_PRINTHOOD)
854 },
855 { /* 0x1c - CSIDL_LOCAL_APPDATA */
856 &FOLDERID_LocalAppData,
857 CSIDL_Type_User,
858 Local_AppDataW,
859 MAKEINTRESOURCEW(IDS_LOCAL_APPDATA)
860 },
861 { /* 0x1d - CSIDL_ALTSTARTUP */
862 &GUID_NULL,
863 CSIDL_Type_NonExistent,
864 NULL,
865 NULL
866 },
867 { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
868 &GUID_NULL,
869 CSIDL_Type_NonExistent,
870 NULL,
871 NULL
872 },
873 { /* 0x1f - CSIDL_COMMON_FAVORITES */
874 &FOLDERID_Favorites,
875 CSIDL_Type_AllUsers,
876 Common_FavoritesW,
877 MAKEINTRESOURCEW(IDS_FAVORITES)
878 },
879 { /* 0x20 - CSIDL_INTERNET_CACHE */
880 &FOLDERID_InternetCache,
881 CSIDL_Type_User,
882 CacheW,
883 MAKEINTRESOURCEW(IDS_INTERNET_CACHE)
884 },
885 { /* 0x21 - CSIDL_COOKIES */
886 &FOLDERID_Cookies,
887 CSIDL_Type_User,
888 CookiesW,
889 MAKEINTRESOURCEW(IDS_COOKIES)
890 },
891 { /* 0x22 - CSIDL_HISTORY */
892 &FOLDERID_History,
893 CSIDL_Type_User,
894 HistoryW,
895 MAKEINTRESOURCEW(IDS_HISTORY)
896 },
897 { /* 0x23 - CSIDL_COMMON_APPDATA */
898 &FOLDERID_ProgramData,
899 CSIDL_Type_AllUsers,
900 Common_AppDataW,
901 MAKEINTRESOURCEW(IDS_APPDATA)
902 },
903 { /* 0x24 - CSIDL_WINDOWS */
904 &FOLDERID_Windows,
905 CSIDL_Type_WindowsPath,
906 NULL,
907 NULL
908 },
909 { /* 0x25 - CSIDL_SYSTEM */
910 &FOLDERID_System,
911 CSIDL_Type_SystemPath,
912 NULL,
913 NULL
914 },
915 { /* 0x26 - CSIDL_PROGRAM_FILES */
916 &FOLDERID_ProgramFiles,
917 CSIDL_Type_CurrVer,
918 ProgramFilesDirW,
919 MAKEINTRESOURCEW(IDS_PROGRAM_FILES)
920 },
921 { /* 0x27 - CSIDL_MYPICTURES */
922 &FOLDERID_Pictures,
923 CSIDL_Type_User,
924 My_PicturesW,
925 MAKEINTRESOURCEW(IDS_MYPICTURES)
926 },
927 { /* 0x28 - CSIDL_PROFILE */
928 &FOLDERID_Profile,
929 CSIDL_Type_User,
930 NULL,
931 NULL
932 },
933 { /* 0x29 - CSIDL_SYSTEMX86 */
934 &FOLDERID_SystemX86,
935 CSIDL_Type_SystemX86Path,
936 NULL,
937 NULL
938 },
939 { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
940 &FOLDERID_ProgramFilesX86,
941 CSIDL_Type_CurrVer,
942 ProgramFilesDirX86W,
943 Program_Files_x86W
944 },
945 { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
946 &FOLDERID_ProgramFilesCommon,
947 CSIDL_Type_CurrVer,
948 CommonFilesDirW,
949 MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON)
950 },
951 { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
952 &FOLDERID_ProgramFilesCommonX86,
953 CSIDL_Type_CurrVer,
954 CommonFilesDirX86W,
955 Program_Files_x86_Common_FilesW
956 },
957 { /* 0x2d - CSIDL_COMMON_TEMPLATES */
958 &FOLDERID_CommonTemplates,
959 CSIDL_Type_AllUsers,
960 Common_TemplatesW,
961 MAKEINTRESOURCEW(IDS_TEMPLATES)
962 },
963 { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
964 &FOLDERID_PublicDocuments,
965 CSIDL_Type_AllUsers,
966 Common_DocumentsW,
967 MAKEINTRESOURCEW(IDS_PERSONAL)
968 },
969 { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
970 &FOLDERID_CommonAdminTools,
971 CSIDL_Type_AllUsers,
972 Common_Administrative_ToolsW,
973 MAKEINTRESOURCEW(IDS_ADMINTOOLS)
974 },
975 { /* 0x30 - CSIDL_ADMINTOOLS */
976 &FOLDERID_AdminTools,
977 CSIDL_Type_User,
978 Administrative_ToolsW,
979 MAKEINTRESOURCEW(IDS_ADMINTOOLS)
980 },
981 { /* 0x31 - CSIDL_CONNECTIONS */
982 &FOLDERID_ConnectionsFolder,
983 CSIDL_Type_Disallowed,
984 NULL,
985 NULL
986 },
987 { /* 0x32 - unassigned */
988 &GUID_NULL,
989 CSIDL_Type_Disallowed,
990 NULL,
991 NULL
992 },
993 { /* 0x33 - unassigned */
994 &GUID_NULL,
995 CSIDL_Type_Disallowed,
996 NULL,
997 NULL
998 },
999 { /* 0x34 - unassigned */
1000 &GUID_NULL,
1001 CSIDL_Type_Disallowed,
1002 NULL,
1003 NULL
1004 },
1005 { /* 0x35 - CSIDL_COMMON_MUSIC */
1006 &FOLDERID_PublicMusic,
1007 CSIDL_Type_AllUsers,
1008 CommonMusicW,
1009 MAKEINTRESOURCEW(IDS_COMMON_MUSIC)
1010 },
1011 { /* 0x36 - CSIDL_COMMON_PICTURES */
1012 &FOLDERID_PublicPictures,
1013 CSIDL_Type_AllUsers,
1014 CommonPicturesW,
1015 MAKEINTRESOURCEW(IDS_COMMON_PICTURES)
1016 },
1017 { /* 0x37 - CSIDL_COMMON_VIDEO */
1018 &FOLDERID_PublicVideos,
1019 CSIDL_Type_AllUsers,
1020 CommonVideoW,
1021 MAKEINTRESOURCEW(IDS_COMMON_VIDEO)
1022 },
1023 { /* 0x38 - CSIDL_RESOURCES */
1024 &FOLDERID_ResourceDir,
1025 CSIDL_Type_WindowsPath,
1026 NULL,
1027 ResourcesW
1028 },
1029 { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1030 &FOLDERID_LocalizedResourcesDir,
1031 CSIDL_Type_NonExistent,
1032 NULL,
1033 NULL
1034 },
1035 { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1036 &FOLDERID_CommonOEMLinks,
1037 CSIDL_Type_AllUsers,
1038 NULL,
1039 OEM_LinksW
1040 },
1041 { /* 0x3b - CSIDL_CDBURN_AREA */
1042 &FOLDERID_CDBurning,
1043 CSIDL_Type_User,
1044 CD_BurningW,
1045 Local_Settings_CD_BurningW
1046 },
1047 { /* 0x3c unassigned */
1048 &GUID_NULL,
1049 CSIDL_Type_Disallowed,
1050 NULL,
1051 NULL
1052 },
1053 { /* 0x3d - CSIDL_COMPUTERSNEARME */
1054 &GUID_NULL,
1055 CSIDL_Type_Disallowed, /* FIXME */
1056 NULL,
1057 NULL
1058 },
1059 { /* 0x3e - CSIDL_PROFILES */
1060 &GUID_NULL,
1061 CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1062 NULL,
1063 NULL
1064 },
1065 #if WIN32_WINNT >= 0x0600
1066 { /* 0x3f */
1067 &FOLDERID_AddNewPrograms,
1068 CSIDL_Type_Disallowed,
1069 NULL,
1070 NULL
1071 },
1072 { /* 0x40 */
1073 &FOLDERID_AppUpdates,
1074 CSIDL_Type_Disallowed,
1075 NULL,
1076 NULL
1077 },
1078 { /* 0x41 */
1079 &FOLDERID_ChangeRemovePrograms,
1080 CSIDL_Type_Disallowed,
1081 NULL,
1082 NULL
1083 },
1084 { /* 0x42 */
1085 &FOLDERID_ConflictFolder,
1086 CSIDL_Type_Disallowed,
1087 NULL,
1088 NULL
1089 },
1090 { /* 0x43 - CSIDL_CONTACTS */
1091 &FOLDERID_Contacts,
1092 CSIDL_Type_User,
1093 NULL,
1094 ContactsW
1095 },
1096 { /* 0x44 */
1097 &FOLDERID_DeviceMetadataStore,
1098 CSIDL_Type_Disallowed, /* FIXME */
1099 NULL,
1100 NULL
1101 },
1102 { /* 0x45 */
1103 &GUID_NULL,
1104 CSIDL_Type_User,
1105 NULL,
1106 DocumentsW
1107 },
1108 { /* 0x46 */
1109 &FOLDERID_DocumentsLibrary,
1110 CSIDL_Type_Disallowed, /* FIXME */
1111 NULL,
1112 NULL
1113 },
1114 { /* 0x47 - CSIDL_DOWNLOADS */
1115 &FOLDERID_Downloads,
1116 CSIDL_Type_User,
1117 NULL,
1118 DownloadsW
1119 },
1120 { /* 0x48 */
1121 &FOLDERID_Games,
1122 CSIDL_Type_Disallowed,
1123 NULL,
1124 NULL
1125 },
1126 { /* 0x49 */
1127 &FOLDERID_GameTasks,
1128 CSIDL_Type_Disallowed, /* FIXME */
1129 NULL,
1130 NULL
1131 },
1132 { /* 0x4a */
1133 &FOLDERID_HomeGroup,
1134 CSIDL_Type_Disallowed,
1135 NULL,
1136 NULL
1137 },
1138 { /* 0x4b */
1139 &FOLDERID_ImplicitAppShortcuts,
1140 CSIDL_Type_Disallowed, /* FIXME */
1141 NULL,
1142 NULL
1143 },
1144 { /* 0x4c */
1145 &FOLDERID_Libraries,
1146 CSIDL_Type_Disallowed, /* FIXME */
1147 NULL,
1148 NULL
1149 },
1150 { /* 0x4d - CSIDL_LINKS */
1151 &FOLDERID_Links,
1152 CSIDL_Type_User,
1153 NULL,
1154 LinksW
1155 },
1156 { /* 0x4e - CSIDL_APPDATA_LOCALLOW */
1157 &FOLDERID_LocalAppDataLow,
1158 CSIDL_Type_User,
1159 NULL,
1160 AppData_LocalLowW
1161 },
1162 { /* 0x4f */
1163 &FOLDERID_MusicLibrary,
1164 CSIDL_Type_Disallowed, /* FIXME */
1165 NULL,
1166 NULL
1167 },
1168 { /* 0x50 */
1169 &FOLDERID_OriginalImages,
1170 CSIDL_Type_Disallowed, /* FIXME */
1171 NULL,
1172 NULL
1173 },
1174 { /* 0x51 */
1175 &FOLDERID_PhotoAlbums,
1176 CSIDL_Type_User,
1177 NULL,
1178 Pictures_Slide_ShowsW
1179 },
1180 { /* 0x52 */
1181 &FOLDERID_PicturesLibrary,
1182 CSIDL_Type_Disallowed, /* FIXME */
1183 NULL,
1184 NULL
1185 },
1186 { /* 0x53 */
1187 &FOLDERID_Playlists,
1188 CSIDL_Type_User,
1189 NULL,
1190 Music_PlaylistsW
1191 },
1192 { /* 0x54 */
1193 &FOLDERID_ProgramFilesX64,
1194 CSIDL_Type_NonExistent,
1195 NULL,
1196 NULL
1197 },
1198 { /* 0x55 */
1199 &FOLDERID_ProgramFilesCommonX64,
1200 CSIDL_Type_NonExistent,
1201 NULL,
1202 NULL
1203 },
1204 { /* 0x56 */
1205 &FOLDERID_Public,
1206 CSIDL_Type_CurrVer, /* FIXME */
1207 NULL,
1208 UsersPublicW
1209 },
1210 { /* 0x57 */
1211 &FOLDERID_PublicDownloads,
1212 CSIDL_Type_AllUsers,
1213 NULL,
1214 DownloadsW
1215 },
1216 { /* 0x58 */
1217 &FOLDERID_PublicGameTasks,
1218 CSIDL_Type_AllUsers,
1219 NULL,
1220 Microsoft_Windows_GameExplorerW
1221 },
1222 { /* 0x59 */
1223 &FOLDERID_PublicLibraries,
1224 CSIDL_Type_AllUsers,
1225 NULL,
1226 Microsoft_Windows_LibrariesW
1227 },
1228 { /* 0x5a */
1229 &FOLDERID_PublicRingtones,
1230 CSIDL_Type_AllUsers,
1231 NULL,
1232 Microsoft_Windows_RingtonesW
1233 },
1234 { /* 0x5b */
1235 &FOLDERID_QuickLaunch,
1236 CSIDL_Type_Disallowed, /* FIXME */
1237 NULL,
1238 NULL
1239 },
1240 { /* 0x5c */
1241 &FOLDERID_RecordedTVLibrary,
1242 CSIDL_Type_Disallowed, /* FIXME */
1243 NULL,
1244 NULL
1245 },
1246 { /* 0x5d */
1247 &FOLDERID_Ringtones,
1248 CSIDL_Type_Disallowed, /* FIXME */
1249 NULL,
1250 NULL
1251 },
1252 { /* 0x5e */
1253 &FOLDERID_SampleMusic,
1254 CSIDL_Type_AllUsers,
1255 NULL,
1256 Music_Sample_MusicW
1257 },
1258 { /* 0x5f */
1259 &FOLDERID_SamplePictures,
1260 CSIDL_Type_AllUsers,
1261 NULL,
1262 Pictures_Sample_PicturesW
1263 },
1264 { /* 0x60 */
1265 &FOLDERID_SamplePlaylists,
1266 CSIDL_Type_AllUsers,
1267 NULL,
1268 Music_Sample_PlaylistsW
1269 },
1270 { /* 0x61 */
1271 &FOLDERID_SampleVideos,
1272 CSIDL_Type_AllUsers,
1273 NULL,
1274 Videos_Sample_VideosW
1275 },
1276 { /* 0x62 - CSIDL_SAVED_GAMES */
1277 &FOLDERID_SavedGames,
1278 CSIDL_Type_User,
1279 NULL,
1280 Saved_GamesW
1281 },
1282 { /* 0x63 - CSIDL_SEARCHES */
1283 &FOLDERID_SavedSearches,
1284 CSIDL_Type_User,
1285 NULL,
1286 SearchesW
1287 },
1288 { /* 0x64 */
1289 &FOLDERID_SEARCH_CSC,
1290 CSIDL_Type_Disallowed,
1291 NULL,
1292 NULL
1293 },
1294 { /* 0x65 */
1295 &FOLDERID_SEARCH_MAPI,
1296 CSIDL_Type_Disallowed,
1297 NULL,
1298 NULL
1299 },
1300 { /* 0x66 */
1301 &FOLDERID_SearchHome,
1302 CSIDL_Type_Disallowed,
1303 NULL,
1304 NULL
1305 },
1306 { /* 0x67 */
1307 &FOLDERID_SidebarDefaultParts,
1308 CSIDL_Type_Disallowed, /* FIXME */
1309 NULL,
1310 NULL
1311 },
1312 { /* 0x68 */
1313 &FOLDERID_SidebarParts,
1314 CSIDL_Type_Disallowed, /* FIXME */
1315 NULL,
1316 NULL
1317 },
1318 { /* 0x69 */
1319 &FOLDERID_SyncManagerFolder,
1320 CSIDL_Type_Disallowed,
1321 NULL,
1322 NULL
1323 },
1324 { /* 0x6a */
1325 &FOLDERID_SyncResultsFolder,
1326 CSIDL_Type_Disallowed,
1327 NULL,
1328 NULL
1329 },
1330 { /* 0x6b */
1331 &FOLDERID_SyncSetupFolder,
1332 CSIDL_Type_Disallowed,
1333 NULL,
1334 NULL
1335 },
1336 { /* 0x6c */
1337 &FOLDERID_UserPinned,
1338 CSIDL_Type_Disallowed, /* FIXME */
1339 NULL,
1340 NULL
1341 },
1342 { /* 0x6d */
1343 &FOLDERID_UserProfiles,
1344 CSIDL_Type_CurrVer,
1345 UsersW,
1346 UsersW
1347 },
1348 { /* 0x6e */
1349 &FOLDERID_UserProgramFiles,
1350 CSIDL_Type_Disallowed, /* FIXME */
1351 NULL,
1352 NULL
1353 },
1354 { /* 0x6f */
1355 &FOLDERID_UserProgramFilesCommon,
1356 CSIDL_Type_Disallowed, /* FIXME */
1357 NULL,
1358 NULL
1359 },
1360 { /* 0x70 */
1361 &FOLDERID_UsersFiles,
1362 CSIDL_Type_Disallowed,
1363 NULL,
1364 NULL
1365 },
1366 { /* 0x71 */
1367 &FOLDERID_UsersLibraries,
1368 CSIDL_Type_Disallowed,
1369 NULL,
1370 NULL
1371 },
1372 { /* 0x72 */
1373 &FOLDERID_VideosLibrary,
1374 CSIDL_Type_Disallowed, /* FIXME */
1375 NULL,
1376 NULL
1377 }
1378 #endif
1379 };
1380
1381 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
1382
1383 /* Gets the value named value from the registry key
1384 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1385 * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
1386 * is assumed to be MAX_PATH WCHARs in length.
1387 * If it exists, expands the value and writes the expanded value to
1388 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
1389 * Returns successful error code if the value was retrieved from the registry,
1390 * and a failure otherwise.
1391 */
1392 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
1393 LPCWSTR value, LPWSTR path)
1394 {
1395 HRESULT hr;
1396 WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
1397 LPCWSTR pShellFolderPath, pUserShellFolderPath;
1398 DWORD dwType, dwPathLen = MAX_PATH;
1399 HKEY userShellFolderKey, shellFolderKey;
1400
1401 TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
1402 path);
1403
1404 if (userPrefix)
1405 {
1406 strcpyW(shellFolderPath, userPrefix);
1407 PathAddBackslashW(shellFolderPath);
1408 strcatW(shellFolderPath, szSHFolders);
1409 pShellFolderPath = shellFolderPath;
1410 strcpyW(userShellFolderPath, userPrefix);
1411 PathAddBackslashW(userShellFolderPath);
1412 strcatW(userShellFolderPath, szSHUserFolders);
1413 pUserShellFolderPath = userShellFolderPath;
1414 }
1415 else
1416 {
1417 pUserShellFolderPath = szSHUserFolders;
1418 pShellFolderPath = szSHFolders;
1419 }
1420
1421 if (RegCreateKeyW(rootKey, pShellFolderPath, &shellFolderKey))
1422 {
1423 TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
1424 return E_FAIL;
1425 }
1426 if (RegCreateKeyW(rootKey, pUserShellFolderPath, &userShellFolderKey))
1427 {
1428 TRACE("Failed to create %s\n",
1429 debugstr_w(pUserShellFolderPath));
1430 RegCloseKey(shellFolderKey);
1431 return E_FAIL;
1432 }
1433
1434 if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
1435 (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
1436 {
1437 LONG ret;
1438
1439 path[dwPathLen / sizeof(WCHAR)] = '\0';
1440 if (dwType == REG_EXPAND_SZ && path[0] == '%')
1441 {
1442 WCHAR szTemp[MAX_PATH];
1443
1444 _SHExpandEnvironmentStrings(path, szTemp);
1445 lstrcpynW(path, szTemp, MAX_PATH);
1446 }
1447 ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
1448 (strlenW(path) + 1) * sizeof(WCHAR));
1449 if (ret != ERROR_SUCCESS)
1450 hr = HRESULT_FROM_WIN32(ret);
1451 else
1452 hr = S_OK;
1453 }
1454 else
1455 hr = E_FAIL;
1456 RegCloseKey(shellFolderKey);
1457 RegCloseKey(userShellFolderKey);
1458 TRACE("returning 0x%08x\n", hr);
1459 return hr;
1460 }
1461
1462 BOOL _SHGetUserProfileDirectoryW(HANDLE hToken, LPWSTR szPath, LPDWORD lpcchPath)
1463 {
1464 BOOL result;
1465 if (!hToken)
1466 {
1467 OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
1468 result = GetUserProfileDirectoryW(hToken, szPath, lpcchPath);
1469 CloseHandle(hToken);
1470 }
1471 else if ((INT) hToken == -1)
1472 {
1473 result = GetDefaultUserProfileDirectoryW(szPath, lpcchPath);
1474 }
1475 else
1476 {
1477 result = GetUserProfileDirectoryW(hToken, szPath, lpcchPath);
1478 }
1479 DbgPrint("_SHGetUserProfileDirectoryW returning %S\n", szPath);
1480 return result;
1481 }
1482
1483 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
1484 * pszPath, based on the entries in CSIDL_Data. By semi-expanded, I mean:
1485 * - The entry's szDefaultPath may be either a string value or an integer
1486 * resource identifier. In the latter case, the string value of the resource
1487 * is written.
1488 * - Depending on the entry's type, the path may begin with an (unexpanded)
1489 * environment variable name. The caller is responsible for expanding
1490 * environment strings if so desired.
1491 * The types that are prepended with environment variables are:
1492 * CSIDL_Type_User: %USERPROFILE%
1493 * CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
1494 * CSIDL_Type_CurrVer: %SystemDrive%
1495 * (Others might make sense too, but as yet are unneeded.)
1496 */
1497 static HRESULT _SHGetDefaultValue(HANDLE hToken, BYTE folder, LPWSTR pszPath)
1498 {
1499 DWORD cchSize;
1500 HRESULT hr;
1501 WCHAR resourcePath[MAX_PATH];
1502
1503 TRACE("0x%02x,%p\n", folder, pszPath);
1504
1505 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1506 return E_INVALIDARG;
1507 if (!pszPath)
1508 return E_INVALIDARG;
1509
1510 switch (folder)
1511 {
1512 case CSIDL_PROGRAM_FILES:
1513 if (IsWow64())
1514 folder = CSIDL_PROGRAM_FILESX86;
1515 break;
1516 case CSIDL_PROGRAM_FILES_COMMON:
1517 if (IsWow64())
1518 folder = CSIDL_PROGRAM_FILESX86;
1519 break;
1520 }
1521
1522 switch (CSIDL_Data[folder].type)
1523 {
1524 case CSIDL_Type_User:
1525 cchSize = MAX_PATH;
1526 if (!_SHGetUserProfileDirectoryW(hToken, pszPath, &cchSize))
1527 return HRESULT_FROM_WIN32(GetLastError());
1528 break;
1529 case CSIDL_Type_AllUsers:
1530 cchSize = MAX_PATH;
1531 if (!GetAllUsersProfileDirectoryW(pszPath, &cchSize))
1532 return HRESULT_FROM_WIN32(GetLastError());
1533 break;
1534 case CSIDL_Type_CurrVer:
1535 strcpyW(pszPath, SystemDriveW);
1536 break;
1537 default:
1538 ; /* no corresponding env. var, do nothing */
1539 }
1540
1541 if (CSIDL_Data[folder].szDefaultPath)
1542 {
1543 if (IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
1544 {
1545 if (LoadStringW(shell32_hInstance,
1546 LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
1547 {
1548 hr = S_OK;
1549 PathAppendW(pszPath, resourcePath);
1550 }
1551 else
1552 {
1553 ERR("(%d,%s), LoadString failed, missing translation?\n", folder,
1554 debugstr_w(pszPath));
1555 hr = E_FAIL;
1556 }
1557 }
1558 else
1559 {
1560 hr = S_OK;
1561 PathAppendW(pszPath, CSIDL_Data[folder].szDefaultPath);
1562 }
1563 }
1564 TRACE("returning 0x%08x\n", hr);
1565 return hr;
1566 }
1567
1568 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
1569 * The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value
1570 * can be overridden in the HKLM\\szCurrentVersion key.
1571 * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
1572 * the registry, uses _SHGetDefaultValue to get the value.
1573 */
1574 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
1575 LPWSTR pszPath)
1576 {
1577 HRESULT hr;
1578
1579 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1580
1581 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1582 return E_INVALIDARG;
1583 if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
1584 return E_INVALIDARG;
1585 if (!pszPath)
1586 return E_INVALIDARG;
1587
1588 if (dwFlags & SHGFP_TYPE_DEFAULT)
1589 hr = _SHGetDefaultValue(NULL, folder, pszPath);
1590 else
1591 {
1592 HKEY hKey;
1593
1594 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, szCurrentVersion, &hKey))
1595 hr = E_FAIL;
1596 else
1597 {
1598 DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
1599
1600 if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
1601 &dwType, (LPBYTE)pszPath, &dwPathLen) ||
1602 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
1603 {
1604 hr = _SHGetDefaultValue(NULL, folder, pszPath);
1605 dwType = REG_EXPAND_SZ;
1606 switch (folder)
1607 {
1608 case CSIDL_PROGRAM_FILESX86:
1609 case CSIDL_PROGRAM_FILES_COMMONX86:
1610 /* these two should never be set on 32-bit setups */
1611 if (!is_win64)
1612 {
1613 BOOL is_wow64;
1614 IsWow64Process( GetCurrentProcess(), &is_wow64 );
1615 if (!is_wow64) break;
1616 }
1617 /* fall through */
1618 default:
1619 RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
1620 (LPBYTE)pszPath, (strlenW(pszPath)+1)*sizeof(WCHAR));
1621 }
1622 }
1623 else
1624 {
1625 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
1626 hr = S_OK;
1627 }
1628 RegCloseKey(hKey);
1629 }
1630 }
1631 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1632 return hr;
1633 }
1634
1635 static LPWSTR _GetUserSidStringFromToken(HANDLE Token)
1636 {
1637 char InfoBuffer[64];
1638 PTOKEN_USER UserInfo;
1639 DWORD InfoSize;
1640 LPWSTR SidStr;
1641
1642 UserInfo = (PTOKEN_USER) InfoBuffer;
1643 if (! GetTokenInformation(Token, TokenUser, InfoBuffer, sizeof(InfoBuffer),
1644 &InfoSize))
1645 {
1646 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1647 return NULL;
1648 UserInfo = HeapAlloc(GetProcessHeap(), 0, InfoSize);
1649 if (UserInfo == NULL)
1650 return NULL;
1651 if (! GetTokenInformation(Token, TokenUser, UserInfo, InfoSize,
1652 &InfoSize))
1653 {
1654 HeapFree(GetProcessHeap(), 0, UserInfo);
1655 return NULL;
1656 }
1657 }
1658
1659 if (! ConvertSidToStringSidW(UserInfo->User.Sid, &SidStr))
1660 SidStr = NULL;
1661
1662 if (UserInfo != (PTOKEN_USER) InfoBuffer)
1663 HeapFree(GetProcessHeap(), 0, UserInfo);
1664
1665 return SidStr;
1666 }
1667
1668 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
1669 * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise
1670 * calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken:
1671 * - if hToken is -1, looks in HKEY_USERS\.Default
1672 * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
1673 * if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally
1674 * calls _SHGetDefaultValue for it.
1675 */
1676 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
1677 LPWSTR pszPath)
1678 {
1679 const WCHAR *szValueName;
1680 WCHAR buffer[40];
1681 HRESULT hr;
1682
1683 TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
1684
1685 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1686 return E_INVALIDARG;
1687 if (CSIDL_Data[folder].type != CSIDL_Type_User)
1688 return E_INVALIDARG;
1689 if (!pszPath)
1690 return E_INVALIDARG;
1691
1692 if (dwFlags & SHGFP_TYPE_DEFAULT)
1693 {
1694 hr = _SHGetDefaultValue(hToken, folder, pszPath);
1695 }
1696 else
1697 {
1698 LPCWSTR userPrefix = NULL;
1699 HKEY hRootKey;
1700
1701 if (hToken == (HANDLE)-1)
1702 {
1703 hRootKey = HKEY_USERS;
1704 userPrefix = DefaultW;
1705 }
1706 else if (hToken == NULL)
1707 hRootKey = HKEY_CURRENT_USER;
1708 else
1709 {
1710 hRootKey = HKEY_USERS;
1711 userPrefix = _GetUserSidStringFromToken(hToken);
1712 if (userPrefix == NULL)
1713 {
1714 hr = E_FAIL;
1715 goto error;
1716 }
1717 }
1718
1719 /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
1720 szValueName = CSIDL_Data[folder].szValueName;
1721 if (!szValueName)
1722 {
1723 StringFromGUID2( CSIDL_Data[folder].id, buffer, 39 );
1724 szValueName = &buffer[0];
1725 }
1726
1727 hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, szValueName, pszPath);
1728 if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
1729 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, szValueName, pszPath);
1730 if (FAILED(hr))
1731 hr = _SHGetDefaultValue(hToken, folder, pszPath);
1732 if (userPrefix != NULL && userPrefix != DefaultW)
1733 LocalFree((HLOCAL) userPrefix);
1734 }
1735 error:
1736 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1737 return hr;
1738 }
1739
1740 /* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has
1741 * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls
1742 * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
1743 * If this fails, falls back to _SHGetDefaultValue.
1744 */
1745 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
1746 LPWSTR pszPath)
1747 {
1748 HRESULT hr;
1749
1750 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1751
1752 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1753 return E_INVALIDARG;
1754 if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
1755 return E_INVALIDARG;
1756 if (!pszPath)
1757 return E_INVALIDARG;
1758
1759 if (dwFlags & SHGFP_TYPE_DEFAULT)
1760 hr = _SHGetDefaultValue(NULL, folder, pszPath);
1761 else
1762 {
1763 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1764 CSIDL_Data[folder].szValueName, pszPath);
1765 if (FAILED(hr))
1766 hr = _SHGetDefaultValue(NULL, folder, pszPath);
1767 }
1768 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1769 return hr;
1770 }
1771
1772 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
1773 {
1774 LONG lRet;
1775 DWORD disp;
1776
1777 lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, NULL, 0,
1778 KEY_ALL_ACCESS, NULL, pKey, &disp);
1779 return HRESULT_FROM_WIN32(lRet);
1780 }
1781
1782 /* Reads the value named szValueName from the key profilesKey (assumed to be
1783 * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
1784 * WCHARs in length. If it doesn't exist, returns szDefault (and saves
1785 * szDefault to the registry).
1786 */
1787 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
1788 LPWSTR szValue, LPCWSTR szDefault)
1789 {
1790 HRESULT hr;
1791 DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
1792 LONG lRet;
1793
1794 TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
1795 debugstr_w(szDefault));
1796 lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
1797 (LPBYTE)szValue, &dwPathLen);
1798 if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
1799 && *szValue)
1800 {
1801 dwPathLen /= sizeof(WCHAR);
1802 szValue[dwPathLen] = '\0';
1803 hr = S_OK;
1804 }
1805 else
1806 {
1807 /* Missing or invalid value, set a default */
1808 lstrcpynW(szValue, szDefault, MAX_PATH);
1809 TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
1810 debugstr_w(szValue));
1811 lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
1812 (LPBYTE)szValue,
1813 (strlenW(szValue) + 1) * sizeof(WCHAR));
1814 if (lRet)
1815 hr = HRESULT_FROM_WIN32(lRet);
1816 else
1817 hr = S_OK;
1818 }
1819 TRACE("returning 0x%08x (output value is %s)\n", hr, debugstr_w(szValue));
1820 return hr;
1821 }
1822
1823 /* Attempts to expand environment variables from szSrc into szDest, which is
1824 * assumed to be MAX_PATH characters in length. Before referring to the
1825 * environment, handles a few variables directly, because the environment
1826 * variables may not be set when this is called (as during Wine's installation
1827 * when default values are being written to the registry).
1828 * The directly handled environment variables, and their source, are:
1829 * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
1830 * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
1831 * path
1832 * If one of the directly handled environment variables is expanded, only
1833 * expands a single variable, and only in the beginning of szSrc.
1834 */
1835 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
1836 {
1837 HRESULT hr;
1838 WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
1839 HKEY key = NULL;
1840
1841 TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
1842
1843 if (!szSrc || !szDest) return E_INVALIDARG;
1844
1845 /* short-circuit if there's nothing to expand */
1846 if (szSrc[0] != '%')
1847 {
1848 strcpyW(szDest, szSrc);
1849 hr = S_OK;
1850 goto end;
1851 }
1852 /* Get the profile prefix, we'll probably be needing it */
1853 hr = _SHOpenProfilesKey(&key);
1854 if (SUCCEEDED(hr))
1855 {
1856 WCHAR def_val[MAX_PATH];
1857
1858 /* get the system drive */
1859 GetSystemDirectoryW(def_val, MAX_PATH);
1860 if (def_val[1] == ':') strcpyW( def_val + 3, szDefaultProfileDirW );
1861 else FIXME("non-drive system paths unsupported\n");
1862
1863 hr = _SHGetProfilesValue(key, ProfilesDirectoryW, szProfilesPrefix, def_val );
1864 }
1865
1866 *szDest = 0;
1867 strcpyW(szTemp, szSrc);
1868 while (SUCCEEDED(hr) && szTemp[0] == '%')
1869 {
1870 if (!strncmpiW(szTemp, AllUsersProfileW, strlenW(AllUsersProfileW)))
1871 {
1872 WCHAR szAllUsers[MAX_PATH];
1873
1874 strcpyW(szDest, szProfilesPrefix);
1875 hr = _SHGetProfilesValue(key, AllUsersProfileValueW,
1876 szAllUsers, AllUsersW);
1877 PathAppendW(szDest, szAllUsers);
1878 PathAppendW(szDest, szTemp + strlenW(AllUsersProfileW));
1879 }
1880 else if (!strncmpiW(szTemp, UserProfileW, strlenW(UserProfileW)))
1881 {
1882 WCHAR userName[MAX_PATH];
1883 DWORD userLen = MAX_PATH;
1884
1885 strcpyW(szDest, szProfilesPrefix);
1886 GetUserNameW(userName, &userLen);
1887 PathAppendW(szDest, userName);
1888 PathAppendW(szDest, szTemp + strlenW(UserProfileW));
1889 }
1890 else if (!strncmpiW(szTemp, SystemDriveW, strlenW(SystemDriveW)))
1891 {
1892 GetSystemDirectoryW(szDest, MAX_PATH);
1893 if (szDest[1] != ':')
1894 {
1895 FIXME("non-drive system paths unsupported\n");
1896 hr = E_FAIL;
1897 }
1898 else
1899 {
1900 strcpyW(szDest + 3, szTemp + strlenW(SystemDriveW) + 1);
1901 hr = S_OK;
1902 }
1903 }
1904 else
1905 {
1906 DWORD ret = ExpandEnvironmentStringsW(szSrc, szDest, MAX_PATH);
1907
1908 if (ret > MAX_PATH)
1909 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1910 else if (ret == 0)
1911 hr = HRESULT_FROM_WIN32(GetLastError());
1912 else
1913 hr = S_OK;
1914 }
1915 if (SUCCEEDED(hr) && szDest[0] == '%')
1916 strcpyW(szTemp, szDest);
1917 else
1918 {
1919 /* terminate loop */
1920 szTemp[0] = '\0';
1921 }
1922 }
1923 end:
1924 if (key)
1925 RegCloseKey(key);
1926 TRACE("returning 0x%08x (input was %s, output is %s)\n", hr,
1927 debugstr_w(szSrc), debugstr_w(szDest));
1928 return hr;
1929 }
1930
1931 /*************************************************************************
1932 * SHGetFolderPathW [SHELL32.@]
1933 *
1934 * Convert nFolder to path.
1935 *
1936 * RETURNS
1937 * Success: S_OK
1938 * Failure: standard HRESULT error codes.
1939 *
1940 * NOTES
1941 * Most values can be overridden in either
1942 * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1943 * or in the same location in HKLM.
1944 * The "Shell Folders" registry key was used in NT4 and earlier systems.
1945 * Beginning with Windows 2000, the "User Shell Folders" key is used, so
1946 * changes made to it are made to the former key too. This synchronization is
1947 * done on-demand: not until someone requests the value of one of these paths
1948 * (by calling one of the SHGet functions) is the value synchronized.
1949 * Furthermore, the HKCU paths take precedence over the HKLM paths.
1950 */
1951 HRESULT WINAPI SHGetFolderPathW(
1952 HWND hwndOwner, /* [I] owner window */
1953 int nFolder, /* [I] CSIDL identifying the folder */
1954 HANDLE hToken, /* [I] access token */
1955 DWORD dwFlags, /* [I] which path to return */
1956 LPWSTR pszPath) /* [O] converted path */
1957 {
1958 HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
1959 if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
1960 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1961 return hr;
1962 }
1963
1964 HRESULT WINAPI SHGetFolderPathAndSubDirA(
1965 HWND hwndOwner, /* [I] owner window */
1966 int nFolder, /* [I] CSIDL identifying the folder */
1967 HANDLE hToken, /* [I] access token */
1968 DWORD dwFlags, /* [I] which path to return */
1969 LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
1970 LPSTR pszPath) /* [O] converted path */
1971 {
1972 int length;
1973 HRESULT hr = S_OK;
1974 LPWSTR pszSubPathW = NULL;
1975 LPWSTR pszPathW = NULL;
1976 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1977
1978 if(pszPath) {
1979 pszPathW = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
1980 if(!pszPathW) {
1981 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1982 goto cleanup;
1983 }
1984 }
1985 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1986
1987 /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
1988 * set (null), or an empty string.therefore call it without the parameter set
1989 * if pszSubPath is an empty string
1990 */
1991 if (pszSubPath && pszSubPath[0]) {
1992 length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
1993 pszSubPathW = HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR));
1994 if(!pszSubPathW) {
1995 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1996 goto cleanup;
1997 }
1998 MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
1999 }
2000
2001 hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
2002
2003 if (SUCCEEDED(hr) && pszPath)
2004 WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
2005
2006 cleanup:
2007 HeapFree(GetProcessHeap(), 0, pszPathW);
2008 HeapFree(GetProcessHeap(), 0, pszSubPathW);
2009 return hr;
2010 }
2011
2012 /*************************************************************************
2013 * SHGetFolderPathAndSubDirW [SHELL32.@]
2014 */
2015 HRESULT WINAPI SHGetFolderPathAndSubDirW(
2016 HWND hwndOwner, /* [I] owner window */
2017 int nFolder, /* [I] CSIDL identifying the folder */
2018 HANDLE hToken, /* [I] access token */
2019 DWORD dwFlags, /* [I] which path to return */
2020 LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
2021 LPWSTR pszPath) /* [O] converted path */
2022 {
2023 HRESULT hr;
2024 WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
2025 DWORD folder = nFolder & CSIDL_FOLDER_MASK;
2026 CSIDL_Type type;
2027 int ret;
2028
2029 TRACE("%p,%p,nFolder=0x%04x,%s\n", hwndOwner,pszPath,nFolder,debugstr_w(pszSubPath));
2030
2031 /* Windows always NULL-terminates the resulting path regardless of success
2032 * or failure, so do so first
2033 */
2034 if (pszPath)
2035 *pszPath = '\0';
2036
2037 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
2038 return E_INVALIDARG;
2039 if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
2040 return E_INVALIDARG;
2041 szTemp[0] = 0;
2042 type = CSIDL_Data[folder].type;
2043 switch (type)
2044 {
2045 case CSIDL_Type_Disallowed:
2046 hr = E_INVALIDARG;
2047 break;
2048 case CSIDL_Type_NonExistent:
2049 hr = S_FALSE;
2050 break;
2051 case CSIDL_Type_WindowsPath:
2052 GetWindowsDirectoryW(szTemp, MAX_PATH);
2053 if (CSIDL_Data[folder].szDefaultPath &&
2054 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2055 *CSIDL_Data[folder].szDefaultPath)
2056 {
2057 PathAddBackslashW(szTemp);
2058 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2059 }
2060 hr = S_OK;
2061 break;
2062 case CSIDL_Type_SystemPath:
2063 GetSystemDirectoryW(szTemp, MAX_PATH);
2064 if (CSIDL_Data[folder].szDefaultPath &&
2065 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2066 *CSIDL_Data[folder].szDefaultPath)
2067 {
2068 PathAddBackslashW(szTemp);
2069 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2070 }
2071 hr = S_OK;
2072 break;
2073 case CSIDL_Type_SystemX86Path:
2074 if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
2075 if (CSIDL_Data[folder].szDefaultPath &&
2076 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2077 *CSIDL_Data[folder].szDefaultPath)
2078 {
2079 PathAddBackslashW(szTemp);
2080 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2081 }
2082 hr = S_OK;
2083 break;
2084 case CSIDL_Type_CurrVer:
2085 hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
2086 break;
2087 case CSIDL_Type_User:
2088 hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
2089 break;
2090 case CSIDL_Type_AllUsers:
2091 hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
2092 break;
2093 default:
2094 FIXME("bogus type %d, please fix\n", type);
2095 hr = E_INVALIDARG;
2096 break;
2097 }
2098
2099 /* Expand environment strings if necessary */
2100 if (*szTemp == '%')
2101 hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
2102 else
2103 strcpyW(szBuildPath, szTemp);
2104
2105 if (FAILED(hr)) goto end;
2106
2107 if(pszSubPath) {
2108 /* make sure the new path does not exceed the buffer length
2109 * and remember to backslash and terminate it */
2110 if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
2111 hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
2112 goto end;
2113 }
2114 PathAppendW(szBuildPath, pszSubPath);
2115 PathRemoveBackslashW(szBuildPath);
2116 }
2117 /* Copy the path if it's available before we might return */
2118 if (SUCCEEDED(hr) && pszPath)
2119 strcpyW(pszPath, szBuildPath);
2120
2121 /* if we don't care about existing directories we are ready */
2122 if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
2123
2124 if (PathFileExistsW(szBuildPath)) goto end;
2125
2126 /* not existing but we are not allowed to create it. The return value
2127 * is verified against shell32 version 6.0.
2128 */
2129 if (!(nFolder & CSIDL_FLAG_CREATE))
2130 {
2131 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
2132 goto end;
2133 }
2134
2135 /* create directory/directories */
2136 ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
2137 if (ret && ret != ERROR_ALREADY_EXISTS)
2138 {
2139 ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
2140 hr = E_FAIL;
2141 goto end;
2142 }
2143
2144 TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
2145 end:
2146 TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
2147 return hr;
2148 }
2149
2150 /*************************************************************************
2151 * SHGetFolderPathA [SHELL32.@]
2152 *
2153 * See SHGetFolderPathW.
2154 */
2155 HRESULT WINAPI SHGetFolderPathA(
2156 HWND hwndOwner,
2157 int nFolder,
2158 HANDLE hToken,
2159 DWORD dwFlags,
2160 LPSTR pszPath)
2161 {
2162 WCHAR szTemp[MAX_PATH];
2163 HRESULT hr;
2164
2165 TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
2166
2167 if (pszPath)
2168 *pszPath = '\0';
2169 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
2170 if (SUCCEEDED(hr) && pszPath)
2171 WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
2172 NULL);
2173
2174 return hr;
2175 }
2176
2177 /* For each folder in folders, if its value has not been set in the registry,
2178 * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
2179 * folder's type) to get the unexpanded value first.
2180 * Writes the unexpanded value to User Shell Folders, and queries it with
2181 * SHGetFolderPathW to force the creation of the directory if it doesn't
2182 * already exist. SHGetFolderPathW also returns the expanded value, which
2183 * this then writes to Shell Folders.
2184 */
2185 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
2186 LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
2187 UINT foldersLen)
2188 {
2189 const WCHAR *szValueName;
2190 WCHAR buffer[40];
2191 UINT i;
2192 WCHAR path[MAX_PATH];
2193 HRESULT hr = S_OK;
2194 HKEY hUserKey = NULL, hKey = NULL;
2195 DWORD dwType, dwPathLen;
2196 LONG ret;
2197
2198 TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
2199 debugstr_w(szUserShellFolderPath), folders, foldersLen);
2200
2201 ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
2202 if (ret)
2203 hr = HRESULT_FROM_WIN32(ret);
2204 else
2205 {
2206 ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
2207 if (ret)
2208 hr = HRESULT_FROM_WIN32(ret);
2209 }
2210 for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
2211 {
2212 dwPathLen = MAX_PATH * sizeof(WCHAR);
2213
2214 /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
2215 szValueName = CSIDL_Data[folders[i]].szValueName;
2216 if (!szValueName && CSIDL_Data[folders[i]].type == CSIDL_Type_User)
2217 {
2218 StringFromGUID2( CSIDL_Data[folders[i]].id, buffer, 39 );
2219 szValueName = &buffer[0];
2220 }
2221
2222 if (RegQueryValueExW(hUserKey, szValueName, NULL,
2223 &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
2224 dwType != REG_EXPAND_SZ))
2225 {
2226 *path = '\0';
2227 if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
2228 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
2229 path);
2230 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
2231 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
2232 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
2233 {
2234 GetWindowsDirectoryW(path, MAX_PATH);
2235 if (CSIDL_Data[folders[i]].szDefaultPath &&
2236 !IS_INTRESOURCE(CSIDL_Data[folders[i]].szDefaultPath))
2237 {
2238 PathAddBackslashW(path);
2239 strcatW(path, CSIDL_Data[folders[i]].szDefaultPath);
2240 }
2241 }
2242 else
2243 hr = E_FAIL;
2244 if (*path)
2245 {
2246 ret = RegSetValueExW(hUserKey, szValueName, 0, REG_EXPAND_SZ,
2247 (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2248 if (ret)
2249 hr = HRESULT_FROM_WIN32(ret);
2250 else
2251 {
2252 hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
2253 hToken, SHGFP_TYPE_DEFAULT, path);
2254 ret = RegSetValueExW(hKey, szValueName, 0, REG_SZ,
2255 (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2256 if (ret)
2257 hr = HRESULT_FROM_WIN32(ret);
2258 }
2259 }
2260 }
2261 }
2262 if (hUserKey)
2263 RegCloseKey(hUserKey);
2264 if (hKey)
2265 RegCloseKey(hKey);
2266
2267 TRACE("returning 0x%08x\n", hr);
2268 return hr;
2269 }
2270
2271 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
2272 {
2273 static const UINT folders[] = {
2274 CSIDL_PROGRAMS,
2275 CSIDL_PERSONAL,
2276 CSIDL_FAVORITES,
2277 CSIDL_APPDATA,
2278 CSIDL_STARTUP,
2279 CSIDL_RECENT,
2280 CSIDL_SENDTO,
2281 CSIDL_STARTMENU,
2282 CSIDL_MYMUSIC,
2283 CSIDL_MYVIDEO,
2284 CSIDL_DESKTOPDIRECTORY,
2285 CSIDL_NETHOOD,
2286 CSIDL_TEMPLATES,
2287 CSIDL_PRINTHOOD,
2288 CSIDL_LOCAL_APPDATA,
2289 CSIDL_INTERNET_CACHE,
2290 CSIDL_COOKIES,
2291 CSIDL_HISTORY,
2292 CSIDL_MYPICTURES,
2293 CSIDL_FONTS,
2294 CSIDL_ADMINTOOLS,
2295 #if WIN32_WINNT >= 0x0600
2296 CSIDL_CONTACTS,
2297 CSIDL_DOWNLOADS,
2298 CSIDL_LINKS,
2299 CSIDL_APPDATA_LOCALLOW,
2300 CSIDL_SAVED_GAMES,
2301 CSIDL_SEARCHES
2302 #endif
2303 };
2304 WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
2305 LPCWSTR pUserShellFolderPath, pShellFolderPath;
2306 HRESULT hr = S_OK;
2307 HKEY hRootKey;
2308 HANDLE hToken;
2309
2310 TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
2311 if (bDefault)
2312 {
2313 hToken = (HANDLE)-1;
2314 hRootKey = HKEY_USERS;
2315 strcpyW(userShellFolderPath, DefaultW);
2316 PathAddBackslashW(userShellFolderPath);
2317 strcatW(userShellFolderPath, szSHUserFolders);
2318 pUserShellFolderPath = userShellFolderPath;
2319 strcpyW(shellFolderPath, DefaultW);
2320 PathAddBackslashW(shellFolderPath);
2321 strcatW(shellFolderPath, szSHFolders);
2322 pShellFolderPath = shellFolderPath;
2323 }
2324 else
2325 {
2326 hToken = NULL;
2327 hRootKey = HKEY_CURRENT_USER;
2328 pUserShellFolderPath = szSHUserFolders;
2329 pShellFolderPath = szSHFolders;
2330 }
2331
2332 hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
2333 pShellFolderPath, folders, sizeof(folders) / sizeof(folders[0]));
2334 TRACE("returning 0x%08x\n", hr);
2335 return hr;
2336 }
2337
2338 static HRESULT _SHRegisterCommonShellFolders(void)
2339 {
2340 static const UINT folders[] = {
2341 CSIDL_COMMON_STARTMENU,
2342 CSIDL_COMMON_PROGRAMS,
2343 CSIDL_COMMON_STARTUP,
2344 CSIDL_COMMON_DESKTOPDIRECTORY,
2345 CSIDL_COMMON_FAVORITES,
2346 CSIDL_COMMON_APPDATA,
2347 CSIDL_COMMON_TEMPLATES,
2348 CSIDL_COMMON_DOCUMENTS,
2349 CSIDL_COMMON_ADMINTOOLS,
2350 CSIDL_COMMON_MUSIC,
2351 CSIDL_COMMON_PICTURES,
2352 CSIDL_COMMON_VIDEO,
2353 };
2354 HRESULT hr;
2355
2356 TRACE("\n");
2357 hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
2358 szSHFolders, folders, sizeof(folders) / sizeof(folders[0]));
2359 TRACE("returning 0x%08x\n", hr);
2360 return hr;
2361 }
2362
2363 /* Register the default values in the registry, as some apps seem to depend
2364 * on their presence. The set registered was taken from Windows XP.
2365 */
2366 HRESULT SHELL_RegisterShellFolders(void)
2367 {
2368 HRESULT hr;
2369
2370 hr = _SHRegisterUserShellFolders(TRUE);
2371 if (SUCCEEDED(hr))
2372 hr = _SHRegisterUserShellFolders(FALSE);
2373 if (SUCCEEDED(hr))
2374 hr = _SHRegisterCommonShellFolders();
2375 return hr;
2376 }
2377
2378 /*************************************************************************
2379 * SHGetSpecialFolderPathA [SHELL32.@]
2380 */
2381 BOOL WINAPI SHGetSpecialFolderPathA (
2382 HWND hwndOwner,
2383 LPSTR szPath,
2384 int nFolder,
2385 BOOL bCreate)
2386 {
2387 return SHGetFolderPathA(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
2388 szPath) == S_OK;
2389 }
2390
2391 /*************************************************************************
2392 * SHGetSpecialFolderPathW
2393 */
2394 BOOL WINAPI SHGetSpecialFolderPathW (
2395 HWND hwndOwner,
2396 LPWSTR szPath,
2397 int nFolder,
2398 BOOL bCreate)
2399 {
2400 return SHGetFolderPathW(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
2401 szPath) == S_OK;
2402 }
2403
2404 /*************************************************************************
2405 * SHGetFolderLocation [SHELL32.@]
2406 *
2407 * Gets the folder locations from the registry and creates a pidl.
2408 *
2409 * PARAMS
2410 * hwndOwner [I]
2411 * nFolder [I] CSIDL_xxxxx
2412 * hToken [I] token representing user, or NULL for current user, or -1 for
2413 * default user
2414 * dwReserved [I] must be zero
2415 * ppidl [O] PIDL of a special folder
2416 *
2417 * RETURNS
2418 * Success: S_OK
2419 * Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
2420 *
2421 * NOTES
2422 * Creates missing reg keys and directories.
2423 * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
2424 * virtual folders that are handled here.
2425 */
2426 HRESULT WINAPI SHGetFolderLocation(
2427 HWND hwndOwner,
2428 int nFolder,
2429 HANDLE hToken,
2430 DWORD dwReserved,
2431 LPITEMIDLIST *ppidl)
2432 {
2433 HRESULT hr = E_INVALIDARG;
2434
2435 TRACE("%p 0x%08x %p 0x%08x %p\n",
2436 hwndOwner, nFolder, hToken, dwReserved, ppidl);
2437
2438 if (!ppidl)
2439 return E_INVALIDARG;
2440 if (dwReserved)
2441 return E_INVALIDARG;
2442
2443 /* The virtual folders' locations are not user-dependent */
2444 *ppidl = NULL;
2445 switch (nFolder & CSIDL_FOLDER_MASK)
2446 {
2447 case CSIDL_DESKTOP:
2448 *ppidl = _ILCreateDesktop();
2449 break;
2450
2451 case CSIDL_PERSONAL:
2452 *ppidl = _ILCreateMyDocuments();
2453 break;
2454
2455 case CSIDL_INTERNET:
2456 *ppidl = _ILCreateIExplore();
2457 break;
2458
2459 case CSIDL_CONTROLS:
2460 *ppidl = _ILCreateControlPanel();
2461 break;
2462
2463 case CSIDL_PRINTERS:
2464 *ppidl = _ILCreatePrinters();
2465 break;
2466
2467 case CSIDL_BITBUCKET:
2468 *ppidl = _ILCreateBitBucket();
2469 break;
2470
2471 case CSIDL_DRIVES:
2472 *ppidl = _ILCreateMyComputer();
2473 break;
2474
2475 case CSIDL_NETWORK:
2476 *ppidl = _ILCreateNetwork();
2477 break;
2478
2479 default:
2480 {
2481 WCHAR szPath[MAX_PATH];
2482
2483 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
2484 SHGFP_TYPE_CURRENT, szPath);
2485 if (SUCCEEDED(hr))
2486 {
2487 DWORD attributes=0;
2488
2489 TRACE("Value=%s\n", debugstr_w(szPath));
2490 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
2491 }
2492 else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
2493 {
2494 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
2495 * version 6.0 returns E_FAIL for nonexistent paths
2496 */
2497 hr = E_FAIL;
2498 }
2499 }
2500 }
2501 if(*ppidl)
2502 hr = S_OK;
2503
2504 TRACE("-- (new pidl %p)\n",*ppidl);
2505 return hr;
2506 }
2507
2508 /*************************************************************************
2509 * SHGetSpecialFolderLocation [SHELL32.@]
2510 *
2511 * NOTES
2512 * In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
2513 * directory.
2514 */
2515 HRESULT WINAPI SHGetSpecialFolderLocation(
2516 HWND hwndOwner,
2517 INT nFolder,
2518 LPITEMIDLIST * ppidl)
2519 {
2520 HRESULT hr = E_INVALIDARG;
2521
2522 TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
2523
2524 if (!ppidl)
2525 return E_INVALIDARG;
2526
2527 hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
2528 return hr;
2529 }