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