Fix freeing the allocated string SID
[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 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
913
914 /* Gets the value named value from the registry key
915 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
916 * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
917 * is assumed to be MAX_PATH WCHARs in length.
918 * If it exists, expands the value and writes the expanded value to
919 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
920 * Returns successful error code if the value was retrieved from the registry,
921 * and a failure otherwise.
922 */
923 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
924 LPCWSTR value, LPWSTR path)
925 {
926 HRESULT hr;
927 WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
928 LPCWSTR pShellFolderPath, pUserShellFolderPath;
929 DWORD dwDisp, dwType, dwPathLen = MAX_PATH;
930 HKEY userShellFolderKey, shellFolderKey;
931
932 TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
933 path);
934
935 if (userPrefix)
936 {
937 wcscpy(shellFolderPath, userPrefix);
938 PathAddBackslashW(shellFolderPath);
939 wcscat(shellFolderPath, szSHFolders);
940 pShellFolderPath = shellFolderPath;
941 wcscpy(userShellFolderPath, userPrefix);
942 PathAddBackslashW(userShellFolderPath);
943 wcscat(userShellFolderPath, szSHUserFolders);
944 pUserShellFolderPath = userShellFolderPath;
945 }
946 else
947 {
948 pUserShellFolderPath = szSHUserFolders;
949 pShellFolderPath = szSHFolders;
950 }
951
952 if (RegCreateKeyExW(rootKey, pShellFolderPath, 0, NULL, 0, KEY_ALL_ACCESS,
953 NULL, &shellFolderKey, &dwDisp))
954 {
955 TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
956 return E_FAIL;
957 }
958 if (RegCreateKeyExW(rootKey, pUserShellFolderPath, 0, NULL, 0,
959 KEY_ALL_ACCESS, NULL, &userShellFolderKey, &dwDisp))
960 {
961 TRACE("Failed to create %s\n",
962 debugstr_w(pUserShellFolderPath));
963 RegCloseKey(shellFolderKey);
964 return E_FAIL;
965 }
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 path[dwPathLen / sizeof(WCHAR)] = '\0';
973 if (dwType == REG_EXPAND_SZ && path[0] == '%')
974 {
975 WCHAR szTemp[MAX_PATH];
976
977 _SHExpandEnvironmentStrings(path, szTemp);
978 lstrcpynW(path, szTemp, MAX_PATH);
979 }
980 ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
981 (wcslen(path) + 1) * sizeof(WCHAR));
982 if (ret != ERROR_SUCCESS)
983 hr = HRESULT_FROM_WIN32(ret);
984 else
985 hr = S_OK;
986 }
987 else
988 hr = E_FAIL;
989 RegCloseKey(shellFolderKey);
990 RegCloseKey(userShellFolderKey);
991 TRACE("returning 0x%08x\n", hr);
992 return hr;
993 }
994
995 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
996 * pszPath, based on the entries in CSIDL_Data. By semi-expanded, I mean:
997 * - The entry's szDefaultPath may be either a string value or an integer
998 * resource identifier. In the latter case, the string value of the resource
999 * is written.
1000 * - Depending on the entry's type, the path may begin with an (unexpanded)
1001 * environment variable name. The caller is responsible for expanding
1002 * environment strings if so desired.
1003 * The types that are prepended with environment variables are:
1004 * CSIDL_Type_User: %USERPROFILE%
1005 * CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
1006 * CSIDL_Type_CurrVer: %SystemDrive%
1007 * (Others might make sense too, but as yet are unneeded.)
1008 */
1009 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
1010 {
1011 HRESULT hr;
1012 WCHAR resourcePath[MAX_PATH];
1013 LPCWSTR pDefaultPath = NULL;
1014
1015 TRACE("0x%02x,%p\n", folder, pszPath);
1016
1017 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1018 return E_INVALIDARG;
1019 if (!pszPath)
1020 return E_INVALIDARG;
1021
1022 if (CSIDL_Data[folder].szDefaultPath &&
1023 IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
1024 {
1025 if (LoadStringW(shell32_hInstance,
1026 LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
1027 {
1028 hr = S_OK;
1029 pDefaultPath = resourcePath;
1030 }
1031 else
1032 {
1033 FIXME("(%d,%s), LoadString failed, missing translation?\n", folder,
1034 debugstr_w(pszPath));
1035 hr = E_FAIL;
1036 }
1037 }
1038 else
1039 {
1040 hr = S_OK;
1041 pDefaultPath = CSIDL_Data[folder].szDefaultPath;
1042 }
1043 if (SUCCEEDED(hr))
1044 {
1045 switch (CSIDL_Data[folder].type)
1046 {
1047 case CSIDL_Type_User:
1048 wcscpy(pszPath, UserProfileW);
1049 break;
1050 case CSIDL_Type_AllUsers:
1051 wcscpy(pszPath, AllUsersProfileW);
1052 break;
1053 case CSIDL_Type_CurrVer:
1054 wcscpy(pszPath, SystemDriveW);
1055 break;
1056 default:
1057 ; /* no corresponding env. var, do nothing */
1058 }
1059 if (pDefaultPath)
1060 {
1061 PathAddBackslashW(pszPath);
1062 wcscat(pszPath, pDefaultPath);
1063 }
1064 }
1065 TRACE("returning 0x%08x\n", hr);
1066 return hr;
1067 }
1068
1069 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
1070 * The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value
1071 * can be overridden in the HKLM\\szCurrentVersion key.
1072 * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
1073 * the registry, uses _SHGetDefaultValue to get the value.
1074 */
1075 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
1076 LPWSTR pszPath)
1077 {
1078 HRESULT hr;
1079
1080 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1081
1082 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1083 return E_INVALIDARG;
1084 if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
1085 return E_INVALIDARG;
1086 if (!pszPath)
1087 return E_INVALIDARG;
1088
1089 if (dwFlags & SHGFP_TYPE_DEFAULT)
1090 hr = _SHGetDefaultValue(folder, pszPath);
1091 else
1092 {
1093 HKEY hKey;
1094 DWORD dwDisp;
1095
1096 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szCurrentVersion, 0,
1097 NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp))
1098 hr = E_FAIL;
1099 else
1100 {
1101 DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
1102
1103 if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
1104 &dwType, (LPBYTE)pszPath, &dwPathLen) ||
1105 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
1106 {
1107 hr = _SHGetDefaultValue(folder, pszPath);
1108 dwType = REG_EXPAND_SZ;
1109 RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
1110 (LPBYTE)pszPath, (wcslen(pszPath)+1)*sizeof(WCHAR));
1111 }
1112 else
1113 {
1114 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
1115 hr = S_OK;
1116 }
1117 RegCloseKey(hKey);
1118 }
1119 }
1120 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1121 return hr;
1122 }
1123
1124 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
1125 * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise
1126 * calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken:
1127 * - if hToken is -1, looks in HKEY_USERS\.Default
1128 * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
1129 * if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally
1130 * calls _SHGetDefaultValue for it.
1131 */
1132 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
1133 LPWSTR pszPath)
1134 {
1135 HRESULT hr;
1136
1137 TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
1138
1139 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1140 return E_INVALIDARG;
1141
1142 if (CSIDL_Data[folder].type != CSIDL_Type_User)
1143 return E_INVALIDARG;
1144
1145 if (!pszPath)
1146 return E_INVALIDARG;
1147
1148 if (dwFlags & SHGFP_TYPE_DEFAULT)
1149 {
1150 hr = _SHGetDefaultValue(folder, pszPath);
1151 }
1152 else
1153 {
1154 LPWSTR userPrefix;
1155 HKEY hRootKey;
1156
1157 if (hToken == (HANDLE)-1)
1158 {
1159 /* Get the folder of the default user */
1160 hRootKey = HKEY_USERS;
1161 userPrefix = (LPWSTR)DefaultW;
1162 }
1163 else if(!hToken)
1164 {
1165 /* Get the folder of the current user */
1166 hRootKey = HKEY_CURRENT_USER;
1167 userPrefix = NULL;
1168 }
1169 else
1170 {
1171 /* Get the folder of the specified user */
1172 DWORD InfoLength;
1173 PTOKEN_USER UserInfo;
1174
1175 hRootKey = HKEY_USERS;
1176
1177 GetTokenInformation(hToken, TokenUser, NULL, 0, &InfoLength);
1178 UserInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, InfoLength);
1179
1180 if(!GetTokenInformation(hToken, TokenUser, UserInfo, InfoLength, &InfoLength))
1181 {
1182 WARN("GetTokenInformation failed for %x!\n", hToken);
1183 HeapFree(GetProcessHeap(), 0, UserInfo);
1184 return E_FAIL;
1185 }
1186
1187 if(!ConvertSidToStringSidW(UserInfo->User.Sid, &userPrefix))
1188 {
1189 WARN("ConvertSidToStringSidW failed for %x!\n", hToken);
1190 HeapFree(GetProcessHeap(), 0, UserInfo);
1191 return E_FAIL;
1192 }
1193
1194 HeapFree(GetProcessHeap(), 0, UserInfo);
1195 }
1196
1197 hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, CSIDL_Data[folder].szValueName, pszPath);
1198
1199 /* Free the memory allocated by ConvertSidToStringSidW */
1200 if(hToken && hToken != (HANDLE)-1)
1201 LocalFree(userPrefix);
1202
1203 if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
1204 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, CSIDL_Data[folder].szValueName, pszPath);
1205
1206 if (FAILED(hr))
1207 hr = _SHGetDefaultValue(folder, pszPath);
1208 }
1209
1210 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1211 return hr;
1212 }
1213
1214 /* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has
1215 * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls
1216 * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
1217 * If this fails, falls back to _SHGetDefaultValue.
1218 */
1219 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
1220 LPWSTR pszPath)
1221 {
1222 HRESULT hr;
1223
1224 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1225
1226 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1227 return E_INVALIDARG;
1228 if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
1229 return E_INVALIDARG;
1230 if (!pszPath)
1231 return E_INVALIDARG;
1232
1233 if (dwFlags & SHGFP_TYPE_DEFAULT)
1234 hr = _SHGetDefaultValue(folder, pszPath);
1235 else
1236 {
1237 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1238 CSIDL_Data[folder].szValueName, pszPath);
1239 if (FAILED(hr))
1240 hr = _SHGetDefaultValue(folder, pszPath);
1241 }
1242 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1243 return hr;
1244 }
1245
1246 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
1247 {
1248 LONG lRet;
1249 DWORD disp;
1250
1251 lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, NULL, 0,
1252 KEY_ALL_ACCESS, NULL, pKey, &disp);
1253 return HRESULT_FROM_WIN32(lRet);
1254 }
1255
1256 /* Reads the value named szValueName from the key profilesKey (assumed to be
1257 * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
1258 * WCHARs in length. If it doesn't exist, returns szDefault (and saves
1259 * szDefault to the registry).
1260 */
1261 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
1262 LPWSTR szValue, LPCWSTR szDefault)
1263 {
1264 HRESULT hr;
1265 DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
1266 LONG lRet;
1267
1268 TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
1269 debugstr_w(szDefault));
1270 lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
1271 (LPBYTE)szValue, &dwPathLen);
1272 if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
1273 && *szValue)
1274 {
1275 dwPathLen /= sizeof(WCHAR);
1276 szValue[dwPathLen] = '\0';
1277 hr = S_OK;
1278 }
1279 else
1280 {
1281 /* Missing or invalid value, set a default */
1282 lstrcpynW(szValue, szDefault, MAX_PATH);
1283 TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
1284 debugstr_w(szValue));
1285 lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
1286 (LPBYTE)szValue,
1287 (wcslen(szValue) + 1) * sizeof(WCHAR));
1288 if (lRet)
1289 hr = HRESULT_FROM_WIN32(lRet);
1290 else
1291 hr = S_OK;
1292 }
1293 TRACE("returning 0x%08x (output value is %s)\n", hr, debugstr_w(szValue));
1294 return hr;
1295 }
1296
1297 /* Attempts to expand environment variables from szSrc into szDest, which is
1298 * assumed to be MAX_PATH characters in length. Before referring to the
1299 * environment, handles a few variables directly, because the environment
1300 * variables may not be set when this is called (as during Wine's installation
1301 * when default values are being written to the registry).
1302 * The directly handled environment variables, and their source, are:
1303 * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
1304 * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
1305 * path
1306 * If one of the directly handled environment variables is expanded, only
1307 * expands a single variable, and only in the beginning of szSrc.
1308 */
1309 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
1310 {
1311 HRESULT hr;
1312 WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
1313 HKEY key = NULL;
1314
1315 TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
1316
1317 if (!szSrc || !szDest) return E_INVALIDARG;
1318
1319 /* short-circuit if there's nothing to expand */
1320 if (szSrc[0] != '%')
1321 {
1322 wcscpy(szDest, szSrc);
1323 hr = S_OK;
1324 goto end;
1325 }
1326 /* Get the profile prefix, we'll probably be needing it */
1327 hr = _SHOpenProfilesKey(&key);
1328 if (SUCCEEDED(hr))
1329 {
1330 WCHAR szDefaultProfilesPrefix[MAX_PATH];
1331
1332 GetWindowsDirectoryW(szDefaultProfilesPrefix, MAX_PATH);
1333 PathAddBackslashW(szDefaultProfilesPrefix);
1334 PathAppendW(szDefaultProfilesPrefix, szDefaultProfileDirW);
1335 hr = _SHGetProfilesValue(key, ProfilesDirectoryW, szProfilesPrefix,
1336 szDefaultProfilesPrefix);
1337 }
1338
1339 *szDest = 0;
1340 wcscpy(szTemp, szSrc);
1341 while (SUCCEEDED(hr) && szTemp[0] == '%')
1342 {
1343 if (!strncmpiW(szTemp, AllUsersProfileW, wcslen(AllUsersProfileW)))
1344 {
1345 WCHAR szAllUsers[MAX_PATH];
1346
1347 wcscpy(szDest, szProfilesPrefix);
1348 hr = _SHGetProfilesValue(key, AllUsersProfileValueW,
1349 szAllUsers, AllUsersW);
1350 PathAppendW(szDest, szAllUsers);
1351 PathAppendW(szDest, szTemp + wcslen(AllUsersProfileW));
1352 }
1353 else if (!strncmpiW(szTemp, UserProfileW, wcslen(UserProfileW)))
1354 {
1355 WCHAR userName[MAX_PATH];
1356 DWORD userLen = MAX_PATH;
1357
1358 wcscpy(szDest, szProfilesPrefix);
1359 GetUserNameW(userName, &userLen);
1360 PathAppendW(szDest, userName);
1361 PathAppendW(szDest, szTemp + wcslen(UserProfileW));
1362 }
1363 else if (!strncmpiW(szTemp, SystemDriveW, wcslen(SystemDriveW)))
1364 {
1365 GetSystemDirectoryW(szDest, MAX_PATH);
1366 if (szDest[1] != ':')
1367 {
1368 FIXME("non-drive system paths unsupported\n");
1369 hr = E_FAIL;
1370 }
1371 else
1372 {
1373 wcscpy(szDest + 3, szTemp + wcslen(SystemDriveW) + 1);
1374 hr = S_OK;
1375 }
1376 }
1377 else
1378 {
1379 DWORD ret = ExpandEnvironmentStringsW(szSrc, szDest, MAX_PATH);
1380
1381 if (ret > MAX_PATH)
1382 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1383 else if (ret == 0)
1384 hr = HRESULT_FROM_WIN32(GetLastError());
1385 else
1386 hr = S_OK;
1387 }
1388 if (SUCCEEDED(hr) && szDest[0] == '%')
1389 wcscpy(szTemp, szDest);
1390 else
1391 {
1392 /* terminate loop */
1393 szTemp[0] = '\0';
1394 }
1395 }
1396 end:
1397 if (key)
1398 RegCloseKey(key);
1399 TRACE("returning 0x%08x (input was %s, output is %s)\n", hr,
1400 debugstr_w(szSrc), debugstr_w(szDest));
1401 return hr;
1402 }
1403
1404 /*************************************************************************
1405 * SHGetFolderPathW [SHELL32.@]
1406 *
1407 * Convert nFolder to path.
1408 *
1409 * RETURNS
1410 * Success: S_OK
1411 * Failure: standard HRESULT error codes.
1412 *
1413 * NOTES
1414 * Most values can be overridden in either
1415 * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1416 * or in the same location in HKLM.
1417 * The "Shell Folders" registry key was used in NT4 and earlier systems.
1418 * Beginning with Windows 2000, the "User Shell Folders" key is used, so
1419 * changes made to it are made to the former key too. This synchronization is
1420 * done on-demand: not until someone requests the value of one of these paths
1421 * (by calling one of the SHGet functions) is the value synchronized.
1422 * Furthermore, the HKCU paths take precedence over the HKLM paths.
1423 */
1424 HRESULT WINAPI SHGetFolderPathW(
1425 HWND hwndOwner, /* [I] owner window */
1426 int nFolder, /* [I] CSIDL identifying the folder */
1427 HANDLE hToken, /* [I] access token */
1428 DWORD dwFlags, /* [I] which path to return */
1429 LPWSTR pszPath) /* [O] converted path */
1430 {
1431 HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
1432 if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
1433 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1434 return hr;
1435 }
1436
1437 HRESULT WINAPI SHGetFolderPathAndSubDirA(
1438 HWND hwndOwner, /* [I] owner window */
1439 int nFolder, /* [I] CSIDL identifying the folder */
1440 HANDLE hToken, /* [I] access token */
1441 DWORD dwFlags, /* [I] which path to return */
1442 LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
1443 LPSTR pszPath) /* [O] converted path */
1444 {
1445 int length;
1446 HRESULT hr = S_OK;
1447 LPWSTR pszSubPathW = NULL;
1448 LPWSTR pszPathW = NULL;
1449 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1450
1451 if(pszPath) {
1452 pszPathW = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
1453 if(!pszPathW) {
1454 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1455 goto cleanup;
1456 }
1457 }
1458 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1459
1460 /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
1461 * set (null), or an empty string.therefore call it without the parameter set
1462 * if pszSubPath is an empty string
1463 */
1464 if (pszSubPath && pszSubPath[0]) {
1465 length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
1466 pszSubPathW = HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR));
1467 if(!pszSubPathW) {
1468 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1469 goto cleanup;
1470 }
1471 MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
1472 }
1473
1474 hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
1475
1476 if (SUCCEEDED(hr) && pszPath)
1477 WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
1478
1479 cleanup:
1480 HeapFree(GetProcessHeap(), 0, pszPathW);
1481 HeapFree(GetProcessHeap(), 0, pszSubPathW);
1482 return hr;
1483 }
1484
1485 HRESULT WINAPI SHGetFolderPathAndSubDirW(
1486 HWND hwndOwner, /* [I] owner window */
1487 int nFolder, /* [I] CSIDL identifying the folder */
1488 HANDLE hToken, /* [I] access token */
1489 DWORD dwFlags, /* [I] which path to return */
1490 LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
1491 LPWSTR pszPath) /* [O] converted path */
1492 {
1493 HRESULT hr;
1494 WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
1495 DWORD folder = nFolder & CSIDL_FOLDER_MASK; //FIXME
1496 CSIDL_Type type;
1497 int ret;
1498
1499 TRACE("%p,%p,nFolder=0x%04x,%s\n", hwndOwner,pszPath,nFolder,debugstr_w(pszSubPath));
1500
1501 /* Windows always NULL-terminates the resulting path regardless of success
1502 * or failure, so do so first
1503 */
1504 if (pszPath)
1505 *pszPath = '\0';
1506
1507 if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1508 return E_INVALIDARG;
1509 if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
1510 return E_INVALIDARG;
1511 szTemp[0] = 0;
1512 type = CSIDL_Data[folder].type;
1513 switch (type)
1514 {
1515 case CSIDL_Type_Disallowed:
1516 hr = E_INVALIDARG;
1517 break;
1518 case CSIDL_Type_NonExistent:
1519 hr = S_FALSE;
1520 break;
1521 case CSIDL_Type_WindowsPath:
1522 GetWindowsDirectoryW(szTemp, MAX_PATH);
1523 if (CSIDL_Data[folder].szDefaultPath &&
1524 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1525 *CSIDL_Data[folder].szDefaultPath)
1526 {
1527 PathAddBackslashW(szTemp);
1528 wcscat(szTemp, CSIDL_Data[folder].szDefaultPath);
1529 }
1530 hr = S_OK;
1531 break;
1532 case CSIDL_Type_SystemPath:
1533 GetSystemDirectoryW(szTemp, MAX_PATH);
1534 if (CSIDL_Data[folder].szDefaultPath &&
1535 !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1536 *CSIDL_Data[folder].szDefaultPath)
1537 {
1538 PathAddBackslashW(szTemp);
1539 wcscat(szTemp, CSIDL_Data[folder].szDefaultPath);
1540 }
1541 hr = S_OK;
1542 break;
1543 case CSIDL_Type_CurrVer:
1544 hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
1545 break;
1546 case CSIDL_Type_User:
1547 hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
1548 break;
1549 case CSIDL_Type_AllUsers:
1550 hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
1551 break;
1552 default:
1553 FIXME("bogus type %d, please fix\n", type);
1554 hr = E_INVALIDARG;
1555 break;
1556 }
1557
1558 /* Expand environment strings if necessary */
1559 if (*szTemp == '%')
1560 hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
1561 else
1562 wcscpy(szBuildPath, szTemp);
1563
1564 if (FAILED(hr)) goto end;
1565
1566 if(pszSubPath) {
1567 /* make sure the new path does not exceed th bufferlength
1568 * rememebr to backslash and the termination */
1569 if(MAX_PATH < (wcslen(szBuildPath) + wcslen(pszSubPath) + 2)) {
1570 hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
1571 goto end;
1572 }
1573 PathAppendW(szBuildPath, pszSubPath);
1574 PathRemoveBackslashW(szBuildPath);
1575 }
1576 /* Copy the path if it's available before we might return */
1577 if (SUCCEEDED(hr) && pszPath)
1578 wcscpy(pszPath, szBuildPath);
1579
1580 /* if we don't care about existing directories we are ready */
1581 if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
1582
1583 if (PathFileExistsW(szBuildPath)) goto end;
1584
1585 /* not existing but we are not allowed to create it. The return value
1586 * is verified against shell32 version 6.0.
1587 */
1588 if (!(nFolder & CSIDL_FLAG_CREATE))
1589 {
1590 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
1591 goto end;
1592 }
1593
1594 /* create directory/directories */
1595 ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
1596 if (ret && ret != ERROR_ALREADY_EXISTS)
1597 {
1598 ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
1599 hr = E_FAIL;
1600 goto end;
1601 }
1602
1603 TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
1604 end:
1605 TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
1606 return hr;
1607 }
1608
1609 /*************************************************************************
1610 * SHGetFolderPathA [SHELL32.@]
1611 *
1612 * See SHGetFolderPathW.
1613 */
1614 HRESULT WINAPI SHGetFolderPathA(
1615 HWND hwndOwner,
1616 int nFolder,
1617 HANDLE hToken,
1618 DWORD dwFlags,
1619 LPSTR pszPath)
1620 {
1621 WCHAR szTemp[MAX_PATH];
1622 HRESULT hr;
1623
1624 TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
1625
1626 if (pszPath)
1627 *pszPath = '\0';
1628 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
1629 if (SUCCEEDED(hr) && pszPath)
1630 WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
1631 NULL);
1632
1633 return hr;
1634 }
1635
1636 /* For each folder in folders, if its value has not been set in the registry,
1637 * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
1638 * folder's type) to get the unexpanded value first.
1639 * Writes the unexpanded value to User Shell Folders, and queries it with
1640 * SHGetFolderPathW to force the creation of the directory if it doesn't
1641 * already exist. SHGetFolderPathW also returns the expanded value, which
1642 * this then writes to Shell Folders.
1643 */
1644 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
1645 LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
1646 UINT foldersLen)
1647 {
1648 UINT i;
1649 WCHAR path[MAX_PATH];
1650 HRESULT hr = S_OK;
1651 HKEY hUserKey = NULL, hKey = NULL;
1652 DWORD dwDisp, dwType, dwPathLen;
1653 LONG ret;
1654
1655 TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
1656 debugstr_w(szUserShellFolderPath), folders, foldersLen);
1657
1658 ret = RegCreateKeyExW(hRootKey, szUserShellFolderPath, 0, NULL, 0,
1659 KEY_ALL_ACCESS, NULL, &hUserKey, &dwDisp);
1660 if (ret)
1661 hr = HRESULT_FROM_WIN32(ret);
1662 else
1663 {
1664 ret = RegCreateKeyExW(hRootKey, szShellFolderPath, 0, NULL, 0,
1665 KEY_ALL_ACCESS, NULL, &hKey, &dwDisp);
1666 if (ret)
1667 hr = HRESULT_FROM_WIN32(ret);
1668 }
1669 for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
1670 {
1671 dwPathLen = MAX_PATH * sizeof(WCHAR);
1672 if (RegQueryValueExW(hUserKey, CSIDL_Data[folders[i]].szValueName, NULL,
1673 &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
1674 dwType != REG_EXPAND_SZ))
1675 {
1676 *path = '\0';
1677 if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
1678 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
1679 path);
1680 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
1681 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
1682 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
1683 GetWindowsDirectoryW(path, MAX_PATH);
1684 else
1685 hr = E_FAIL;
1686 if (*path)
1687 {
1688 ret = RegSetValueExW(hUserKey,
1689 CSIDL_Data[folders[i]].szValueName, 0, REG_EXPAND_SZ,
1690 (LPBYTE)path, (wcslen(path) + 1) * sizeof(WCHAR));
1691 if (ret)
1692 hr = HRESULT_FROM_WIN32(ret);
1693 else
1694 {
1695 hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
1696 hToken, SHGFP_TYPE_DEFAULT, path);
1697 ret = RegSetValueExW(hKey,
1698 CSIDL_Data[folders[i]].szValueName, 0, REG_SZ,
1699 (LPBYTE)path, (wcslen(path) + 1) * sizeof(WCHAR));
1700 if (ret)
1701 hr = HRESULT_FROM_WIN32(ret);
1702 }
1703 }
1704 }
1705 }
1706 if (hUserKey)
1707 RegCloseKey(hUserKey);
1708 if (hKey)
1709 RegCloseKey(hKey);
1710
1711 TRACE("returning 0x%08x\n", hr);
1712 return hr;
1713 }
1714
1715 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
1716 {
1717 static const UINT folders[] = {
1718 CSIDL_PROGRAMS,
1719 CSIDL_PERSONAL,
1720 CSIDL_FAVORITES,
1721 CSIDL_APPDATA,
1722 CSIDL_STARTUP,
1723 CSIDL_RECENT,
1724 CSIDL_SENDTO,
1725 CSIDL_STARTMENU,
1726 CSIDL_MYMUSIC,
1727 CSIDL_MYVIDEO,
1728 CSIDL_DESKTOPDIRECTORY,
1729 CSIDL_NETHOOD,
1730 CSIDL_TEMPLATES,
1731 CSIDL_PRINTHOOD,
1732 CSIDL_LOCAL_APPDATA,
1733 CSIDL_INTERNET_CACHE,
1734 CSIDL_COOKIES,
1735 CSIDL_HISTORY,
1736 CSIDL_MYPICTURES,
1737 CSIDL_FONTS
1738 };
1739 WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
1740 LPCWSTR pUserShellFolderPath, pShellFolderPath;
1741 HRESULT hr = S_OK;
1742 HKEY hRootKey;
1743 HANDLE hToken;
1744
1745 TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
1746 if (bDefault)
1747 {
1748 hToken = (HANDLE)-1;
1749 hRootKey = HKEY_USERS;
1750 wcscpy(userShellFolderPath, DefaultW);
1751 PathAddBackslashW(userShellFolderPath);
1752 wcscat(userShellFolderPath, szSHUserFolders);
1753 pUserShellFolderPath = userShellFolderPath;
1754 wcscpy(shellFolderPath, DefaultW);
1755 PathAddBackslashW(shellFolderPath);
1756 wcscat(shellFolderPath, szSHFolders);
1757 pShellFolderPath = shellFolderPath;
1758 }
1759 else
1760 {
1761 hToken = NULL;
1762 hRootKey = HKEY_CURRENT_USER;
1763 pUserShellFolderPath = szSHUserFolders;
1764 pShellFolderPath = szSHFolders;
1765 }
1766
1767 hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
1768 pShellFolderPath, folders, sizeof(folders) / sizeof(folders[0]));
1769 TRACE("returning 0x%08x\n", hr);
1770 return hr;
1771 }
1772
1773 static HRESULT _SHRegisterCommonShellFolders(void)
1774 {
1775 static const UINT folders[] = {
1776 CSIDL_COMMON_STARTMENU,
1777 CSIDL_COMMON_PROGRAMS,
1778 CSIDL_COMMON_STARTUP,
1779 CSIDL_COMMON_DESKTOPDIRECTORY,
1780 CSIDL_COMMON_FAVORITES,
1781 CSIDL_COMMON_APPDATA,
1782 CSIDL_COMMON_TEMPLATES,
1783 CSIDL_COMMON_DOCUMENTS,
1784 };
1785 HRESULT hr;
1786
1787 TRACE("\n");
1788 hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
1789 szSHFolders, folders, sizeof(folders) / sizeof(folders[0]));
1790 TRACE("returning 0x%08x\n", hr);
1791 return hr;
1792 }
1793
1794 /******************************************************************************
1795 * _SHAppendToUnixPath [Internal]
1796 *
1797 * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the
1798 * corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath'
1799 * and replaces backslashes with slashes.
1800 *
1801 * PARAMS
1802 * szBasePath [IO] The unix base path, which will be appended to (CP_UNXICP).
1803 * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW).
1804 *
1805 * RETURNS
1806 * Success: TRUE,
1807 * Failure: FALSE
1808 */
1809 static BOOL __inline _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) {
1810 WCHAR wszSubPath[MAX_PATH];
1811 int cLen = strlen(szBasePath);
1812 char *pBackslash;
1813
1814 if (IS_INTRESOURCE(pwszSubPath)) {
1815 if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) {
1816 /* Fall back to hard coded defaults. */
1817 switch (LOWORD(pwszSubPath)) {
1818 case IDS_PERSONAL:
1819 wcscpy(wszSubPath, PersonalW);
1820 break;
1821 case IDS_MYMUSIC:
1822 wcscpy(wszSubPath, My_MusicW);
1823 break;
1824 case IDS_MYPICTURES:
1825 wcscpy(wszSubPath, My_PicturesW);
1826 break;
1827 case IDS_MYVIDEO:
1828 wcscpy(wszSubPath, My_VideoW);
1829 break;
1830 default:
1831 ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath));
1832 return FALSE;
1833 }
1834 }
1835 } else {
1836 wcscpy(wszSubPath, pwszSubPath);
1837 }
1838
1839 if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
1840
1841 if (!WideCharToMultiByte(CP_ACP, 0, wszSubPath, -1, szBasePath + cLen,
1842 FILENAME_MAX - cLen, NULL, NULL))
1843 {
1844 return FALSE;
1845 }
1846
1847 pBackslash = szBasePath + cLen;
1848 while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
1849
1850 return TRUE;
1851 }
1852 #if 0
1853 /******************************************************************************
1854 * _SHCreateSymbolicLinks [Internal]
1855 *
1856 * Sets up symbol links for various shell folders to point into the users home
1857 * directory. We do an educated guess about what the user would probably want:
1858 * - If there is a 'My Documents' directory in $HOME, the user probably wants
1859 * wine's 'My Documents' to point there. Furthermore, we imply that the user
1860 * is a Windows lover and has no problem with wine creating 'My Pictures',
1861 * 'My Music' and 'My Video' subfolders under '$HOME/My Documents', if those
1862 * do not already exits. We put appropriate symbolic links in place for those,
1863 * too.
1864 * - If there is no 'My Documents' directory in $HOME, we let 'My Documents'
1865 * point directly to $HOME. We assume the user to be a unix hacker who does not
1866 * want wine to create anything anywhere besides the .wine directory. So, if
1867 * there already is a 'My Music' directory in $HOME, we symlink the 'My Music'
1868 * shell folder to it. But if not, we symlink it to $HOME directly. The same
1869 * holds fo 'My Pictures' and 'My Video'.
1870 * - The Desktop shell folder is symlinked to '$HOME/Desktop', if that does
1871 * exists and left alone if not.
1872 * ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
1873 */
1874 static void _SHCreateSymbolicLinks(void)
1875 {
1876 UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEO, IDS_MYMUSIC }, i;
1877 int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC };
1878 WCHAR wszTempPath[MAX_PATH];
1879 char szPersonalTarget[FILENAME_MAX], *pszPersonal;
1880 char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
1881 char szDesktopTarget[FILENAME_MAX], *pszDesktop;
1882 struct stat statFolder;
1883 const char *pszHome;
1884 HRESULT hr;
1885
1886 /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
1887 hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
1888 SHGFP_TYPE_DEFAULT, wszTempPath);
1889 if (FAILED(hr)) return;
1890 pszPersonal = wine_get_unix_file_name(wszTempPath);
1891 if (!pszPersonal) return;
1892
1893 pszHome = getenv("HOME");
1894 if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode)) {
1895 strcpy(szPersonalTarget, pszHome);
1896 if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) &&
1897 !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
1898 {
1899 /* '$HOME/My Documents' exists. Create 'My Pictures', 'My Videos' and
1900 * 'My Music' subfolders or fail silently if they already exist. */
1901 for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
1902 strcpy(szMyStuffTarget, szPersonalTarget);
1903 if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
1904 mkdir(szMyStuffTarget);
1905 }
1906 }
1907 else
1908 {
1909 /* '$HOME/My Documents' doesn't exists, but '$HOME' does. */
1910 strcpy(szPersonalTarget, pszHome);
1911 }
1912
1913 /* Replace 'My Documents' directory with a symlink of fail silently if not empty. */
1914 rmdir(pszPersonal);
1915 symlink(szPersonalTarget, pszPersonal);
1916 }
1917 else
1918 {
1919 /* '$HOME' doesn't exist. Create 'My Pictures', 'My Videos' and 'My Music' subdirs
1920 * in '%USERPROFILE%\\My Documents' or fail silently if they already exist. */
1921 strcpy(szPersonalTarget, pszPersonal);
1922 for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
1923 strcpy(szMyStuffTarget, szPersonalTarget);
1924 if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
1925 mkdir(szMyStuffTarget);
1926 }
1927 }
1928
1929 /* Create symbolic links for 'My Pictures', 'My Video' and 'My Music'. */
1930 for (i=0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
1931 /* Create the current 'My Whatever' folder and get it's unix path. */
1932 hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL,
1933 SHGFP_TYPE_DEFAULT, wszTempPath);
1934 if (FAILED(hr)) continue;
1935 pszMyStuff = wine_get_unix_file_name(wszTempPath);
1936 if (!pszMyStuff) continue;
1937
1938 strcpy(szMyStuffTarget, szPersonalTarget);
1939 if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
1940 !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
1941 {
1942 /* If there's a 'My Whatever' directory where 'My Documents' links to, link to it. */
1943 rmdir(pszMyStuff);
1944 symlink(szMyStuffTarget, pszMyStuff);
1945 }
1946 else
1947 {
1948 /* Else link to where 'My Documents' itself links to. */
1949 rmdir(pszMyStuff);
1950 symlink(szPersonalTarget, pszMyStuff);
1951 }
1952 HeapFree(GetProcessHeap(), 0, pszMyStuff);
1953 }
1954
1955 /* Last but not least, the Desktop folder */
1956 if (pszHome)
1957 strcpy(szDesktopTarget, pszHome);
1958 else
1959 strcpy(szDesktopTarget, pszPersonal);
1960 HeapFree(GetProcessHeap(), 0, pszPersonal);
1961
1962 if (_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
1963 !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
1964 {
1965 hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL,
1966 SHGFP_TYPE_DEFAULT, wszTempPath);
1967 if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath)))
1968 {
1969 rmdir(pszDesktop);
1970 symlink(szDesktopTarget, pszDesktop);
1971 HeapFree(GetProcessHeap(), 0, pszDesktop);
1972 }
1973 }
1974 }
1975 #endif
1976
1977 /* Register the default values in the registry, as some apps seem to depend
1978 * on their presence. The set registered was taken from Windows XP.
1979 */
1980 HRESULT SHELL_RegisterShellFolders(void)
1981 {
1982 HRESULT hr;
1983
1984 /* Set up '$HOME' targeted symlinks for 'My Documents', 'My Pictures',
1985 * 'My Video', 'My Music' and 'Desktop' in advance, so that the
1986 * _SHRegister*ShellFolders() functions will find everything nice and clean
1987 * and thus will not attempt to create them in the profile directory. */
1988 #if 0
1989 _SHCreateSymbolicLinks();
1990 #endif
1991
1992 hr = _SHRegisterUserShellFolders(TRUE);
1993 if (SUCCEEDED(hr))
1994 hr = _SHRegisterUserShellFolders(FALSE);
1995 if (SUCCEEDED(hr))
1996 hr = _SHRegisterCommonShellFolders();
1997 return hr;
1998 }
1999
2000 /*************************************************************************
2001 * SHGetSpecialFolderPathA [SHELL32.@]
2002 */
2003 BOOL WINAPI SHGetSpecialFolderPathA (
2004 HWND hwndOwner,
2005 LPSTR szPath,
2006 int nFolder,
2007 BOOL bCreate)
2008 {
2009 return (SHGetFolderPathA(
2010 hwndOwner,
2011 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
2012 NULL,
2013 0,
2014 szPath)) == S_OK ? TRUE : FALSE;
2015 }
2016
2017 /*************************************************************************
2018 * SHGetSpecialFolderPathW
2019 */
2020 BOOL WINAPI SHGetSpecialFolderPathW (
2021 HWND hwndOwner,
2022 LPWSTR szPath,
2023 int nFolder,
2024 BOOL bCreate)
2025 {
2026 return (SHGetFolderPathW(
2027 hwndOwner,
2028 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
2029 NULL,
2030 0,
2031 szPath)) == S_OK ? TRUE : FALSE;
2032 }
2033
2034 /*************************************************************************
2035 * SHGetFolderLocation [SHELL32.@]
2036 *
2037 * Gets the folder locations from the registry and creates a pidl.
2038 *
2039 * PARAMS
2040 * hwndOwner [I]
2041 * nFolder [I] CSIDL_xxxxx
2042 * hToken [I] token representing user, or NULL for current user, or -1 for
2043 * default user
2044 * dwReserved [I] must be zero
2045 * ppidl [O] PIDL of a special folder
2046 *
2047 * RETURNS
2048 * Success: S_OK
2049 * Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
2050 *
2051 * NOTES
2052 * Creates missing reg keys and directories.
2053 * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
2054 * virtual folders that are handled here.
2055 */
2056 HRESULT WINAPI SHGetFolderLocation(
2057 HWND hwndOwner,
2058 int nFolder,
2059 HANDLE hToken,
2060 DWORD dwReserved,
2061 LPITEMIDLIST *ppidl)
2062 {
2063 HRESULT hr = E_INVALIDARG;
2064
2065 TRACE("%p 0x%08x %p 0x%08x %p\n",
2066 hwndOwner, nFolder, hToken, dwReserved, ppidl);
2067
2068 if (!ppidl)
2069 return E_INVALIDARG;
2070 if (dwReserved)
2071 return E_INVALIDARG;
2072
2073 /* The virtual folders' locations are not user-dependent */
2074 *ppidl = NULL;
2075 switch (nFolder)
2076 {
2077 case CSIDL_DESKTOP:
2078 *ppidl = _ILCreateDesktop();
2079 break;
2080
2081 case CSIDL_PERSONAL:
2082 *ppidl = _ILCreateMyDocuments();
2083 break;
2084
2085 case CSIDL_INTERNET:
2086 *ppidl = _ILCreateIExplore();
2087 break;
2088
2089 case CSIDL_CONTROLS:
2090 *ppidl = _ILCreateControlPanel();
2091 break;
2092
2093 case CSIDL_PRINTERS:
2094 *ppidl = _ILCreatePrinters();
2095 break;
2096
2097 case CSIDL_BITBUCKET:
2098 *ppidl = _ILCreateBitBucket();
2099 break;
2100
2101 case CSIDL_DRIVES:
2102 *ppidl = _ILCreateMyComputer();
2103 break;
2104
2105 case CSIDL_NETWORK:
2106 *ppidl = _ILCreateNetwork();
2107 break;
2108
2109 default:
2110 {
2111 WCHAR szPath[MAX_PATH];
2112
2113 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
2114 SHGFP_TYPE_CURRENT, szPath);
2115 if (SUCCEEDED(hr))
2116 {
2117 DWORD attributes=0;
2118
2119 TRACE("Value=%s\n", debugstr_w(szPath));
2120 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
2121 }
2122 else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
2123 {
2124 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
2125 * version 6.0 returns E_FAIL for nonexistent paths
2126 */
2127 hr = E_FAIL;
2128 }
2129 }
2130 }
2131 if(*ppidl)
2132 hr = NOERROR;
2133
2134 TRACE("-- (new pidl %p)\n",*ppidl);
2135 return hr;
2136 }
2137
2138 /*************************************************************************
2139 * SHGetSpecialFolderLocation [SHELL32.@]
2140 *
2141 * NOTES
2142 * In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
2143 * directory.
2144 */
2145 HRESULT WINAPI SHGetSpecialFolderLocation(
2146 HWND hwndOwner,
2147 INT nFolder,
2148 LPITEMIDLIST * ppidl)
2149 {
2150 HRESULT hr = E_INVALIDARG;
2151
2152 TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
2153
2154 if (!ppidl)
2155 return E_INVALIDARG;
2156
2157 hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
2158 return hr;
2159 }