Sync with trunk r63430.
[reactos.git] / dll / win32 / shell32 / shellpath.cpp
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 #include "precomp.h"
28
29 #include <sddl.h>
30
31 WINE_DEFAULT_DEBUG_CHANNEL(shell);
32
33 /*
34 ########## Combining and Constructing paths ##########
35 */
36
37 /*************************************************************************
38 * PathAppend [SHELL32.36]
39 */
40 BOOL WINAPI PathAppendAW(
41 LPVOID lpszPath1,
42 LPCVOID lpszPath2)
43 {
44 if (SHELL_OsIsUnicode())
45 return PathAppendW((LPWSTR)lpszPath1, (LPCWSTR)lpszPath2);
46 return PathAppendA((LPSTR)lpszPath1, (LPCSTR)lpszPath2);
47 }
48
49 /*************************************************************************
50 * PathBuildRoot [SHELL32.30]
51 */
52 LPVOID WINAPI PathBuildRootAW(LPVOID lpszPath, int drive)
53 {
54 if(SHELL_OsIsUnicode())
55 return PathBuildRootW((LPWSTR)lpszPath, drive);
56 return PathBuildRootA((LPSTR)lpszPath, drive);
57 }
58
59 /*************************************************************************
60 * PathGetExtensionA [internal]
61 *
62 * NOTES
63 * exported by ordinal
64 * return value points to the first char after the dot
65 */
66 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
67 {
68 TRACE("(%s)\n",lpszPath);
69
70 lpszPath = PathFindExtensionA(lpszPath);
71 return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
72 }
73
74 /*************************************************************************
75 * PathGetExtensionW [internal]
76 */
77 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
78 {
79 TRACE("(%s)\n",debugstr_w(lpszPath));
80
81 lpszPath = PathFindExtensionW(lpszPath);
82 return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
83 }
84
85 /*************************************************************************
86 * SHPathGetExtension [SHELL32.158]
87 */
88 EXTERN_C LPVOID WINAPI SHPathGetExtensionW(LPCWSTR lpszPath, DWORD void1, DWORD void2)
89 {
90 return PathGetExtensionW(lpszPath);
91 }
92
93 /*************************************************************************
94 * PathRemoveFileSpec [SHELL32.35]
95 */
96 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
97 {
98 if (SHELL_OsIsUnicode())
99 return PathRemoveFileSpecW((LPWSTR)lpszPath);
100 return PathRemoveFileSpecA((LPSTR)lpszPath);
101 }
102
103 /*
104 Path Manipulations
105 */
106
107 /*************************************************************************
108 * PathGetShortPathA [internal]
109 */
110 static void PathGetShortPathA(LPSTR pszPath)
111 {
112 CHAR path[MAX_PATH];
113
114 TRACE("%s\n", pszPath);
115
116 if (GetShortPathNameA(pszPath, path, MAX_PATH))
117 {
118 lstrcpyA(pszPath, path);
119 }
120 }
121
122 /*************************************************************************
123 * PathGetShortPathW [internal]
124 */
125 static void PathGetShortPathW(LPWSTR pszPath)
126 {
127 WCHAR path[MAX_PATH];
128
129 TRACE("%s\n", debugstr_w(pszPath));
130
131 if (GetShortPathNameW(pszPath, path, MAX_PATH))
132 {
133 wcscpy(pszPath, path);
134 }
135 }
136
137 /*************************************************************************
138 * PathGetShortPath [SHELL32.92]
139 */
140 EXTERN_C VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
141 {
142 if(SHELL_OsIsUnicode())
143 PathGetShortPathW((LPWSTR)pszPath);
144 PathGetShortPathA((LPSTR)pszPath);
145 }
146
147 /*
148 ########## Path Testing ##########
149 */
150
151 /*************************************************************************
152 * PathIsRoot [SHELL32.29]
153 */
154 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
155 {
156 if (SHELL_OsIsUnicode())
157 return PathIsRootW((LPWSTR)lpszPath);
158 return PathIsRootA((LPSTR)lpszPath);
159 }
160
161 /*************************************************************************
162 * PathIsExeA [internal]
163 */
164 static BOOL PathIsExeA (LPCSTR lpszPath)
165 {
166 LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
167 int i;
168 static const char * const lpszExtensions[] =
169 {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
170
171 TRACE("path=%s\n",lpszPath);
172
173 for(i=0; lpszExtensions[i]; i++)
174 if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
175
176 return FALSE;
177 }
178
179 /*************************************************************************
180 * PathIsExeW [internal]
181 */
182 BOOL PathIsExeW (LPCWSTR lpszPath)
183 {
184 LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
185 int i;
186 static const WCHAR lpszExtensions[][4] =
187 {{'e','x','e','\0'}, {'c','o','m','\0'}, {'p','i','f','\0'},
188 {'c','m','d','\0'}, {'b','a','t','\0'}, {'s','c','f','\0'},
189 {'s','c','r','\0'}, {'\0'} };
190
191 TRACE("path=%s\n",debugstr_w(lpszPath));
192
193 for(i=0; lpszExtensions[i][0]; i++)
194 if (!strcmpiW(lpszExtension,lpszExtensions[i])) return TRUE;
195
196 return FALSE;
197 }
198
199 /*************************************************************************
200 * PathIsExe [SHELL32.43]
201 */
202 BOOL WINAPI PathIsExeAW (LPCVOID path)
203 {
204 if (SHELL_OsIsUnicode())
205 return PathIsExeW ((LPWSTR)path);
206 return PathIsExeA((LPSTR)path);
207 }
208
209 /*************************************************************************
210 * PathFileExists [SHELL32.45]
211 */
212 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
213 {
214 if (SHELL_OsIsUnicode())
215 return PathFileExistsW ((LPWSTR)lpszPath);
216 return PathFileExistsA ((LPSTR)lpszPath);
217 }
218
219 /*************************************************************************
220 * PathIsSameRoot [SHELL32.650]
221 */
222 BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2)
223 {
224 if (SHELL_OsIsUnicode())
225 return PathIsSameRootW((LPCWSTR)lpszPath1, (LPCWSTR)lpszPath2);
226 return PathIsSameRootA((LPCSTR)lpszPath1, (LPCSTR)lpszPath2);
227 }
228
229 /*************************************************************************
230 * IsLFNDriveA [SHELL32.41]
231 */
232 EXTERN_C BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
233 {
234 DWORD fnlen;
235
236 if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
237 return FALSE;
238 return fnlen > 12;
239 }
240
241 /*************************************************************************
242 * IsLFNDriveW [SHELL32.42]
243 */
244 EXTERN_C BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
245 {
246 DWORD fnlen;
247
248 if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
249 return FALSE;
250 return fnlen > 12;
251 }
252
253 /*************************************************************************
254 * IsLFNDrive [SHELL32.119]
255 */
256 EXTERN_C BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
257 {
258 if (SHELL_OsIsUnicode())
259 return IsLFNDriveW((LPCWSTR)lpszPath);
260 return IsLFNDriveA((LPCSTR)lpszPath);
261 }
262
263 /*
264 ########## Creating Something Unique ##########
265 */
266 /*************************************************************************
267 * PathMakeUniqueNameA [internal]
268 */
269 static BOOL PathMakeUniqueNameA(
270 LPSTR lpszBuffer,
271 DWORD dwBuffSize,
272 LPCSTR lpszShortName,
273 LPCSTR lpszLongName,
274 LPCSTR lpszPathName)
275 {
276 FIXME("%p %u %s %s %s stub\n",
277 lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
278 debugstr_a(lpszLongName), debugstr_a(lpszPathName));
279 return TRUE;
280 }
281
282 /*************************************************************************
283 * PathMakeUniqueNameW [internal]
284 */
285 static BOOL PathMakeUniqueNameW(
286 LPWSTR lpszBuffer,
287 DWORD dwBuffSize,
288 LPCWSTR lpszShortName,
289 LPCWSTR lpszLongName,
290 LPCWSTR lpszPathName)
291 {
292 FIXME("%p %u %s %s %s stub\n",
293 lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
294 debugstr_w(lpszLongName), debugstr_w(lpszPathName));
295 return TRUE;
296 }
297
298 /*************************************************************************
299 * PathMakeUniqueName [SHELL32.47]
300 */
301 BOOL WINAPI PathMakeUniqueNameAW(
302 LPVOID lpszBuffer,
303 DWORD dwBuffSize,
304 LPCVOID lpszShortName,
305 LPCVOID lpszLongName,
306 LPCVOID lpszPathName)
307 {
308 if (SHELL_OsIsUnicode())
309 return PathMakeUniqueNameW((LPWSTR)lpszBuffer, dwBuffSize, (LPCWSTR)lpszShortName, (LPCWSTR)lpszLongName, (LPCWSTR)lpszPathName);
310 return PathMakeUniqueNameA((LPSTR)lpszBuffer, dwBuffSize, (LPCSTR)lpszShortName, (LPCSTR)lpszLongName, (LPCSTR)lpszPathName);
311 }
312
313 /*************************************************************************
314 * PathYetAnotherMakeUniqueName [SHELL32.75]
315 *
316 * NOTES
317 * exported by ordinal
318 */
319 BOOL WINAPI PathYetAnotherMakeUniqueName(
320 LPWSTR lpszBuffer,
321 LPCWSTR lpszPathName,
322 LPCWSTR lpszShortName,
323 LPCWSTR lpszLongName)
324 {
325 FIXME("(%p, %s, %s ,%s):stub.\n",
326 lpszBuffer, debugstr_w(lpszPathName), debugstr_w(lpszShortName), debugstr_w(lpszLongName));
327 return TRUE;
328 }
329
330
331 /*
332 ########## cleaning and resolving paths ##########
333 */
334
335 /*************************************************************************
336 * PathCleanupSpec [SHELL32.171]
337 *
338 * lpszFile is changed in place.
339 */
340 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
341 {
342 int i = 0;
343 DWORD rc = 0;
344 int length = 0;
345
346 if (SHELL_OsIsUnicode())
347 {
348 LPWSTR p = lpszFileW;
349
350 TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
351
352 if (lpszPathW)
353 length = wcslen(lpszPathW);
354
355 while (*p)
356 {
357 int gct = PathGetCharTypeW(*p);
358 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
359 {
360 lpszFileW[i]='-';
361 rc |= PCS_REPLACEDCHAR;
362 }
363 else
364 lpszFileW[i]=*p;
365 i++;
366 p++;
367 if (length + i == MAX_PATH)
368 {
369 rc |= PCS_FATAL | PCS_PATHTOOLONG;
370 break;
371 }
372 }
373 lpszFileW[i]=0;
374 }
375 else
376 {
377 LPSTR lpszFileA = (LPSTR)lpszFileW;
378 LPCSTR lpszPathA = (LPCSTR)lpszPathW;
379 LPSTR p = lpszFileA;
380
381 TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
382
383 if (lpszPathA)
384 length = strlen(lpszPathA);
385
386 while (*p)
387 {
388 int gct = PathGetCharTypeA(*p);
389 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
390 {
391 lpszFileA[i]='-';
392 rc |= PCS_REPLACEDCHAR;
393 }
394 else
395 lpszFileA[i]=*p;
396 i++;
397 p++;
398 if (length + i == MAX_PATH)
399 {
400 rc |= PCS_FATAL | PCS_PATHTOOLONG;
401 break;
402 }
403 }
404 lpszFileA[i]=0;
405 }
406 return rc;
407 }
408
409 /*************************************************************************
410 * PathQualifyA [SHELL32]
411 */
412 static BOOL PathQualifyA(LPCSTR pszPath)
413 {
414 FIXME("%s\n",pszPath);
415 return 0;
416 }
417
418 /*************************************************************************
419 * PathQualifyW [SHELL32]
420 */
421 static BOOL PathQualifyW(LPCWSTR pszPath)
422 {
423 FIXME("%s\n",debugstr_w(pszPath));
424 return 0;
425 }
426
427 /*************************************************************************
428 * PathQualify [SHELL32.49]
429 */
430 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
431 {
432 if (SHELL_OsIsUnicode())
433 return PathQualifyW((LPCWSTR)pszPath);
434 return PathQualifyA((LPCSTR)pszPath);
435 }
436
437 /*************************************************************************
438 * PathResolveA [SHELL32.51]
439 */
440 static BOOL PathResolveA(
441 LPSTR lpszPath,
442 LPCSTR *alpszPaths,
443 DWORD dwFlags)
444 {
445 FIXME("(%s,%p,0x%08x),stub!\n",
446 lpszPath, *alpszPaths, dwFlags);
447 return 0;
448 }
449
450 /*************************************************************************
451 * PathResolveW [SHELL32]
452 */
453 static BOOL PathResolveW(
454 LPWSTR lpszPath,
455 LPCWSTR *alpszPaths,
456 DWORD dwFlags)
457 {
458 FIXME("(%s,%p,0x%08x),stub!\n",
459 debugstr_w(lpszPath), debugstr_w(*alpszPaths), dwFlags);
460 return 0;
461 }
462
463 /*************************************************************************
464 * PathResolve [SHELL32.51]
465 */
466 BOOL WINAPI PathResolveAW(
467 LPVOID lpszPath,
468 LPCVOID *alpszPaths,
469 DWORD dwFlags)
470 {
471 if (SHELL_OsIsUnicode())
472 return PathResolveW((LPWSTR)lpszPath, (LPCWSTR *)alpszPaths, dwFlags);
473 return PathResolveA((LPSTR)lpszPath, (LPCSTR *)alpszPaths, dwFlags);
474 }
475
476 /*************************************************************************
477 * PathProcessCommandA [SHELL32.653]
478 */
479 static LONG PathProcessCommandA (
480 LPCSTR lpszPath,
481 LPSTR lpszBuff,
482 DWORD dwBuffSize,
483 DWORD dwFlags)
484 {
485 FIXME("%s %p 0x%04x 0x%04x stub\n",
486 lpszPath, lpszBuff, dwBuffSize, dwFlags);
487 if(!lpszPath) return -1;
488 if(lpszBuff) strcpy(lpszBuff, lpszPath);
489 return strlen(lpszPath);
490 }
491
492 /*************************************************************************
493 * PathProcessCommandW
494 */
495 static LONG PathProcessCommandW (
496 LPCWSTR lpszPath,
497 LPWSTR lpszBuff,
498 DWORD dwBuffSize,
499 DWORD dwFlags)
500 {
501 FIXME("(%s, %p, 0x%04x, 0x%04x) stub\n",
502 debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
503 if(!lpszPath) return -1;
504 if(lpszBuff) wcscpy(lpszBuff, lpszPath);
505 return wcslen(lpszPath);
506 }
507
508 /*************************************************************************
509 * PathProcessCommand (SHELL32.653)
510 */
511 LONG WINAPI PathProcessCommandAW (
512 LPCVOID lpszPath,
513 LPVOID lpszBuff,
514 DWORD dwBuffSize,
515 DWORD dwFlags)
516 {
517 if (SHELL_OsIsUnicode())
518 return PathProcessCommandW((LPCWSTR)lpszPath, (LPWSTR)lpszBuff, dwBuffSize, dwFlags);
519 return PathProcessCommandA((LPCSTR)lpszPath, (LPSTR)lpszBuff, dwBuffSize, dwFlags);
520 }
521
522 /*
523 ########## special ##########
524 */
525
526 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'};
527 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'};
528 static const WCHAR AppDataW[] = {'A','p','p','D','a','t','a','\0'};
529 static const WCHAR CacheW[] = {'C','a','c','h','e','\0'};
530 static const WCHAR CD_BurningW[] = {'C','D',' ','B','u','r','n','i','n','g','\0'};
531 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'};
532 static const WCHAR Common_AppDataW[] = {'C','o','m','m','o','n',' ','A','p','p','D','a','t','a','\0'};
533 static const WCHAR Common_DesktopW[] = {'C','o','m','m','o','n',' ','D','e','s','k','t','o','p','\0'};
534 static const WCHAR Common_DocumentsW[] = {'C','o','m','m','o','n',' ','D','o','c','u','m','e','n','t','s','\0'};
535 static const WCHAR CommonFilesDirW[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r','\0'};
536 static const WCHAR CommonMusicW[] = {'C','o','m','m','o','n','M','u','s','i','c','\0'};
537 static const WCHAR CommonPicturesW[] = {'C','o','m','m','o','n','P','i','c','t','u','r','e','s','\0'};
538 static const WCHAR Common_ProgramsW[] = {'C','o','m','m','o','n',' ','P','r','o','g','r','a','m','s','\0'};
539 static const WCHAR Common_StartUpW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t','U','p','\0'};
540 static const WCHAR Common_Start_MenuW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t',' ','M','e','n','u','\0'};
541 static const WCHAR Common_TemplatesW[] = {'C','o','m','m','o','n',' ','T','e','m','p','l','a','t','e','s','\0'};
542 static const WCHAR CommonVideoW[] = {'C','o','m','m','o','n','V','i','d','e','o','\0'};
543 static const WCHAR CookiesW[] = {'C','o','o','k','i','e','s','\0'};
544 static const WCHAR DesktopW[] = {'D','e','s','k','t','o','p','\0'};
545 static const WCHAR FavoritesW[] = {'F','a','v','o','r','i','t','e','s','\0'};
546 static const WCHAR FontsW[] = {'F','o','n','t','s','\0'};
547 static const WCHAR HistoryW[] = {'H','i','s','t','o','r','y','\0'};
548 static const WCHAR Local_AppDataW[] = {'L','o','c','a','l',' ','A','p','p','D','a','t','a','\0'};
549 static const WCHAR My_MusicW[] = {'M','y',' ','M','u','s','i','c','\0'};
550 static const WCHAR My_PicturesW[] = {'M','y',' ','P','i','c','t','u','r','e','s','\0'};
551 static const WCHAR My_VideoW[] = {'M','y',' ','V','i','d','e','o','\0'};
552 static const WCHAR NetHoodW[] = {'N','e','t','H','o','o','d','\0'};
553 static const WCHAR PersonalW[] = {'P','e','r','s','o','n','a','l','\0'};
554 static const WCHAR PrintHoodW[] = {'P','r','i','n','t','H','o','o','d','\0'};
555 static const WCHAR ProgramFilesDirW[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r','\0'};
556 static const WCHAR ProgramsW[] = {'P','r','o','g','r','a','m','s','\0'};
557 static const WCHAR RecentW[] = {'R','e','c','e','n','t','\0'};
558 static const WCHAR ResourcesW[] = {'R','e','s','o','u','r','c','e','s','\0'};
559 static const WCHAR SendToW[] = {'S','e','n','d','T','o','\0'};
560 static const WCHAR StartUpW[] = {'S','t','a','r','t','U','p','\0'};
561 static const WCHAR Start_MenuW[] = {'S','t','a','r','t',' ','M','e','n','u','\0'};
562 static const WCHAR TemplatesW[] = {'T','e','m','p','l','a','t','e','s','\0'};
563 static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t','\0'};
564 static const WCHAR AllUsersProfileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%','\0'};
565 static const WCHAR UserProfileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%','\0'};
566 static const WCHAR SystemDriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%','\0'};
567 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'};
568 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'};
569
570 #if 0
571 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};
572 static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
573 static const WCHAR AllUsersProfileValueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
574 static const WCHAR szDefaultProfileDirW[] = {'p','r','o','f','i','l','e','s','\0'};
575 static const WCHAR AllUsersW[] = {'A','l','l',' ','U','s','e','r','s','\0'};
576 #endif
577
578 /* This defaults to L"Documents and Settings" on Windows 2000/XP, but we're
579 * acting more Windows 9x-like for now.
580 */
581
582 typedef enum _CSIDL_Type {
583 CSIDL_Type_User,
584 CSIDL_Type_AllUsers,
585 CSIDL_Type_CurrVer,
586 CSIDL_Type_Disallowed,
587 CSIDL_Type_NonExistent,
588 CSIDL_Type_WindowsPath,
589 CSIDL_Type_SystemPath,
590 } CSIDL_Type;
591
592 typedef struct
593 {
594 CSIDL_Type type;
595 LPCWSTR szValueName;
596 LPCWSTR szDefaultPath; /* fallback string or resource ID */
597 } CSIDL_DATA;
598
599 static const CSIDL_DATA CSIDL_Data[] =
600 {
601 { /* 0x00 - CSIDL_DESKTOP */
602 CSIDL_Type_User,
603 DesktopW,
604 MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
605 },
606 { /* 0x01 - CSIDL_INTERNET */
607 CSIDL_Type_Disallowed,
608 NULL,
609 NULL
610 },
611 { /* 0x02 - CSIDL_PROGRAMS */
612 CSIDL_Type_User,
613 ProgramsW,
614 MAKEINTRESOURCEW(IDS_PROGRAMS)
615 },
616 { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
617 CSIDL_Type_SystemPath,
618 NULL,
619 NULL
620 },
621 { /* 0x04 - CSIDL_PRINTERS */
622 CSIDL_Type_SystemPath,
623 NULL,
624 NULL
625 },
626 { /* 0x05 - CSIDL_PERSONAL */
627 CSIDL_Type_User,
628 PersonalW,
629 MAKEINTRESOURCEW(IDS_PERSONAL)
630 },
631 { /* 0x06 - CSIDL_FAVORITES */
632 CSIDL_Type_User,
633 FavoritesW,
634 MAKEINTRESOURCEW(IDS_FAVORITES)
635 },
636 { /* 0x07 - CSIDL_STARTUP */
637 CSIDL_Type_User,
638 StartUpW,
639 MAKEINTRESOURCEW(IDS_STARTUP)
640 },
641 { /* 0x08 - CSIDL_RECENT */
642 CSIDL_Type_User,
643 RecentW,
644 MAKEINTRESOURCEW(IDS_RECENT)
645 },
646 { /* 0x09 - CSIDL_SENDTO */
647 CSIDL_Type_User,
648 SendToW,
649 MAKEINTRESOURCEW(IDS_SENDTO)
650 },
651 { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
652 CSIDL_Type_Disallowed,
653 NULL,
654 NULL,
655 },
656 { /* 0x0b - CSIDL_STARTMENU */
657 CSIDL_Type_User,
658 Start_MenuW,
659 MAKEINTRESOURCEW(IDS_STARTMENU)
660 },
661 { /* 0x0c - CSIDL_MYDOCUMENTS */
662 CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
663 NULL,
664 NULL
665 },
666 { /* 0x0d - CSIDL_MYMUSIC */
667 CSIDL_Type_User,
668 My_MusicW,
669 MAKEINTRESOURCEW(IDS_MYMUSIC)
670 },
671 { /* 0x0e - CSIDL_MYVIDEO */
672 CSIDL_Type_User,
673 My_VideoW,
674 MAKEINTRESOURCEW(IDS_MYVIDEO)
675 },
676 { /* 0x0f - unassigned */
677 CSIDL_Type_Disallowed,
678 NULL,
679 NULL,
680 },
681 { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
682 CSIDL_Type_User,
683 DesktopW,
684 MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
685 },
686 { /* 0x11 - CSIDL_DRIVES */
687 CSIDL_Type_Disallowed,
688 NULL,
689 NULL,
690 },
691 { /* 0x12 - CSIDL_NETWORK */
692 CSIDL_Type_Disallowed,
693 NULL,
694 NULL,
695 },
696 { /* 0x13 - CSIDL_NETHOOD */
697 CSIDL_Type_User,
698 NetHoodW,
699 MAKEINTRESOURCEW(IDS_NETHOOD)
700 },
701 { /* 0x14 - CSIDL_FONTS */
702 CSIDL_Type_WindowsPath,
703 FontsW,
704 FontsW
705 },
706 { /* 0x15 - CSIDL_TEMPLATES */
707 CSIDL_Type_User,
708 TemplatesW,
709 MAKEINTRESOURCEW(IDS_TEMPLATES)
710 },
711 { /* 0x16 - CSIDL_COMMON_STARTMENU */
712 CSIDL_Type_AllUsers,
713 Common_Start_MenuW,
714 MAKEINTRESOURCEW(IDS_STARTMENU)
715 },
716 { /* 0x17 - CSIDL_COMMON_PROGRAMS */
717 CSIDL_Type_AllUsers,
718 Common_ProgramsW,
719 MAKEINTRESOURCEW(IDS_PROGRAMS)
720 },
721 { /* 0x18 - CSIDL_COMMON_STARTUP */
722 CSIDL_Type_AllUsers,
723 Common_StartUpW,
724 MAKEINTRESOURCEW(IDS_STARTUP)
725 },
726 { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
727 CSIDL_Type_AllUsers,
728 Common_DesktopW,
729 MAKEINTRESOURCEW(IDS_DESKTOP)
730 },
731 { /* 0x1a - CSIDL_APPDATA */
732 CSIDL_Type_User,
733 AppDataW,
734 MAKEINTRESOURCEW(IDS_APPDATA)
735 },
736 { /* 0x1b - CSIDL_PRINTHOOD */
737 CSIDL_Type_User,
738 PrintHoodW,
739 MAKEINTRESOURCEW(IDS_PRINTHOOD)
740 },
741 { /* 0x1c - CSIDL_LOCAL_APPDATA */
742 CSIDL_Type_User,
743 Local_AppDataW,
744 MAKEINTRESOURCEW(IDS_LOCAL_APPDATA)
745 },
746 { /* 0x1d - CSIDL_ALTSTARTUP */
747 CSIDL_Type_NonExistent,
748 NULL,
749 NULL
750 },
751 { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
752 CSIDL_Type_NonExistent,
753 NULL,
754 NULL
755 },
756 { /* 0x1f - CSIDL_COMMON_FAVORITES */
757 CSIDL_Type_AllUsers,
758 FavoritesW,
759 MAKEINTRESOURCEW(IDS_FAVORITES)
760 },
761 { /* 0x20 - CSIDL_INTERNET_CACHE */
762 CSIDL_Type_User,
763 CacheW,
764 MAKEINTRESOURCEW(IDS_INTERNET_CACHE)
765 },
766 { /* 0x21 - CSIDL_COOKIES */
767 CSIDL_Type_User,
768 CookiesW,
769 MAKEINTRESOURCEW(IDS_COOKIES)
770 },
771 { /* 0x22 - CSIDL_HISTORY */
772 CSIDL_Type_User,
773 HistoryW,
774 MAKEINTRESOURCEW(IDS_HISTORY)
775 },
776 { /* 0x23 - CSIDL_COMMON_APPDATA */
777 CSIDL_Type_AllUsers,
778 Common_AppDataW,
779 MAKEINTRESOURCEW(IDS_APPDATA)
780 },
781 { /* 0x24 - CSIDL_WINDOWS */
782 CSIDL_Type_WindowsPath,
783 NULL,
784 NULL
785 },
786 { /* 0x25 - CSIDL_SYSTEM */
787 CSIDL_Type_SystemPath,
788 NULL,
789 NULL
790 },
791 { /* 0x26 - CSIDL_PROGRAM_FILES */
792 CSIDL_Type_CurrVer,
793 ProgramFilesDirW,
794 MAKEINTRESOURCEW(IDS_PROGRAM_FILES)
795 },
796 { /* 0x27 - CSIDL_MYPICTURES */
797 CSIDL_Type_User,
798 My_PicturesW,
799 MAKEINTRESOURCEW(IDS_MYPICTURES)
800 },
801 { /* 0x28 - CSIDL_PROFILE */
802 CSIDL_Type_User,
803 NULL,
804 NULL
805 },
806 { /* 0x29 - CSIDL_SYSTEMX86 */
807 CSIDL_Type_NonExistent,
808 NULL,
809 NULL
810 },
811 { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
812 CSIDL_Type_NonExistent,
813 NULL,
814 NULL
815 },
816 { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
817 CSIDL_Type_CurrVer,
818 CommonFilesDirW,
819 MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON)
820 },
821 { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
822 CSIDL_Type_NonExistent,
823 NULL,
824 NULL
825 },
826 { /* 0x2d - CSIDL_COMMON_TEMPLATES */
827 CSIDL_Type_AllUsers,
828 Common_TemplatesW,
829 MAKEINTRESOURCEW(IDS_TEMPLATES)
830 },
831 { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
832 CSIDL_Type_AllUsers,
833 Common_DocumentsW,
834 MAKEINTRESOURCEW(IDS_COMMON_DOCUMENTS)
835 },
836 { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
837 CSIDL_Type_AllUsers,
838 Common_Administrative_ToolsW,
839 MAKEINTRESOURCEW(IDS_ADMINTOOLS)
840 },
841 { /* 0x30 - CSIDL_ADMINTOOLS */
842 CSIDL_Type_User,
843 Administrative_ToolsW,
844 MAKEINTRESOURCEW(IDS_ADMINTOOLS)
845 },
846 { /* 0x31 - CSIDL_CONNECTIONS */
847 CSIDL_Type_Disallowed,
848 NULL,
849 NULL
850 },
851 { /* 0x32 - unassigned */
852 CSIDL_Type_Disallowed,
853 NULL,
854 NULL
855 },
856 { /* 0x33 - unassigned */
857 CSIDL_Type_Disallowed,
858 NULL,
859 NULL
860 },
861 { /* 0x34 - unassigned */
862 CSIDL_Type_Disallowed,
863 NULL,
864 NULL
865 },
866 { /* 0x35 - CSIDL_COMMON_MUSIC */
867 CSIDL_Type_AllUsers,
868 CommonMusicW,
869 MAKEINTRESOURCEW(IDS_COMMON_MUSIC)
870 },
871 { /* 0x36 - CSIDL_COMMON_PICTURES */
872 CSIDL_Type_AllUsers,
873 CommonPicturesW,
874 MAKEINTRESOURCEW(IDS_COMMON_PICTURES)
875 },
876 { /* 0x37 - CSIDL_COMMON_VIDEO */
877 CSIDL_Type_AllUsers,
878 CommonVideoW,
879 MAKEINTRESOURCEW(IDS_COMMON_VIDEO)
880 },
881 { /* 0x38 - CSIDL_RESOURCES */
882 CSIDL_Type_WindowsPath,
883 NULL,
884 ResourcesW
885 },
886 { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
887 CSIDL_Type_NonExistent,
888 NULL,
889 NULL
890 },
891 { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
892 CSIDL_Type_NonExistent,
893 NULL,
894 NULL
895 },
896 { /* 0x3b - CSIDL_CDBURN_AREA */
897 CSIDL_Type_User,
898 CD_BurningW,
899 MAKEINTRESOURCEW(IDS_CDBURN_AREA)
900 },
901 { /* 0x3c unassigned */
902 CSIDL_Type_Disallowed,
903 NULL,
904 NULL
905 },
906 { /* 0x3d - CSIDL_COMPUTERSNEARME */
907 CSIDL_Type_Disallowed, /* FIXME */
908 NULL,
909 NULL
910 },
911 { /* 0x3e - CSIDL_PROFILES */
912 CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
913 NULL,
914 NULL
915 }
916 };
917
918 /* Gets the value named value from the registry key
919 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
920 * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
921 * is assumed to be MAX_PATH WCHARs in length.
922 * If it exists, expands the value and writes the expanded value to
923 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
924 * Returns successful error code if the value was retrieved from the registry,
925 * and a failure otherwise.
926 */
927 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
928 LPCWSTR value, LPWSTR path)
929 {
930 HRESULT hr;
931 WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
932 LPCWSTR pShellFolderPath, pUserShellFolderPath;
933 DWORD dwDisp, dwType, dwPathLen;
934 HKEY userShellFolderKey, shellFolderKey;
935
936 TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
937 path);
938
939 if (userPrefix)
940 {
941 wcscpy(shellFolderPath, userPrefix);
942 PathAddBackslashW(shellFolderPath);
943 wcscat(shellFolderPath, szSHFolders);
944 pShellFolderPath = shellFolderPath;
945 wcscpy(userShellFolderPath, userPrefix);
946 PathAddBackslashW(userShellFolderPath);
947 wcscat(userShellFolderPath, szSHUserFolders);
948 pUserShellFolderPath = userShellFolderPath;
949 }
950 else
951 {
952 pUserShellFolderPath = szSHUserFolders;
953 pShellFolderPath = szSHFolders;
954 }
955
956 if (RegCreateKeyExW(rootKey, pShellFolderPath, 0, NULL, 0, KEY_SET_VALUE,
957 NULL, &shellFolderKey, &dwDisp))
958 {
959 TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
960 return E_FAIL;
961 }
962 if (RegCreateKeyExW(rootKey, pUserShellFolderPath, 0, NULL, 0,
963 KEY_QUERY_VALUE, NULL, &userShellFolderKey, &dwDisp))
964 {
965 TRACE("Failed to create %s\n",
966 debugstr_w(pUserShellFolderPath));
967 RegCloseKey(shellFolderKey);
968 return E_FAIL;
969 }
970
971 dwPathLen = MAX_PATH * sizeof(WCHAR);
972
973 if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
974 (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
975 {
976 LONG ret;
977
978 dwPathLen /= sizeof(WCHAR);
979
980 path[dwPathLen] = '\0';
981 if (dwType == REG_EXPAND_SZ && path[0] == '%')
982 {
983 WCHAR szTemp[MAX_PATH];
984
985 dwPathLen = ExpandEnvironmentStringsW(path, szTemp, MAX_PATH);
986 lstrcpynW(path, szTemp, dwPathLen);
987 }
988
989 ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path, dwPathLen * sizeof(WCHAR));
990 if (ret != ERROR_SUCCESS)
991 hr = HRESULT_FROM_WIN32(ret);
992 else
993 hr = S_OK;
994 }
995 else
996 hr = E_FAIL;
997
998 RegCloseKey(shellFolderKey);
999 RegCloseKey(userShellFolderKey);
1000 TRACE("returning 0x%08x\n", hr);
1001 return hr;
1002 }
1003
1004 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
1005 * pszPath, based on the entries in CSIDL_Data. By semi-expanded, I mean:
1006 * - The entry's szDefaultPath may be either a string value or an integer
1007 * resource identifier. In the latter case, the string value of the resource
1008 * is written.
1009 * - Depending on the entry's type, the path may begin with an (unexpanded)
1010 * environment variable name. The caller is responsible for expanding
1011 * environment strings if so desired.
1012 * The types that are prepended with environment variables are:
1013 * CSIDL_Type_User: %USERPROFILE%
1014 * CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
1015 * CSIDL_Type_CurrVer: %SystemDrive%
1016 * (Others might make sense too, but as yet are unneeded.)
1017 */
1018 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
1019 {
1020 DWORD dwSize;
1021 HRESULT hr;
1022 HKEY hKey;
1023 WCHAR resourcePath[MAX_PATH];
1024 LPCWSTR pDefaultPath = NULL;
1025
1026 TRACE("0x%02x,%p\n", folder, pszPath);
1027
1028 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1029 return E_INVALIDARG;
1030 if (!pszPath)
1031 return E_INVALIDARG;
1032
1033
1034 if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1035 {
1036 /* FIXME assume MAX_PATH size */
1037 dwSize = MAX_PATH * sizeof(WCHAR);
1038 if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL, NULL, (LPBYTE)pszPath, &dwSize) == ERROR_SUCCESS)
1039 {
1040 RegCloseKey(hKey);
1041 return S_OK;
1042 }
1043 RegCloseKey(hKey);
1044 }
1045
1046 if (CSIDL_Data[folder].szDefaultPath &&
1047 IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
1048 {
1049 if (LoadStringW(shell32_hInstance,
1050 LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
1051 {
1052 hr = S_OK;
1053 pDefaultPath = resourcePath;
1054 }
1055 else
1056 {
1057 FIXME("(%d,%s), LoadString failed, missing translation?\n", folder,
1058 debugstr_w(pszPath));
1059 hr = E_FAIL;
1060 }
1061 }
1062 else
1063 {
1064 hr = S_OK;
1065 pDefaultPath = CSIDL_Data[folder].szDefaultPath;
1066 }
1067 if (SUCCEEDED(hr))
1068 {
1069 switch (CSIDL_Data[folder].type)
1070 {
1071 case CSIDL_Type_User:
1072 wcscpy(pszPath, UserProfileW);
1073 break;
1074 case CSIDL_Type_AllUsers:
1075 wcscpy(pszPath, AllUsersProfileW);
1076 break;
1077 case CSIDL_Type_CurrVer:
1078 wcscpy(pszPath, SystemDriveW);
1079 break;
1080 default:
1081 ; /* no corresponding env. var, do nothing */
1082 }
1083 if (pDefaultPath)
1084 {
1085 PathAddBackslashW(pszPath);
1086 wcscat(pszPath, pDefaultPath);
1087 }
1088 }
1089 TRACE("returning 0x%08x\n", hr);
1090 return hr;
1091 }
1092
1093 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
1094 * The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value
1095 * can be overridden in the HKLM\\szCurrentVersion key.
1096 * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
1097 * the registry, uses _SHGetDefaultValue to get the value.
1098 */
1099 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
1100 LPWSTR pszPath)
1101 {
1102 HRESULT hr;
1103
1104 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1105
1106 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1107 return E_INVALIDARG;
1108 if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
1109 return E_INVALIDARG;
1110 if (!pszPath)
1111 return E_INVALIDARG;
1112
1113 if (dwFlags & SHGFP_TYPE_DEFAULT)
1114 hr = _SHGetDefaultValue(folder, pszPath);
1115 else
1116 {
1117 HKEY hKey;
1118 DWORD dwDisp;
1119
1120 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szCurrentVersion, 0,
1121 NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp))
1122 hr = E_FAIL;
1123 else
1124 {
1125 DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
1126
1127 if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
1128 &dwType, (LPBYTE)pszPath, &dwPathLen) ||
1129 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
1130 {
1131 hr = _SHGetDefaultValue(folder, pszPath);
1132 dwType = REG_EXPAND_SZ;
1133 RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
1134 (LPBYTE)pszPath, (wcslen(pszPath)+1)*sizeof(WCHAR));
1135 }
1136 else
1137 {
1138 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
1139 hr = S_OK;
1140 }
1141 RegCloseKey(hKey);
1142 }
1143 }
1144 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1145 return hr;
1146 }
1147
1148 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
1149 * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise
1150 * calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken:
1151 * - if hToken is -1, looks in HKEY_USERS\.Default
1152 * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
1153 * if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally
1154 * calls _SHGetDefaultValue for it.
1155 */
1156 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
1157 LPWSTR pszPath)
1158 {
1159 HRESULT hr;
1160
1161 TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
1162
1163 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1164 return E_INVALIDARG;
1165
1166 if (CSIDL_Data[folder].type != CSIDL_Type_User)
1167 return E_INVALIDARG;
1168
1169 if (!pszPath)
1170 return E_INVALIDARG;
1171
1172 if (dwFlags & SHGFP_TYPE_DEFAULT)
1173 {
1174 if (hToken != NULL && hToken != (HANDLE)-1)
1175 {
1176 FIXME("unsupported for user other than current or default\n");
1177 return E_FAIL;
1178 }
1179 hr = _SHGetDefaultValue(folder, pszPath);
1180 }
1181 else
1182 {
1183 LPWSTR userPrefix;
1184 HKEY hRootKey;
1185
1186 if (hToken == (HANDLE)-1)
1187 {
1188 /* Get the folder of the default user */
1189 hRootKey = HKEY_USERS;
1190 userPrefix = (LPWSTR)DefaultW;
1191 }
1192 else if(!hToken)
1193 {
1194 /* Get the folder of the current user */
1195 hRootKey = HKEY_CURRENT_USER;
1196 userPrefix = NULL;
1197 }
1198 else
1199 {
1200 /* Get the folder of the specified user */
1201 DWORD InfoLength;
1202 PTOKEN_USER UserInfo;
1203
1204 hRootKey = HKEY_USERS;
1205
1206 GetTokenInformation(hToken, TokenUser, NULL, 0, &InfoLength);
1207 UserInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, InfoLength);
1208
1209 if(!GetTokenInformation(hToken, TokenUser, UserInfo, InfoLength, &InfoLength))
1210 {
1211 WARN("GetTokenInformation failed for %x!\n", hToken);
1212 HeapFree(GetProcessHeap(), 0, UserInfo);
1213 return E_FAIL;
1214 }
1215
1216 if(!ConvertSidToStringSidW(UserInfo->User.Sid, &userPrefix))
1217 {
1218 WARN("ConvertSidToStringSidW failed for %x!\n", hToken);
1219 HeapFree(GetProcessHeap(), 0, UserInfo);
1220 return E_FAIL;
1221 }
1222
1223 HeapFree(GetProcessHeap(), 0, UserInfo);
1224 }
1225
1226 hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, CSIDL_Data[folder].szValueName, pszPath);
1227
1228 /* Free the memory allocated by ConvertSidToStringSidW */
1229 if(hToken && hToken != (HANDLE)-1)
1230 LocalFree(userPrefix);
1231
1232 if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
1233 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, CSIDL_Data[folder].szValueName, pszPath);
1234
1235 if (FAILED(hr))
1236 hr = _SHGetDefaultValue(folder, pszPath);
1237 }
1238
1239 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1240 return hr;
1241 }
1242
1243 /* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has
1244 * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls
1245 * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
1246 * If this fails, falls back to _SHGetDefaultValue.
1247 */
1248 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
1249 LPWSTR pszPath)
1250 {
1251 HRESULT hr;
1252
1253 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1254
1255 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1256 return E_INVALIDARG;
1257 if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
1258 return E_INVALIDARG;
1259 if (!pszPath)
1260 return E_INVALIDARG;
1261
1262 if (dwFlags & SHGFP_TYPE_DEFAULT)
1263 hr = _SHGetDefaultValue(folder, pszPath);
1264 else
1265 {
1266 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1267 CSIDL_Data[folder].szValueName, pszPath);
1268 if (FAILED(hr))
1269 hr = _SHGetDefaultValue(folder, pszPath);
1270 }
1271 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1272 return hr;
1273 }
1274
1275 /*************************************************************************
1276 * SHGetFolderPathW [SHELL32.@]
1277 *
1278 * Convert nFolder to path.
1279 *
1280 * RETURNS
1281 * Success: S_OK
1282 * Failure: standard HRESULT error codes.
1283 *
1284 * NOTES
1285 * Most values can be overridden in either
1286 * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1287 * or in the same location in HKLM.
1288 * The "Shell Folders" registry key was used in NT4 and earlier systems.
1289 * Beginning with Windows 2000, the "User Shell Folders" key is used, so
1290 * changes made to it are made to the former key too. This synchronization is
1291 * done on-demand: not until someone requests the value of one of these paths
1292 * (by calling one of the SHGet functions) is the value synchronized.
1293 * Furthermore, the HKCU paths take precedence over the HKLM paths.
1294 */
1295 HRESULT WINAPI SHGetFolderPathW(
1296 HWND hwndOwner, /* [I] owner window */
1297 int nFolder, /* [I] CSIDL identifying the folder */
1298 HANDLE hToken, /* [I] access token */
1299 DWORD dwFlags, /* [I] which path to return */
1300 LPWSTR pszPath) /* [O] converted path */
1301 {
1302 HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
1303 if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
1304 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1305 return hr;
1306 }
1307
1308 HRESULT WINAPI SHGetFolderPathAndSubDirA(
1309 HWND hwndOwner, /* [I] owner window */
1310 int nFolder, /* [I] CSIDL identifying the folder */
1311 HANDLE hToken, /* [I] access token */
1312 DWORD dwFlags, /* [I] which path to return */
1313 LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
1314 LPSTR pszPath) /* [O] converted path */
1315 {
1316 int length;
1317 HRESULT hr = S_OK;
1318 LPWSTR pszSubPathW = NULL;
1319 LPWSTR pszPathW = NULL;
1320 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1321
1322 if(pszPath) {
1323 pszPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
1324 if(!pszPathW) {
1325 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1326 goto cleanup;
1327 }
1328 }
1329 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1330
1331 /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
1332 * set (null), or an empty string.therefore call it without the parameter set
1333 * if pszSubPath is an empty string
1334 */
1335 if (pszSubPath && pszSubPath[0]) {
1336 length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
1337 pszSubPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR));
1338 if(!pszSubPathW) {
1339 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1340 goto cleanup;
1341 }
1342 MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
1343 }
1344
1345 hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
1346
1347 if (SUCCEEDED(hr) && pszPath)
1348 WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
1349
1350 cleanup:
1351 HeapFree(GetProcessHeap(), 0, pszPathW);
1352 HeapFree(GetProcessHeap(), 0, pszSubPathW);
1353 return hr;
1354 }
1355
1356 HRESULT WINAPI SHGetFolderPathAndSubDirW(
1357 HWND hwndOwner, /* [I] owner window */
1358 int nFolder, /* [I] CSIDL identifying the folder */
1359 HANDLE hToken, /* [I] access token */
1360 DWORD dwFlags, /* [I] which path to return */
1361 LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
1362 LPWSTR pszPath) /* [O] converted path */
1363 {
1364 HRESULT hr;
1365 WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
1366 DWORD folder = nFolder & CSIDL_FOLDER_MASK; //FIXME
1367 CSIDL_Type type;
1368 int ret;
1369
1370 TRACE("%p,%p,nFolder=0x%04x,%s\n", hwndOwner,pszPath,nFolder,debugstr_w(pszSubPath));
1371
1372 /* Windows always NULL-terminates the resulting path regardless of success
1373 * or failure, so do so first
1374 */
1375 if (pszPath)
1376 *pszPath = '\0';
1377
1378 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1379 return E_INVALIDARG;
1380 if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
1381 return E_INVALIDARG;
1382 szTemp[0] = 0;
1383 type = CSIDL_Data[folder].type;
1384 switch (type)
1385 {
1386 case CSIDL_Type_Disallowed:
1387 hr = E_INVALIDARG;
1388 break;
1389 case CSIDL_Type_NonExistent:
1390 hr = S_FALSE;
1391 break;
1392 case CSIDL_Type_WindowsPath:
1393 GetWindowsDirectoryW(szTemp, MAX_PATH);
1394 if (CSIDL_Data[folder].szDefaultPath &&
1395 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1396 *CSIDL_Data[folder].szDefaultPath)
1397 {
1398 PathAddBackslashW(szTemp);
1399 wcscat(szTemp, CSIDL_Data[folder].szDefaultPath);
1400 }
1401 hr = S_OK;
1402 break;
1403 case CSIDL_Type_SystemPath:
1404 GetSystemDirectoryW(szTemp, MAX_PATH);
1405 if (CSIDL_Data[folder].szDefaultPath &&
1406 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1407 *CSIDL_Data[folder].szDefaultPath)
1408 {
1409 PathAddBackslashW(szTemp);
1410 wcscat(szTemp, CSIDL_Data[folder].szDefaultPath);
1411 }
1412 hr = S_OK;
1413 break;
1414 case CSIDL_Type_CurrVer:
1415 hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
1416 break;
1417 case CSIDL_Type_User:
1418 hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
1419 break;
1420 case CSIDL_Type_AllUsers:
1421 hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
1422 break;
1423 default:
1424 FIXME("bogus type %d, please fix\n", type);
1425 hr = E_INVALIDARG;
1426 break;
1427 }
1428
1429 /* Expand environment strings if necessary */
1430 if (*szTemp == '%')
1431 {
1432 DWORD ExpandRet = ExpandEnvironmentStringsW(szTemp, szBuildPath, MAX_PATH);
1433
1434 if (ExpandRet > MAX_PATH)
1435 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1436 else if (ExpandRet == 0)
1437 hr = HRESULT_FROM_WIN32(GetLastError());
1438 else
1439 hr = S_OK;
1440 }
1441 else
1442 {
1443 wcscpy(szBuildPath, szTemp);
1444 }
1445
1446 if (FAILED(hr)) goto end;
1447
1448 if(pszSubPath) {
1449 /* make sure the new path does not exceed th bufferlength
1450 * rememebr to backslash and the termination */
1451 if(MAX_PATH < (wcslen(szBuildPath) + wcslen(pszSubPath) + 2)) {
1452 hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
1453 goto end;
1454 }
1455 PathAppendW(szBuildPath, pszSubPath);
1456 PathRemoveBackslashW(szBuildPath);
1457 }
1458 /* Copy the path if it's available before we might return */
1459 if (SUCCEEDED(hr) && pszPath)
1460 wcscpy(pszPath, szBuildPath);
1461
1462 /* if we don't care about existing directories we are ready */
1463 if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
1464
1465 if (PathFileExistsW(szBuildPath)) goto end;
1466
1467 /* not existing but we are not allowed to create it. The return value
1468 * is verified against shell32 version 6.0.
1469 */
1470 if (!(nFolder & CSIDL_FLAG_CREATE))
1471 {
1472 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
1473 goto end;
1474 }
1475
1476 /* create directory/directories */
1477 ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
1478 if (ret && ret != ERROR_ALREADY_EXISTS)
1479 {
1480 WARN("Failed to create directory %s.\n", debugstr_w(szBuildPath));
1481 hr = E_FAIL;
1482 goto end;
1483 }
1484
1485 TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
1486 end:
1487 TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
1488 return hr;
1489 }
1490
1491 /*************************************************************************
1492 * SHGetFolderPathA [SHELL32.@]
1493 *
1494 * See SHGetFolderPathW.
1495 */
1496 HRESULT WINAPI SHGetFolderPathA(
1497 HWND hwndOwner,
1498 int nFolder,
1499 HANDLE hToken,
1500 DWORD dwFlags,
1501 LPSTR pszPath)
1502 {
1503 WCHAR szTemp[MAX_PATH];
1504 HRESULT hr;
1505
1506 TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
1507
1508 if (pszPath)
1509 *pszPath = '\0';
1510 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
1511 if (SUCCEEDED(hr) && pszPath)
1512 WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
1513 NULL);
1514
1515 return hr;
1516 }
1517
1518 /* For each folder in folders, if its value has not been set in the registry,
1519 * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
1520 * folder's type) to get the unexpanded value first.
1521 * Writes the unexpanded value to User Shell Folders, and queries it with
1522 * SHGetFolderPathW to force the creation of the directory if it doesn't
1523 * already exist. SHGetFolderPathW also returns the expanded value, which
1524 * this then writes to Shell Folders.
1525 */
1526 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
1527 LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
1528 UINT foldersLen)
1529 {
1530 UINT i;
1531 WCHAR path[MAX_PATH];
1532 HRESULT hr = S_OK;
1533 HKEY hUserKey = NULL, hKey = NULL;
1534 DWORD dwDisp, dwType, dwPathLen;
1535 LONG ret;
1536
1537 TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
1538 debugstr_w(szUserShellFolderPath), folders, foldersLen);
1539
1540 ret = RegCreateKeyExW(hRootKey, szUserShellFolderPath, 0, NULL, 0,
1541 KEY_ALL_ACCESS, NULL, &hUserKey, &dwDisp);
1542 if (ret)
1543 hr = HRESULT_FROM_WIN32(ret);
1544 else
1545 {
1546 ret = RegCreateKeyExW(hRootKey, szShellFolderPath, 0, NULL, 0,
1547 KEY_ALL_ACCESS, NULL, &hKey, &dwDisp);
1548 if (ret)
1549 hr = HRESULT_FROM_WIN32(ret);
1550 }
1551 for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
1552 {
1553 dwPathLen = MAX_PATH * sizeof(WCHAR);
1554 if (RegQueryValueExW(hUserKey, CSIDL_Data[folders[i]].szValueName, NULL,
1555 &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
1556 dwType != REG_EXPAND_SZ))
1557 {
1558 *path = '\0';
1559 if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
1560 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
1561 path);
1562 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
1563 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
1564 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
1565 GetWindowsDirectoryW(path, MAX_PATH);
1566 else
1567 hr = E_FAIL;
1568 if (*path)
1569 {
1570 ret = RegSetValueExW(hUserKey,
1571 CSIDL_Data[folders[i]].szValueName, 0, REG_EXPAND_SZ,
1572 (LPBYTE)path, (wcslen(path) + 1) * sizeof(WCHAR));
1573 if (ret)
1574 hr = HRESULT_FROM_WIN32(ret);
1575 else
1576 {
1577 // Don't fail if folder can't be created
1578 if (SUCCEEDED(SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
1579 hToken, SHGFP_TYPE_DEFAULT, path)))
1580 {
1581 ret = RegSetValueExW(hKey, CSIDL_Data[folders[i]].szValueName, 0, REG_SZ,
1582 (LPBYTE)path, (wcslen(path) + 1) * sizeof(WCHAR));
1583 if (ret)
1584 hr = HRESULT_FROM_WIN32(ret);
1585 }
1586 }
1587 }
1588 }
1589 }
1590 if (hUserKey)
1591 RegCloseKey(hUserKey);
1592 if (hKey)
1593 RegCloseKey(hKey);
1594
1595 TRACE("returning 0x%08x\n", hr);
1596 return hr;
1597 }
1598
1599 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
1600 {
1601 static const UINT folders[] = {
1602 CSIDL_PROGRAMS,
1603 CSIDL_PERSONAL,
1604 CSIDL_FAVORITES,
1605 CSIDL_APPDATA,
1606 CSIDL_STARTUP,
1607 CSIDL_RECENT,
1608 CSIDL_SENDTO,
1609 CSIDL_STARTMENU,
1610 CSIDL_MYMUSIC,
1611 CSIDL_MYVIDEO,
1612 CSIDL_DESKTOPDIRECTORY,
1613 CSIDL_NETHOOD,
1614 CSIDL_TEMPLATES,
1615 CSIDL_PRINTHOOD,
1616 CSIDL_LOCAL_APPDATA,
1617 CSIDL_INTERNET_CACHE,
1618 CSIDL_COOKIES,
1619 CSIDL_HISTORY,
1620 CSIDL_MYPICTURES,
1621 CSIDL_FONTS
1622 };
1623 WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
1624 LPCWSTR pUserShellFolderPath, pShellFolderPath;
1625 HRESULT hr = S_OK;
1626 HKEY hRootKey;
1627 HANDLE hToken;
1628
1629 TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
1630 if (bDefault)
1631 {
1632 hToken = (HANDLE)-1;
1633 hRootKey = HKEY_USERS;
1634 wcscpy(userShellFolderPath, DefaultW);
1635 PathAddBackslashW(userShellFolderPath);
1636 wcscat(userShellFolderPath, szSHUserFolders);
1637 pUserShellFolderPath = userShellFolderPath;
1638 wcscpy(shellFolderPath, DefaultW);
1639 PathAddBackslashW(shellFolderPath);
1640 wcscat(shellFolderPath, szSHFolders);
1641 pShellFolderPath = shellFolderPath;
1642 }
1643 else
1644 {
1645 hToken = NULL;
1646 hRootKey = HKEY_CURRENT_USER;
1647 pUserShellFolderPath = szSHUserFolders;
1648 pShellFolderPath = szSHFolders;
1649 }
1650
1651 hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
1652 pShellFolderPath, folders, sizeof(folders) / sizeof(folders[0]));
1653 TRACE("returning 0x%08x\n", hr);
1654 return hr;
1655 }
1656
1657 static HRESULT _SHRegisterCommonShellFolders(void)
1658 {
1659 static const UINT folders[] = {
1660 CSIDL_COMMON_STARTMENU,
1661 CSIDL_COMMON_PROGRAMS,
1662 CSIDL_COMMON_STARTUP,
1663 CSIDL_COMMON_DESKTOPDIRECTORY,
1664 CSIDL_COMMON_FAVORITES,
1665 CSIDL_COMMON_APPDATA,
1666 CSIDL_COMMON_TEMPLATES,
1667 CSIDL_COMMON_DOCUMENTS,
1668 };
1669 HRESULT hr;
1670
1671 TRACE("\n");
1672 hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
1673 szSHFolders, folders, sizeof(folders) / sizeof(folders[0]));
1674 TRACE("returning 0x%08x\n", hr);
1675 return hr;
1676 }
1677
1678 /* Register the default values in the registry, as some apps seem to depend
1679 * on their presence. The set registered was taken from Windows XP.
1680 */
1681 EXTERN_C HRESULT SHELL_RegisterShellFolders(void)
1682 {
1683 HRESULT hr;
1684
1685 hr = _SHRegisterUserShellFolders(TRUE);
1686 if (SUCCEEDED(hr))
1687 hr = _SHRegisterUserShellFolders(FALSE);
1688 if (SUCCEEDED(hr))
1689 hr = _SHRegisterCommonShellFolders();
1690 return hr;
1691 }
1692
1693 /*************************************************************************
1694 * SHGetSpecialFolderPathA [SHELL32.@]
1695 */
1696 BOOL WINAPI SHGetSpecialFolderPathA (
1697 HWND hwndOwner,
1698 LPSTR szPath,
1699 int nFolder,
1700 BOOL bCreate)
1701 {
1702 return (SHGetFolderPathA(
1703 hwndOwner,
1704 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
1705 NULL,
1706 0,
1707 szPath)) == S_OK ? TRUE : FALSE;
1708 }
1709
1710 /*************************************************************************
1711 * SHGetSpecialFolderPathW
1712 */
1713 BOOL WINAPI SHGetSpecialFolderPathW (
1714 HWND hwndOwner,
1715 LPWSTR szPath,
1716 int nFolder,
1717 BOOL bCreate)
1718 {
1719 return (SHGetFolderPathW(
1720 hwndOwner,
1721 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
1722 NULL,
1723 0,
1724 szPath)) == S_OK ? TRUE : FALSE;
1725 }
1726
1727 /*************************************************************************
1728 * SHGetFolderLocation [SHELL32.@]
1729 *
1730 * Gets the folder locations from the registry and creates a pidl.
1731 *
1732 * PARAMS
1733 * hwndOwner [I]
1734 * nFolder [I] CSIDL_xxxxx
1735 * hToken [I] token representing user, or NULL for current user, or -1 for
1736 * default user
1737 * dwReserved [I] must be zero
1738 * ppidl [O] PIDL of a special folder
1739 *
1740 * RETURNS
1741 * Success: S_OK
1742 * Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
1743 *
1744 * NOTES
1745 * Creates missing reg keys and directories.
1746 * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
1747 * virtual folders that are handled here.
1748 */
1749 HRESULT WINAPI SHGetFolderLocation(
1750 HWND hwndOwner,
1751 int nFolder,
1752 HANDLE hToken,
1753 DWORD dwReserved,
1754 LPITEMIDLIST *ppidl)
1755 {
1756 HRESULT hr = E_INVALIDARG;
1757
1758 TRACE("%p 0x%08x %p 0x%08x %p\n",
1759 hwndOwner, nFolder, hToken, dwReserved, ppidl);
1760
1761 if (!ppidl)
1762 return E_INVALIDARG;
1763 if (dwReserved)
1764 return E_INVALIDARG;
1765
1766 /* The virtual folders' locations are not user-dependent */
1767 *ppidl = NULL;
1768 switch (nFolder)
1769 {
1770 case CSIDL_DESKTOP:
1771 *ppidl = _ILCreateDesktop();
1772 break;
1773
1774 case CSIDL_PERSONAL:
1775 *ppidl = _ILCreateMyDocuments();
1776 break;
1777
1778 case CSIDL_INTERNET:
1779 *ppidl = _ILCreateIExplore();
1780 break;
1781
1782 case CSIDL_CONTROLS:
1783 *ppidl = _ILCreateControlPanel();
1784 break;
1785
1786 case CSIDL_PRINTERS:
1787 *ppidl = _ILCreatePrinters();
1788 break;
1789
1790 case CSIDL_BITBUCKET:
1791 *ppidl = _ILCreateBitBucket();
1792 break;
1793
1794 case CSIDL_DRIVES:
1795 *ppidl = _ILCreateMyComputer();
1796 break;
1797
1798 case CSIDL_NETWORK:
1799 *ppidl = _ILCreateNetwork();
1800 break;
1801
1802 default:
1803 {
1804 WCHAR szPath[MAX_PATH];
1805
1806 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
1807 SHGFP_TYPE_CURRENT, szPath);
1808 if (SUCCEEDED(hr))
1809 {
1810 DWORD attributes=0;
1811
1812 TRACE("Value=%s\n", debugstr_w(szPath));
1813 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
1814 }
1815 else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
1816 {
1817 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
1818 * version 6.0 returns E_FAIL for nonexistent paths
1819 */
1820 hr = E_FAIL;
1821 }
1822 }
1823 }
1824 if(*ppidl)
1825 hr = S_OK;
1826
1827 TRACE("-- (new pidl %p)\n",*ppidl);
1828 return hr;
1829 }
1830
1831 /*************************************************************************
1832 * SHGetSpecialFolderLocation [SHELL32.@]
1833 *
1834 * NOTES
1835 * In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
1836 * directory.
1837 */
1838 HRESULT WINAPI SHGetSpecialFolderLocation(
1839 HWND hwndOwner,
1840 INT nFolder,
1841 LPITEMIDLIST * ppidl)
1842 {
1843 HRESULT hr = E_INVALIDARG;
1844
1845 TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
1846
1847 if (!ppidl)
1848 return E_INVALIDARG;
1849
1850 hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
1851 return hr;
1852 }