From 9cbc42bbae7bf4807d9620833500803e6e13ef05 Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Thu, 21 Oct 2004 19:59:46 +0000 Subject: [PATCH] - the correct registry location to override is User Shell Folders, not Shell Folders - if User Shell Folders doesn't exist in HKCU, HKLM should be tried - SHGetSpecialFolderPath should call SHGetFolderPath, not vice-versa - the default values should be localizable - some of the parameter checking and returned LPITEMIDLISTs were a bit off --- dlls/shell32/regsvr.c | 3 + dlls/shell32/shell32_En.rc | 32 + dlls/shell32/shell32_main.h | 3 + dlls/shell32/shellpath.c | 1678 ++++++++++++++++++++------------ dlls/shell32/shresdef.h | 28 + dlls/shell32/tests/.cvsignore | 1 + dlls/shell32/tests/Makefile.in | 3 +- dlls/shell32/tests/shellpath.c | 897 +++++++++++++++++ include/winuser.h | 1 + 9 files changed, 2031 insertions(+), 615 deletions(-) create mode 100644 dlls/shell32/tests/shellpath.c diff --git a/dlls/shell32/regsvr.c b/dlls/shell32/regsvr.c index e3f46ddf44c..accf46e5c87 100644 --- a/dlls/shell32/regsvr.c +++ b/dlls/shell32/regsvr.c @@ -29,6 +29,7 @@ #include "ole2.h" #include "shlguid.h" +#include "shell32_main.h" #include "wine/debug.h" @@ -518,6 +519,8 @@ HRESULT WINAPI SHELL32_DllRegisterServer() hr = register_coclasses(coclass_list); if (SUCCEEDED(hr)) hr = register_interfaces(interface_list); + if (SUCCEEDED(hr)) + hr = SHELL_RegisterShellFolders(); return hr; } diff --git a/dlls/shell32/shell32_En.rc b/dlls/shell32/shell32_En.rc index dfcc6059c29..bb641958775 100644 --- a/dlls/shell32/shell32_En.rc +++ b/dlls/shell32/shell32_En.rc @@ -186,3 +186,35 @@ STRINGTABLE DISCARDABLE IDS_SHUTDOWN_TITLE "Shutdown" IDS_SHUTDOWN_PROMPT "Do you want to shutdown your Wine session?" } + +/* shell folder path default values */ +STRINGTABLE DISCARDABLE +{ + IDS_PROGRAMS "Start Menu\\Programs" + IDS_PERSONAL "My Documents" + IDS_FAVORITES "Favorites" + IDS_STARTUP "Start Menu\\Programs\\StartUp" + IDS_RECENT "Recent" + IDS_SENDTO "SendTo" + IDS_STARTMENU "Start Menu" + IDS_MYMUSIC "My Documents\\My Music" + IDS_MYVIDEO "My Documents\\My Video" + IDS_DESKTOPDIRECTORY "Desktop" + IDS_NETHOOD "NetHood" + IDS_TEMPLATES "Templates" + IDS_APPDATA "Application Data" + IDS_PRINTHOOD "PrintHood" + IDS_LOCAL_APPDATA "Local Settings\\Application Data" + IDS_INTERNET_CACHE "Temporary Internet Files" + IDS_COOKIES "Cookies" + IDS_HISTORY "History" + IDS_PROGRAM_FILES "Program Files" + IDS_MYPICTURES "My Documents\\My Pictures" + IDS_PROGRAM_FILES_COMMON "Program Files\\Common Files" + IDS_COMMON_DOCUMENTS "Documents" + IDS_ADMINTOOLS "Start Menu\\Programs\\Administrative Tools" + IDS_COMMON_MUSIC "Documents\\My Music" + IDS_COMMON_PICTURES "Documents\\My Pictures" + IDS_COMMON_VIDEO "Documents\\My Video" + IDS_CDBURN_AREA "Local Settings\\Application Data\\Microsoft\\CD Burning" +} diff --git a/dlls/shell32/shell32_main.h b/dlls/shell32/shell32_main.h index adb1103a13e..37952ec8cad 100644 --- a/dlls/shell32/shell32_main.h +++ b/dlls/shell32/shell32_main.h @@ -224,4 +224,7 @@ UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOperation, extern WCHAR swShell32Name[MAX_PATH]; +/* Default shell folder value registration */ +HRESULT SHELL_RegisterShellFolders(void); + #endif diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c index 186f551f70e..bbddef26f4d 100644 --- a/dlls/shell32/shellpath.c +++ b/dlls/shell32/shellpath.c @@ -2,6 +2,7 @@ * Path Functions * * Copyright 1998, 1999, 2000 Juergen Schmied + * Copyright 2004 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -38,6 +39,7 @@ #include "winuser.h" #include "shlobj.h" +#include "shresdef.h" #include "shell32_main.h" #include "undocshell.h" #include "pidl.h" @@ -689,48 +691,9 @@ VOID WINAPI PathSetDlgItemPathAW(HWND hDlg, int id, LPCVOID pszPath) PathSetDlgItemPathA(hDlg, id, pszPath); } -/************************************************************************* - * SHGetFolderPathW [SHELL32.@] - * - * converts csidl to path - */ - -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'}; -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'}; -static const WCHAR szSetup[] = {'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','e','t','u','p','\0'}; 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'}; - - - 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'}; -static const WCHAR All_Users__Application_DataW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\0'}; -static const WCHAR All_Users__DesktopW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'D','e','s','k','t','o','p','\0'}; -static const WCHAR All_Users__DocumentsW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'D','o','c','u','m','e','n','t','s','\0'}; -static const WCHAR All_Users__Documents__My_MusicW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'D','o','c','u','m','e','n','t','s','\\', - 'M','y',' ','M','u','s','i','c','\0'}; -static const WCHAR All_Users__Documents__My_PicturesW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'D','o','c','u','m','e','n','t','s','\\', - 'M','y',' ','P','i','c','t','u','r','e','s','\0'}; -static const WCHAR All_Users__Documents__My_VideoW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'D','o','c','u','m','e','n','t','s','\\', - 'M','y',' ','V','i','d','e','o','\0'}; -static const WCHAR All_Users__Start_MenuW[] = {'A','l','l',' ','U','s','e','r','s','\\','S','t','a','r','t',' ','M','e','n','u','\0'}; -static const WCHAR All_Users__Start_Menu__ProgramsW[] = {'A','l','l',' ','U','s','e','r','s','\\','S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\0'}; -static const WCHAR All_Users__Start_Menu__Programs__Administrative_ToolsW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\\', - 'A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'}; -static const WCHAR All_Users__Start_Menu__Programs__StartUpW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'S','t','a','r','t',' ','M','e','n','u','\\', - 'P','r','o','g','r','a','m','s','\\', - 'S','t','a','r','t','U','p','\0'}; -static const WCHAR All_Users__TemplatesW[] = {'A','l','l',' ','U','s','e','r','s','\\', - 'T','e','m','p','l','a','t','e','s','\0'}; static const WCHAR AppDataW[] = {'A','p','p','D','a','t','a','\0'}; -static const WCHAR Application_DataW[] = {'A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\0'}; static const WCHAR CacheW[] = {'C','a','c','h','e','\0'}; static const WCHAR CD_BurningW[] = {'C','D',' ','B','u','r','n','i','n','g','\0'}; 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'}; @@ -747,24 +710,10 @@ static const WCHAR Common_TemplatesW[] = {'C','o','m','m','o','n',' ','T','e','m static const WCHAR CommonVideoW[] = {'C','o','m','m','o','n','V','i','d','e','o','\0'}; static const WCHAR CookiesW[] = {'C','o','o','k','i','e','s','\0'}; static const WCHAR DesktopW[] = {'D','e','s','k','t','o','p','\0'}; -static const WCHAR Empty_StringW[] = {'\0'}; static const WCHAR FavoritesW[] = {'F','a','v','o','r','i','t','e','s','\0'}; static const WCHAR FontsW[] = {'F','o','n','t','s','\0'}; static const WCHAR HistoryW[] = {'H','i','s','t','o','r','y','\0'}; static const WCHAR Local_AppDataW[] = {'L','o','c','a','l',' ','A','p','p','D','a','t','a','\0'}; -static const WCHAR Local_Settings__Application_DataW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\', - 'A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\0'}; -static const WCHAR Local_Settings__Application_Data__Microsoft__CD_BurningW[] = { - 'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\', - 'A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\\', - 'M','i','c','r','o','s','o','f','t','\\','C','D',' ','B','u','r','n','i','n','g','\0'}; -static const WCHAR My_DocumentsW[] = {'M','y',' ','D','o','c','u','m','e','n','t','s','\0'}; -static const WCHAR My_Documents__My_MusicW[] = {'M','y',' ','D','o','c','u','m','e','n','t','s','\\', - 'M','y',' ','M','u','s','i','c','\0'}; -static const WCHAR My_Documents__My_PicturesW[] = {'M','y',' ','D','o','c','u','m','e','n','t','s','\\', - 'M','y',' ','P','i','c','t','u','r','e','s','\0'}; -static const WCHAR My_Documents__My_VideoW[] = {'M','y',' ','D','o','c','u','m','e','n','t','s','\\', - 'M','y',' ','V','i','d','e','o','\0'}; static const WCHAR My_MusicW[] = {'M','y',' ','M','u','s','i','c','\0'}; static const WCHAR My_PicturesW[] = {'M','y',' ','P','i','c','t','u','r','e','s','\0'}; static const WCHAR My_VideoW[] = {'M','y',' ','V','i','d','e','o','\0'}; @@ -772,596 +721,964 @@ static const WCHAR NetHoodW[] = {'N','e','t','H','o','o','d','\0'}; static const WCHAR PersonalW[] = {'P','e','r','s','o','n','a','l','\0'}; static const WCHAR PrintHoodW[] = {'P','r','i','n','t','H','o','o','d','\0'}; static const WCHAR ProgramFilesDirW[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r','\0'}; -static const WCHAR Program_FilesW[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s','\0'}; -static const WCHAR Program_Files__Common_FilesW[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s','\\', - 'C','o','m','m','o','n',' ','F','i','l','e','s','\0'}; static const WCHAR ProgramsW[] = {'P','r','o','g','r','a','m','s','\0'}; static const WCHAR RecentW[] = {'R','e','c','e','n','t','\0'}; static const WCHAR ResourcesW[] = {'R','e','s','o','u','r','c','e','s','\0'}; static const WCHAR SendToW[] = {'S','e','n','d','T','o','\0'}; -static const WCHAR ShellNewW[] = {'S','h','e','l','l','N','e','w','\0'}; -static const WCHAR Start_Menu__ProgramsW[] = {'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\0'}; -static const WCHAR SysDirW[] = {'S','y','s','D','i','r','\0'}; -static const WCHAR SystemW[] = {'s','y','s','t','e','m','\0'}; static const WCHAR StartUpW[] = {'S','t','a','r','t','U','p','\0'}; static const WCHAR Start_MenuW[] = {'S','t','a','r','t',' ','M','e','n','u','\0'}; -static const WCHAR Start_Menu__Programs__Administrative_ToolsW[] = { - 'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\\', - 'A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'}; -static const WCHAR Start_Menu__Programs__StartUpW[] = {'S','t','a','r','t',' ','M','e','n','u','\\', - 'P','r','o','g','r','a','m','s','\\', - 'S','t','a','r','t','U','p','\0'}; static const WCHAR TemplatesW[] = {'T','e','m','p','l','a','t','e','s','\0'}; -static const WCHAR Temporary_Internet_FilesW[] = {'T','e','m','p','o','r','a','r','y',' ','I','n','t','e','r','n','e','t',' ','F','i','l','e','s','\0'}; -static const WCHAR WinDirW[] = {'W','i','n','D','i','r','\0'}; -static const WCHAR WindowsW[] = {'w','i','n','d','o','w','s','\0'}; - - +static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t','\0'}; +static const WCHAR AllUsersProfileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%','\0'}; +static const WCHAR UserProfileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%','\0'}; +static const WCHAR SystemDriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%','\0'}; +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}; +static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0}; +static const WCHAR AllUsersProfileValueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'}; +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'}; +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'}; +/* This defaults to L"Documents and Settings" on Windows 2000/XP, but we're + * acting more Windows 9x-like for now. + */ +static const WCHAR szDefaultProfileDirW[] = {'w','i','n','d','o','w','s','\\','p','r','o','f','i','l','e','s','\0'}; +static const WCHAR AllUsersW[] = {'A','l','l',' ','U','s','e','r','s','\0'}; +typedef enum _CSIDL_Type { + CSIDL_Type_User, + CSIDL_Type_AllUsers, + CSIDL_Type_CurrVer, + CSIDL_Type_Disallowed, + CSIDL_Type_NonExistent, + CSIDL_Type_WindowsPath, + CSIDL_Type_SystemPath, +} CSIDL_Type; typedef struct { - DWORD dwFlags; - HKEY hRootKey; - LPCWSTR szValueName; - LPCWSTR szDefaultPath; /* fallback string; sub dir of windows directory */ + CSIDL_Type type; + LPCWSTR szValueName; + LPCWSTR szDefaultPath; /* fallback string or resource ID */ } CSIDL_DATA; -#define CSIDL_MYFLAG_SHFOLDER 1 -#define CSIDL_MYFLAG_SETUP 2 -#define CSIDL_MYFLAG_CURRVER 4 -#define CSIDL_MYFLAG_RELATIVE 8 - -#define HKLM HKEY_LOCAL_MACHINE -#define HKCU HKEY_CURRENT_USER -#define HKEY_DISALLOWED (HKEY)0 -#define HKEY_UNIMPLEMENTED (HKEY)1 -#define HKEY_WINDOWSPATH (HKEY)2 -#define HKEY_NONEXISTENT (HKEY)3 static const CSIDL_DATA CSIDL_Data[] = { - { /* CSIDL_DESKTOP */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - DesktopW, - DesktopW + { /* 0x00 - CSIDL_DESKTOP */ + CSIDL_Type_User, + DesktopW, + MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY) }, - { /* CSIDL_INTERNET */ - 0, - HKEY_DISALLOWED, - NULL, - NULL + { /* 0x01 - CSIDL_INTERNET */ + CSIDL_Type_Disallowed, + NULL, + NULL }, - { /* CSIDL_PROGRAMS */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - ProgramsW, - Start_Menu__ProgramsW + { /* 0x02 - CSIDL_PROGRAMS */ + CSIDL_Type_User, + ProgramsW, + MAKEINTRESOURCEW(IDS_PROGRAMS) }, - { /* CSIDL_CONTROLS (.CPL files) */ - CSIDL_MYFLAG_SETUP | CSIDL_MYFLAG_RELATIVE, - HKLM, - SysDirW, - SystemW + { /* 0x03 - CSIDL_CONTROLS (.CPL files) */ + CSIDL_Type_SystemPath, + NULL, + NULL }, - { /* CSIDL_PRINTERS */ - CSIDL_MYFLAG_SETUP | CSIDL_MYFLAG_RELATIVE, - HKLM, - SysDirW, - SystemW + { /* 0x04 - CSIDL_PRINTERS */ + CSIDL_Type_SystemPath, + NULL, + NULL }, - { /* CSIDL_PERSONAL */ - CSIDL_MYFLAG_SHFOLDER, - HKCU, - PersonalW, - My_DocumentsW + { /* 0x05 - CSIDL_PERSONAL */ + CSIDL_Type_User, + PersonalW, + MAKEINTRESOURCEW(IDS_PERSONAL) }, - { /* CSIDL_FAVORITES */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - FavoritesW, - FavoritesW + { /* 0x06 - CSIDL_FAVORITES */ + CSIDL_Type_User, + FavoritesW, + MAKEINTRESOURCEW(IDS_FAVORITES) }, - { /* CSIDL_STARTUP */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - StartUpW, - Start_Menu__Programs__StartUpW + { /* 0x07 - CSIDL_STARTUP */ + CSIDL_Type_User, + StartUpW, + MAKEINTRESOURCEW(IDS_STARTUP) }, - { /* CSIDL_RECENT */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - RecentW, - RecentW + { /* 0x08 - CSIDL_RECENT */ + CSIDL_Type_User, + RecentW, + MAKEINTRESOURCEW(IDS_RECENT) }, - { /* CSIDL_SENDTO */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - SendToW, - SendToW + { /* 0x09 - CSIDL_SENDTO */ + CSIDL_Type_User, + SendToW, + MAKEINTRESOURCEW(IDS_SENDTO) }, - { /* CSIDL_BITBUCKET - Recycle Bin */ - 0, - HKEY_DISALLOWED, - NULL, - NULL, + { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */ + CSIDL_Type_Disallowed, + NULL, + NULL, }, - { /* CSIDL_STARTMENU */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - Start_MenuW, - Start_MenuW + { /* 0x0b - CSIDL_STARTMENU */ + CSIDL_Type_User, + Start_MenuW, + MAKEINTRESOURCEW(IDS_STARTMENU) }, - { /* CSIDL_MYDOCUMENTS */ - 0, - HKEY_UNIMPLEMENTED, /* FIXME */ - NULL, - NULL + { /* 0x0c - CSIDL_MYDOCUMENTS */ + CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */ + NULL, + NULL }, - { /* CSIDL_MYMUSIC */ - CSIDL_MYFLAG_SHFOLDER, - HKCU, - My_MusicW, - My_Documents__My_MusicW + { /* 0x0d - CSIDL_MYMUSIC */ + CSIDL_Type_User, + My_MusicW, + MAKEINTRESOURCEW(IDS_MYMUSIC) }, - { /* CSIDL_MYVIDEO */ - CSIDL_MYFLAG_SHFOLDER, - HKCU, - My_VideoW, - My_Documents__My_VideoW + { /* 0x0e - CSIDL_MYVIDEO */ + CSIDL_Type_User, + My_VideoW, + MAKEINTRESOURCEW(IDS_MYVIDEO) }, - { /* unassigned */ - 0, - HKEY_DISALLOWED, - NULL, - NULL, + { /* 0x0f - unassigned */ + CSIDL_Type_Disallowed, + NULL, + NULL, }, - { /* CSIDL_DESKTOPDIRECTORY */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - DesktopW, - DesktopW + { /* 0x10 - CSIDL_DESKTOPDIRECTORY */ + CSIDL_Type_User, + DesktopW, + MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY) }, - { /* CSIDL_DRIVES */ - 0, - HKEY_DISALLOWED, - NULL, - NULL, + { /* 0x11 - CSIDL_DRIVES */ + CSIDL_Type_Disallowed, + NULL, + NULL, }, - { /* CSIDL_NETWORK */ - 0, - HKEY_DISALLOWED, - NULL, - NULL, + { /* 0x12 - CSIDL_NETWORK */ + CSIDL_Type_Disallowed, + NULL, + NULL, }, - { /* CSIDL_NETHOOD */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - NetHoodW, - NetHoodW + { /* 0x13 - CSIDL_NETHOOD */ + CSIDL_Type_User, + NetHoodW, + MAKEINTRESOURCEW(IDS_NETHOOD) }, - { /* CSIDL_FONTS */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - FontsW, - FontsW + { /* 0x14 - CSIDL_FONTS */ + CSIDL_Type_WindowsPath, + NULL, + FontsW }, - { /* CSIDL_TEMPLATES */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - TemplatesW, - ShellNewW + { /* 0x15 - CSIDL_TEMPLATES */ + CSIDL_Type_User, + TemplatesW, + MAKEINTRESOURCEW(IDS_TEMPLATES) }, - { /* CSIDL_COMMON_STARTMENU */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - Common_Start_MenuW, - All_Users__Start_MenuW + { /* 0x16 - CSIDL_COMMON_STARTMENU */ + CSIDL_Type_AllUsers, + Common_Start_MenuW, + MAKEINTRESOURCEW(IDS_STARTMENU) }, - { /* CSIDL_COMMON_PROGRAMS */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - Common_ProgramsW, - All_Users__Start_Menu__ProgramsW + { /* 0x17 - CSIDL_COMMON_PROGRAMS */ + CSIDL_Type_AllUsers, + Common_ProgramsW, + MAKEINTRESOURCEW(IDS_PROGRAMS) }, - { /* CSIDL_COMMON_STARTUP */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, + { /* 0x18 - CSIDL_COMMON_STARTUP */ + CSIDL_Type_AllUsers, Common_StartUpW, - All_Users__Start_Menu__Programs__StartUpW + MAKEINTRESOURCEW(IDS_STARTUP) }, - { /* CSIDL_COMMON_DESKTOPDIRECTORY */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - Common_DesktopW, - All_Users__DesktopW + { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */ + CSIDL_Type_AllUsers, + Common_DesktopW, + MAKEINTRESOURCEW(IDS_DESKTOP) }, - { /* CSIDL_APPDATA */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - AppDataW, - Application_DataW + { /* 0x1a - CSIDL_APPDATA */ + CSIDL_Type_User, + AppDataW, + MAKEINTRESOURCEW(IDS_APPDATA) }, - { /* CSIDL_PRINTHOOD */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - PrintHoodW, - PrintHoodW + { /* 0x1b - CSIDL_PRINTHOOD */ + CSIDL_Type_User, + PrintHoodW, + MAKEINTRESOURCEW(IDS_PRINTHOOD) }, - { /* CSIDL_LOCAL_APPDATA (win2k only/undocumented) */ - CSIDL_MYFLAG_SHFOLDER, - HKCU, - Local_AppDataW, - Local_Settings__Application_DataW, + { /* 0x1c - CSIDL_LOCAL_APPDATA */ + CSIDL_Type_User, + Local_AppDataW, + MAKEINTRESOURCEW(IDS_LOCAL_APPDATA) }, - { /* CSIDL_ALTSTARTUP */ - 0, - HKEY_NONEXISTENT, - NULL, - NULL + { /* 0x1d - CSIDL_ALTSTARTUP */ + CSIDL_Type_NonExistent, + NULL, + NULL }, - { /* CSIDL_COMMON_ALTSTARTUP */ - 0, - HKEY_NONEXISTENT, - NULL, - NULL + { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */ + CSIDL_Type_NonExistent, + NULL, + NULL }, - { /* CSIDL_COMMON_FAVORITES */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - FavoritesW, - FavoritesW + { /* 0x1f - CSIDL_COMMON_FAVORITES */ + CSIDL_Type_AllUsers, + FavoritesW, + MAKEINTRESOURCEW(IDS_FAVORITES) }, - { /* CSIDL_INTERNET_CACHE (32) */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - CacheW, - Temporary_Internet_FilesW + { /* 0x20 - CSIDL_INTERNET_CACHE */ + CSIDL_Type_User, + CacheW, + MAKEINTRESOURCEW(IDS_INTERNET_CACHE) }, - { /* CSIDL_COOKIES (33) */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - CookiesW, - CookiesW + { /* 0x21 - CSIDL_COOKIES */ + CSIDL_Type_User, + CookiesW, + MAKEINTRESOURCEW(IDS_COOKIES) }, - { /* CSIDL_HISTORY (34) */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - HistoryW, - HistoryW + { /* 0x22 - CSIDL_HISTORY */ + CSIDL_Type_User, + HistoryW, + MAKEINTRESOURCEW(IDS_HISTORY) }, - { /* CSIDL_COMMON_APPDATA */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - Common_AppDataW, - All_Users__Application_DataW + { /* 0x23 - CSIDL_COMMON_APPDATA */ + CSIDL_Type_AllUsers, + Common_AppDataW, + MAKEINTRESOURCEW(IDS_APPDATA) }, - { /* CSIDL_WINDOWS */ - CSIDL_MYFLAG_SETUP, - HKLM, - WinDirW, - WindowsW + { /* 0x24 - CSIDL_WINDOWS */ + CSIDL_Type_WindowsPath, + NULL, + NULL }, - { /* CSIDL_SYSTEM */ - CSIDL_MYFLAG_SETUP | CSIDL_MYFLAG_RELATIVE, - HKLM, - SysDirW, - SystemW + { /* 0x25 - CSIDL_SYSTEM */ + CSIDL_Type_SystemPath, + NULL, + NULL }, - { /* CSIDL_PROGRAM_FILES */ - CSIDL_MYFLAG_CURRVER, - HKLM, - ProgramFilesDirW, - Program_FilesW + { /* 0x26 - CSIDL_PROGRAM_FILES */ + CSIDL_Type_CurrVer, + ProgramFilesDirW, + MAKEINTRESOURCEW(IDS_PROGRAM_FILES) }, - { /* CSIDL_MYPICTURES */ - CSIDL_MYFLAG_SHFOLDER, - HKCU, - My_PicturesW, - My_Documents__My_PicturesW + { /* 0x27 - CSIDL_MYPICTURES */ + CSIDL_Type_User, + My_PicturesW, + MAKEINTRESOURCEW(IDS_MYPICTURES) }, - { /* CSIDL_PROFILE */ - CSIDL_MYFLAG_SETUP | CSIDL_MYFLAG_RELATIVE, - HKLM, - WinDirW, /* correct ? */ - Empty_StringW + { /* 0x28 - CSIDL_PROFILE */ + CSIDL_Type_User, + NULL, + NULL }, - { /* CSIDL_SYSTEMX86 */ - CSIDL_MYFLAG_SETUP | CSIDL_MYFLAG_RELATIVE, - HKLM, - SysDirW, - SystemW + { /* 0x29 - CSIDL_SYSTEMX86 */ + CSIDL_Type_NonExistent, + NULL, + NULL }, - { /* CSIDL_PROGRAM_FILESX86 */ - CSIDL_MYFLAG_CURRVER, - HKLM, - ProgramFilesDirW, - Program_FilesW + { /* 0x2a - CSIDL_PROGRAM_FILESX86 */ + CSIDL_Type_NonExistent, + NULL, + NULL }, - { /* CSIDL_PROGRAM_FILES_COMMON */ - CSIDL_MYFLAG_CURRVER, - HKLM, - CommonFilesDirW, - Program_Files__Common_FilesW /* ? */ + { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */ + CSIDL_Type_CurrVer, + CommonFilesDirW, + MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON) }, - { /* CSIDL_PROGRAM_FILES_COMMONX86 */ - CSIDL_MYFLAG_CURRVER, - HKLM, - CommonFilesDirW, - Program_Files__Common_FilesW /* ? */ + { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */ + CSIDL_Type_NonExistent, + NULL, + NULL }, - { /* CSIDL_COMMON_TEMPLATES */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - Common_TemplatesW, - /*"Documents and Settings\\"*/ All_Users__TemplatesW + { /* 0x2d - CSIDL_COMMON_TEMPLATES */ + CSIDL_Type_AllUsers, + Common_TemplatesW, + MAKEINTRESOURCEW(IDS_TEMPLATES) }, - { /* CSIDL_COMMON_DOCUMENTS */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - Common_DocumentsW, - /*"Documents and Settings\\"*/ All_Users__DocumentsW + { /* 0x2e - CSIDL_COMMON_DOCUMENTS */ + CSIDL_Type_AllUsers, + Common_DocumentsW, + MAKEINTRESOURCEW(IDS_COMMON_DOCUMENTS) }, - { /* CSIDL_COMMON_ADMINTOOLS */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - Common_Administrative_ToolsW, - /*"Documents and Settings\\"*/ All_Users__Start_Menu__Programs__Administrative_ToolsW + { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */ + CSIDL_Type_AllUsers, + Common_Administrative_ToolsW, + MAKEINTRESOURCEW(IDS_ADMINTOOLS) }, - { /* CSIDL_ADMINTOOLS */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKCU, - Administrative_ToolsW, - Start_Menu__Programs__Administrative_ToolsW + { /* 0x30 - CSIDL_ADMINTOOLS */ + CSIDL_Type_User, + Administrative_ToolsW, + MAKEINTRESOURCEW(IDS_ADMINTOOLS) }, - { /* CSIDL_CONNECTIONS */ - 0, - HKEY_DISALLOWED, - NULL, - NULL + { /* 0x31 - CSIDL_CONNECTIONS */ + CSIDL_Type_Disallowed, + NULL, + NULL }, - { /* unassigned 32 */ - 0, - HKEY_DISALLOWED, - NULL, - NULL + { /* 0x32 - unassigned */ + CSIDL_Type_Disallowed, + NULL, + NULL }, - { /* unassigned 33 */ - 0, - HKEY_DISALLOWED, - NULL, - NULL + { /* 0x33 - unassigned */ + CSIDL_Type_Disallowed, + NULL, + NULL }, - { /* unassigned 34 */ - 0, - HKEY_DISALLOWED, - NULL, - NULL + { /* 0x34 - unassigned */ + CSIDL_Type_Disallowed, + NULL, + NULL }, - { /* CSIDL_COMMON_MUSIC */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - CommonMusicW, - /*"Documents and Settings\\"*/ All_Users__Documents__My_MusicW + { /* 0x35 - CSIDL_COMMON_MUSIC */ + CSIDL_Type_AllUsers, + CommonMusicW, + MAKEINTRESOURCEW(IDS_COMMON_MUSIC) }, - { /* CSIDL_COMMON_PICTURES */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - CommonPicturesW, - /*"Documents and Settings\\"*/ All_Users__Documents__My_PicturesW + { /* 0x36 - CSIDL_COMMON_PICTURES */ + CSIDL_Type_AllUsers, + CommonPicturesW, + MAKEINTRESOURCEW(IDS_COMMON_PICTURES) }, - { /* CSIDL_COMMON_VIDEO */ - CSIDL_MYFLAG_SHFOLDER | CSIDL_MYFLAG_RELATIVE, - HKLM, - CommonVideoW, - /*"Documents and Settings\\"*/ All_Users__Documents__My_VideoW + { /* 0x37 - CSIDL_COMMON_VIDEO */ + CSIDL_Type_AllUsers, + CommonVideoW, + MAKEINTRESOURCEW(IDS_COMMON_VIDEO) }, - { /* CSIDL_RESOURCES */ - 0, - HKEY_WINDOWSPATH, - NULL, - ResourcesW + { /* 0x38 - CSIDL_RESOURCES */ + CSIDL_Type_WindowsPath, + NULL, + ResourcesW }, - { /* CSIDL_RESOURCES_LOCALIZED */ - 0, - HKEY_DISALLOWED, /* FIXME */ - NULL, - NULL + { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */ + CSIDL_Type_NonExistent, + NULL, + NULL }, - { /* CSIDL_COMMON_OEM_LINKS */ - 0, - HKEY_DISALLOWED, /* FIXME */ - NULL, - NULL + { /* 0x3a - CSIDL_COMMON_OEM_LINKS */ + CSIDL_Type_NonExistent, + NULL, + NULL }, - { /* CSIDL_CDBURN_AREA */ - CSIDL_MYFLAG_SHFOLDER, - HKCU, - CD_BurningW, - Local_Settings__Application_Data__Microsoft__CD_BurningW + { /* 0x3b - CSIDL_CDBURN_AREA */ + CSIDL_Type_User, + CD_BurningW, + MAKEINTRESOURCEW(IDS_CDBURN_AREA) }, - { /* unassigned 3C */ - 0, - HKEY_DISALLOWED, - NULL, - NULL + { /* 0x3c unassigned */ + CSIDL_Type_Disallowed, + NULL, + NULL }, - { /* CSIDL_COMPUTERSNEARME */ - 0, - HKEY_DISALLOWED, /* FIXME */ - NULL, - NULL + { /* 0x3d - CSIDL_COMPUTERSNEARME */ + CSIDL_Type_Disallowed, /* FIXME */ + NULL, + NULL }, - { /* CSIDL_PROFILES */ - 0, - HKEY_DISALLOWED, /* FIXME */ - NULL, - NULL + { /* 0x3e - CSIDL_PROFILES */ + CSIDL_Type_Disallowed, /* oddly, this matches WinXP */ + NULL, + NULL } }; -#undef HKCU -#undef HKLM -/**********************************************************************/ +static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest); +/* Gets the value named value from the registry key + * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders + * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which + * is assumed to be MAX_PATH WCHARs in length. + * If it exists, expands the value and writes the expanded value to + * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + * Returns successful error code if the value was retrieved from the registry, + * and a failure otherwise. + */ +static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix, + LPCWSTR value, LPWSTR path) +{ + HRESULT hr; + WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH]; + LPCWSTR pShellFolderPath, pUserShellFolderPath; + DWORD dwDisp, dwType, dwPathLen = MAX_PATH; + HKEY userShellFolderKey, shellFolderKey; + + TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value), + path); + + if (userPrefix) + { + strcpyW(shellFolderPath, userPrefix); + PathAddBackslashW(shellFolderPath); + strcatW(shellFolderPath, szSHFolders); + pShellFolderPath = shellFolderPath; + strcpyW(userShellFolderPath, userPrefix); + PathAddBackslashW(userShellFolderPath); + strcatW(userShellFolderPath, szSHUserFolders); + pUserShellFolderPath = userShellFolderPath; + } + else + { + pUserShellFolderPath = szSHUserFolders; + pShellFolderPath = szSHFolders; + } + + if (RegCreateKeyExW(rootKey, pShellFolderPath, 0, NULL, 0, KEY_ALL_ACCESS, + NULL, &shellFolderKey, &dwDisp)) + { + TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath)); + return E_FAIL; + } + if (RegCreateKeyExW(rootKey, pUserShellFolderPath, 0, NULL, 0, + KEY_ALL_ACCESS, NULL, &userShellFolderKey, &dwDisp)) + { + TRACE("Failed to create %s\n", + debugstr_w(pUserShellFolderPath)); + RegCloseKey(shellFolderKey); + return E_FAIL; + } + + if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType, + (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ)) + { + path[dwPathLen / sizeof(WCHAR)] = '\0'; + if (dwType == REG_EXPAND_SZ) + { + WCHAR szTemp[MAX_PATH]; + + _SHExpandEnvironmentStrings(path, szTemp); + strncpyW(path, szTemp, MAX_PATH); + } + RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path, + (strlenW(path) + 1) * sizeof(WCHAR)); + hr = S_OK; + } + else + hr = E_FAIL; + RegCloseKey(shellFolderKey); + RegCloseKey(userShellFolderKey); + TRACE("returning 0x%08lx\n", hr); + return hr; +} + +/* Gets a 'semi-expanded' default value of the CSIDL with index folder into + * pszPath, based on the entries in CSIDL_Data. By semi-expanded, I mean: + * - The entry's szDefaultPath may be either a string value or an integer + * resource identifier. In the latter case, the string value of the resource + * is written. + * - Depending on the entry's type, the path may begin with an (unexpanded) + * environment variable name. The caller is responsible for expanding + * environment strings if so desired. + * The types that are prepended with environment variables are: + * CSIDL_Type_User: %USERPROFILE% + * CSIDL_Type_AllUsers: %ALLUSERSPROFILE% + * CSIDL_Type_CurrVer: %SystemDrive% + * (Others might make sense too, but as yet are unneeded.) + * FIXME: there are two special cases for the default value: + * - the "My Documents" (CSIDL_PERSONAL) entry should be $HOME + * - the CSIDL_DESKTOP and CSIDL_DESKTOPDIRECTORY (which have the same path) + * should be $HOME/Desktop if it exists + * But, $HOME doesn't seem to be inherited into the Wine environment. I could + * use getenv, but this returns me a UNIX path, which may or may not be + * reachable from any currently mounted DOS drives. + */ +static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath) +{ + HRESULT hr; + WCHAR resourcePath[MAX_PATH]; + LPCWSTR pDefaultPath; + + TRACE("0x%02x,%p\n", folder, pszPath); + + if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0])) + return E_INVALIDARG; + if (!pszPath) + return E_INVALIDARG; + + if (CSIDL_Data[folder].szDefaultPath && + IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath)) + { + if (LoadStringW(shell32_hInstance, + (UINT)CSIDL_Data[folder].szDefaultPath, resourcePath, MAX_PATH)) + { + hr = S_OK; + pDefaultPath = resourcePath; + } + else + { + FIXME("LoadString failed, missing translation?\n"); + hr = E_FAIL; + } + } + else + { + hr = S_OK; + pDefaultPath = CSIDL_Data[folder].szDefaultPath; + } + if (SUCCEEDED(hr)) + { + switch (CSIDL_Data[folder].type) + { + case CSIDL_Type_User: + strcpyW(pszPath, UserProfileW); + break; + case CSIDL_Type_AllUsers: + strcpyW(pszPath, AllUsersProfileW); + break; + case CSIDL_Type_CurrVer: + strcpyW(pszPath, SystemDriveW); + break; + default: + ; /* no corresponding env. var, do nothing */ + } + if (pDefaultPath) + { + PathAddBackslashW(pszPath); + strcatW(pszPath, pDefaultPath); + } + } + TRACE("returning 0x%08lx\n", hr); + return hr; +} + +/* Gets the (unexpanded) value of the folder with index folder into pszPath. + * The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value + * can be overridden in the HKLM\\szCurrentVersion key. + * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in + * the registry, uses _SHGetDefaultValue to get the value. + */ +static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder, + LPWSTR pszPath) +{ + HRESULT hr; + + TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath); + + if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0])) + return E_INVALIDARG; + if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer) + return E_INVALIDARG; + if (!pszPath) + return E_INVALIDARG; + + if (dwFlags & SHGFP_TYPE_DEFAULT) + hr = _SHGetDefaultValue(folder, pszPath); + else + { + HKEY hKey; + DWORD dwDisp; + + if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szCurrentVersion, 0, + NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp)) + hr = E_FAIL; + else + { + DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR); + + if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL, + &dwType, (LPBYTE)pszPath, &dwPathLen) || + (dwType != REG_SZ && dwType != REG_EXPAND_SZ)) + { + hr = _SHGetDefaultValue(folder, pszPath); + dwType = REG_EXPAND_SZ; + RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType, + (LPBYTE)pszPath, (strlenW(pszPath)+1)*sizeof(WCHAR)); + } + else + { + pszPath[dwPathLen / sizeof(WCHAR)] = '\0'; + hr = S_OK; + } + RegCloseKey(hKey); + } + } + TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath)); + return hr; +} + +/* Gets the user's path (unexpanded) for the CSIDL with index folder: + * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise + * calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken: + * - if hToken is -1, looks in HKEY_USERS\.Default + * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE + * if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally + * calls _SHGetDefaultValue for it. + */ +static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder, + LPWSTR pszPath) +{ + HRESULT hr; + + TRACE("%p,0x%08lx,0x%02x,%p\n", hToken, dwFlags, folder, pszPath); + + if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0])) + return E_INVALIDARG; + if (CSIDL_Data[folder].type != CSIDL_Type_User) + return E_INVALIDARG; + if (!pszPath) + return E_INVALIDARG; + + /* Only the current user and the default user are supported right now + * I'm afraid. + * FIXME: should be able to call GetTokenInformation on the token, + * then call ConvertSidToStringSidW on it to get the user prefix. + * But Wine's registry doesn't store user info by sid, it stores it + * by user name (and I don't know how to convert from a token to a + * user name). + */ + if (hToken != NULL && hToken != (HANDLE)-1) + { + FIXME("unsupported for user other than current or default\n"); + return E_FAIL; + } + + if (dwFlags & SHGFP_TYPE_DEFAULT) + hr = _SHGetDefaultValue(folder, pszPath); + else + { + LPCWSTR userPrefix = NULL; + HKEY hRootKey; + + if (hToken == (HANDLE)-1) + { + hRootKey = HKEY_USERS; + userPrefix = DefaultW; + } + else /* hToken == NULL, other values disallowed above */ + hRootKey = HKEY_CURRENT_USER; + hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, + CSIDL_Data[folder].szValueName, pszPath); + if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE) + hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, + CSIDL_Data[folder].szValueName, pszPath); + if (FAILED(hr)) + hr = _SHGetDefaultValue(folder, pszPath); + } + TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath)); + return hr; +} + +/* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has + * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls + * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE. + * If this fails, falls back to _SHGetDefaultValue. + */ +static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder, + LPWSTR pszPath) +{ + HRESULT hr; + + TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath); + + if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0])) + return E_INVALIDARG; + if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers) + return E_INVALIDARG; + if (!pszPath) + return E_INVALIDARG; + + if (dwFlags & SHGFP_TYPE_DEFAULT) + hr = _SHGetDefaultValue(folder, pszPath); + else + { + hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, + CSIDL_Data[folder].szValueName, pszPath); + if (FAILED(hr)) + hr = _SHGetDefaultValue(folder, pszPath); + } + TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath)); + return hr; +} + +static HRESULT _SHOpenProfilesKey(PHKEY pKey) +{ + LONG lRet; + DWORD disp; + + lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, NULL, 0, + KEY_ALL_ACCESS, NULL, pKey, &disp); + return HRESULT_FROM_WIN32(lRet); +} + +/* Reads the value named szValueName from the key profilesKey (assumed to be + * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH + * WCHARs in length. If it doesn't exist, returns szDefault (and saves + * szDefault to the registry). + */ +static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName, + LPWSTR szValue, LPCWSTR szDefault) +{ + HRESULT hr; + DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR); + LONG lRet; + + TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue, + debugstr_w(szDefault)); + lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type, + (LPBYTE)szValue, &dwPathLen); + if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen + && *szValue) + { + dwPathLen /= sizeof(WCHAR); + szValue[dwPathLen] = '\0'; + hr = S_OK; + } + else + { + /* Missing or invalid value, set a default */ + strncpyW(szValue, szDefault, MAX_PATH); + szValue[MAX_PATH - 1] = '\0'; + TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName), + debugstr_w(szValue)); + lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ, + (LPBYTE)szValue, (strlenW(szValue) + 1) * sizeof(WCHAR)); + if (lRet) + hr = HRESULT_FROM_WIN32(lRet); + else + hr = S_OK; + } + TRACE("returning 0x%08lx (output value is %s)\n", hr, debugstr_w(szValue)); + return hr; +} + +/* Attempts to expand a subset of environment variables from szSrc into szDest, + * which is assumed to be MAX_PATH characters in length. Unlike the standard + * ExpandEnvironmentStringsW, doesn't actually refer to the environment, for + * two reasons: + * - the environment variables may not be set when this is called (as during + * Wine's installation when default values are being written to the registry) + * - the user perhaps shouldn't be able to override the location of certain + * directories through modifying the environment + * The supported environment variables, and their source, are: + * - ALLUSERSPROFILE, USERPROFILE: reads from the registry + * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its + * path + * Also unlike ExpandEnvironmentStringsW, only expands a single variable, and + * only in the beginning of szSrc. + */ +static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest) +{ + HRESULT hr; + WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 }; + HKEY key = NULL; + + TRACE("%s, %p\n", debugstr_w(szSrc), szDest); + + if (!szSrc || !szDest) return E_INVALIDARG; + + /* Get the profile prefix, we'll probably be needing it */ + hr = _SHOpenProfilesKey(&key); + if (SUCCEEDED(hr)) + { + WCHAR szDefaultProfilesPrefix[MAX_PATH]; + + strcpyW(szDefaultProfilesPrefix, SystemDriveW); + PathAppendW(szDefaultProfilesPrefix, szDefaultProfileDirW); + hr = _SHGetProfilesValue(key, ProfilesDirectoryW, szProfilesPrefix, + szDefaultProfilesPrefix); + } + + *szDest = 0; + strcpyW(szTemp, szSrc); + while (SUCCEEDED(hr) && szTemp[0] == '%') + { + if (!strncmpiW(szTemp, AllUsersProfileW, strlenW(AllUsersProfileW))) + { + WCHAR szAllUsers[MAX_PATH]; + + strcpyW(szDest, szProfilesPrefix); + hr = _SHGetProfilesValue(key, AllUsersProfileValueW, + szAllUsers, AllUsersW); + PathAppendW(szDest, szAllUsers); + PathAppendW(szDest, szTemp + strlenW(AllUsersProfileW)); + } + else if (!strncmpiW(szTemp, UserProfileW, strlenW(UserProfileW))) + { + WCHAR userName[MAX_PATH]; + DWORD userLen = MAX_PATH; + + strcpyW(szDest, szProfilesPrefix); + GetUserNameW(userName, &userLen); + PathAppendW(szDest, userName); + PathAppendW(szDest, szTemp + strlenW(UserProfileW)); + } + else if (!strncmpiW(szTemp, SystemDriveW, strlenW(SystemDriveW))) + { + GetSystemDirectoryW(szDest, MAX_PATH); + if (szDest[1] != ':') + { + FIXME("non-drive system paths unsupported\n"); + hr = E_FAIL; + } + else + { + strcpyW(szDest + 3, szTemp + strlenW(SystemDriveW) + 1); + hr = S_OK; + } + } + else + { + WARN("asked for unsupported environment variable in path %s\n", + debugstr_w(szTemp)); + hr = E_INVALIDARG; + } + if (SUCCEEDED(hr) && szDest[0] == '%') + strcpyW(szTemp, szDest); + else + { + /* terminate loop */ + szTemp[0] = '\0'; + } + } + if (key) + RegCloseKey(key); + TRACE("returning 0x%08lx (input was %s, output is %s)\n", hr, + debugstr_w(szSrc), debugstr_w(szDest)); + return hr; +} + +/************************************************************************* + * SHGetFolderPathW [SHELL32.@] + * + * NOTES + * Converts nFolder to path. Most values can be overridden in either + * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders + * or in the same location in HKLM. + * The registry usage is explained by the following tech note: + * http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36173.asp + * The "Shell Folders" registry key was used in NT4 and earlier systems. + * Beginning with Windows 2000, the "User Shell Folders" key is used, so + * changes made to it are made to the former key too. This synchronization is + * done on-demand: not until someone requests the value of one of these paths + * (by calling one of the SHGet functions) is the value synchronized. + * Furthermore, as explained here: + * http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36276.asp + * the HKCU paths take precedence over the HKLM paths. + * + **********************************************************************/ HRESULT WINAPI SHGetFolderPathW( HWND hwndOwner, - int csidl, - HANDLE hToken, /* [in] FIXME: get paths for specific user */ - DWORD dwFlags, /* [in] FIXME: SHGFP_TYPE_CURRENT|SHGFP_TYPE_DEFAULT */ + int nFolder, + HANDLE hToken, + DWORD dwFlags, LPWSTR pszPath) { - WCHAR szBuildPath[MAX_PATH]; - HKEY hRootKey, hKey; - DWORD dwCsidlFlags; - DWORD dwType, dwDisp, dwPathLen = MAX_PATH; - DWORD folder = csidl & CSIDL_FOLDER_MASK; - WCHAR *p; + HRESULT hr; + WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH]; + DWORD folder = nFolder & CSIDL_FOLDER_MASK; + CSIDL_Type type; + WCHAR *p; - TRACE("%p,%p,csidl=0x%04x\n", hwndOwner,pszPath,csidl); - - if (!pszPath) - return E_INVALIDARG; + TRACE("%p,%p,nFolder=0x%04x\n", hwndOwner,pszPath,nFolder); + /* Windows always NULL-terminates the resulting path regardless of success + * or failure, so do so first + */ + if (pszPath) *pszPath = '\0'; - if ((folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0])) || - (CSIDL_Data[folder].hRootKey == HKEY_DISALLOWED)) - return E_INVALIDARG; - if (CSIDL_Data[folder].hRootKey == HKEY_UNIMPLEMENTED) - { - FIXME("folder 0x%04lx unknown, please add.\n", folder); - return E_FAIL; - } - if (CSIDL_Data[folder].hRootKey == HKEY_NONEXISTENT) - return S_FALSE; + if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0])) + return E_INVALIDARG; + type = CSIDL_Data[folder].type; + switch (type) + { + case CSIDL_Type_Disallowed: + hr = E_INVALIDARG; + break; + case CSIDL_Type_NonExistent: + hr = S_FALSE; + break; + case CSIDL_Type_WindowsPath: + GetWindowsDirectoryW(szTemp, MAX_PATH); + if (CSIDL_Data[folder].szDefaultPath && + !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) && + *CSIDL_Data[folder].szDefaultPath) + { + PathAddBackslashW(szTemp); + strcatW(szTemp, CSIDL_Data[folder].szDefaultPath); + } + hr = S_OK; + break; + case CSIDL_Type_SystemPath: + GetSystemDirectoryW(szTemp, MAX_PATH); + if (CSIDL_Data[folder].szDefaultPath && + !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) && + *CSIDL_Data[folder].szDefaultPath) + { + PathAddBackslashW(szTemp); + strcatW(szTemp, CSIDL_Data[folder].szDefaultPath); + } + hr = S_OK; + break; + case CSIDL_Type_CurrVer: + hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp); + break; + case CSIDL_Type_User: + hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp); + break; + case CSIDL_Type_AllUsers: + hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp); + break; + default: + FIXME("bogus type %d, please fix\n", type); + hr = E_INVALIDARG; + } - /* Special case for some values that don't exist in registry */ - if (CSIDL_Data[folder].hRootKey == HKEY_WINDOWSPATH) - { - GetWindowsDirectoryW(pszPath, MAX_PATH); - PathAddBackslashW(pszPath); - strcatW(pszPath, CSIDL_Data[folder].szDefaultPath); - return S_OK; - } - - dwCsidlFlags = CSIDL_Data[folder].dwFlags; - hRootKey = CSIDL_Data[folder].hRootKey; + /* Expand environment strings if necessary */ + if (*szTemp == '%') + hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath); + else + strcpyW(szBuildPath, szTemp); + /* Copy the path if it's available before we might return */ + if (pszPath) + strcpyW(pszPath, szBuildPath); - if (dwCsidlFlags & CSIDL_MYFLAG_SHFOLDER) - { - /* user shell folders */ - if (RegCreateKeyExW(hRootKey,szSHUserFolders,0,NULL,0,KEY_ALL_ACCESS,NULL,&hKey,&dwDisp)) return E_FAIL; + if (FAILED(hr)) goto end; - if (RegQueryValueExW(hKey,CSIDL_Data[folder].szValueName,NULL,&dwType,(LPBYTE)pszPath,&dwPathLen)) - { - RegCloseKey(hKey); + /* if we don't care about existing directories we are ready */ + if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end; - /* shell folders */ - if (RegCreateKeyExW(hRootKey,szSHFolders,0,NULL,0,KEY_ALL_ACCESS,NULL,&hKey,&dwDisp)) return E_FAIL; + if (PathFileExistsW(szBuildPath)) goto end; - if (RegQueryValueExW(hKey,CSIDL_Data[folder].szValueName,NULL,&dwType,(LPBYTE)pszPath,&dwPathLen)) - { + /* not existing but we are not allowed to create it. The return value + * is verified against shell32 version 6.0. + */ + if (!(nFolder & CSIDL_FLAG_CREATE)) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + goto end; + } - /* value not existing */ - if (dwCsidlFlags & CSIDL_MYFLAG_RELATIVE) - { - GetWindowsDirectoryW(pszPath, MAX_PATH); - PathAddBackslashW(pszPath); - strcatW(pszPath, CSIDL_Data[folder].szDefaultPath); - } - else - { - GetSystemDirectoryW(pszPath, MAX_PATH); - strcpyW(pszPath + 3, CSIDL_Data[folder].szDefaultPath); - } - dwType=REG_SZ; - RegSetValueExW(hKey,CSIDL_Data[folder].szValueName,0,REG_SZ,(LPBYTE)pszPath, - (strlenW(pszPath)+1)*sizeof(WCHAR)); - } - } - RegCloseKey(hKey); + /* create directory/directories */ + p = strchrW(szBuildPath, '\\'); + while (p) + { + *p = 0; + if (!PathFileExistsW(szBuildPath)) + { + TRACE("Creating directory %s\n", debugstr_w(szBuildPath)); + if (!CreateDirectoryW(szBuildPath,NULL)) + { + ERR("Failed to create directory '%s'.\n", + debugstr_w(szBuildPath)); + hr = E_FAIL; + goto end; + } } - else - { - LPCWSTR pRegPath; + *p = '\\'; + p = strchrW(p+1, '\\'); + } + /* last component must be created too. */ + if (!PathFileExistsW(szBuildPath)) + { + if (!CreateDirectoryW(szBuildPath,NULL)) + { + ERR("Failed to create directory '%s'.\n", debugstr_w(szBuildPath)); + hr = E_FAIL; + goto end; + } + } - if (dwCsidlFlags & CSIDL_MYFLAG_SETUP) - pRegPath = szSetup; - else if (dwCsidlFlags & CSIDL_MYFLAG_CURRVER) - pRegPath = szCurrentVersion; - else - { - ERR("folder settings broken, please correct !\n"); - return E_FAIL; - } - - if (RegCreateKeyExW(hRootKey,pRegPath,0,NULL,0,KEY_ALL_ACCESS,NULL,&hKey,&dwDisp)) return E_FAIL; - - if (RegQueryValueExW(hKey,CSIDL_Data[folder].szValueName,NULL,&dwType,(LPBYTE)pszPath,&dwPathLen)) - { - /* value not existing */ - if (dwCsidlFlags & CSIDL_MYFLAG_RELATIVE) - { - GetWindowsDirectoryW(pszPath, MAX_PATH); - PathAddBackslashW(pszPath); - strcatW(pszPath, CSIDL_Data[folder].szDefaultPath); - } - else - { - GetSystemDirectoryW(pszPath, MAX_PATH); - strcpyW(pszPath + 3, CSIDL_Data[folder].szDefaultPath); - } - dwType=REG_SZ; - RegSetValueExW(hKey,CSIDL_Data[folder].szValueName,0,REG_SZ,(LPBYTE)pszPath, - (strlenW(pszPath)+1)*sizeof(WCHAR)); - } - RegCloseKey(hKey); - } - - /* expand paths like %USERPROFILE% */ - if (dwType == REG_EXPAND_SZ) - { - ExpandEnvironmentStringsW(pszPath, szBuildPath, MAX_PATH); - strcpyW(pszPath, szBuildPath); - } - - /* if we don't care about existing directories we are ready */ - if(csidl & CSIDL_FLAG_DONT_VERIFY) return S_OK; - - if (PathFileExistsW(pszPath)) return S_OK; - - /* not existing but we are not allowed to create it */ - if (!(csidl & CSIDL_FLAG_CREATE)) return E_FAIL; - - /* create directory/directories */ - strcpyW(szBuildPath, pszPath); - p = strchrW(szBuildPath, '\\'); - while (p) - { - *p = 0; - if (!PathFileExistsW(szBuildPath)) - { - if (!CreateDirectoryW(szBuildPath,NULL)) - { - ERR("Failed to create directory '%s'.\n", debugstr_w(pszPath)); - return E_FAIL; - } - } - *p = '\\'; - p = strchrW(p+1, '\\'); - } - /* last component must be created too. */ - if (!PathFileExistsW(szBuildPath)) - { - if (!CreateDirectoryW(szBuildPath,NULL)) - { - ERR("Failed to create directory '%s'.\n", debugstr_w(pszPath)); - return E_FAIL; - } - } - - TRACE("Created missing system directory '%s'\n", debugstr_w(pszPath)); - return S_OK; + TRACE("Created missing system directory '%s'\n", debugstr_w(szBuildPath)); +end: + TRACE("returning 0x%08lx (final path is %s)\n", hr, + debugstr_w(szBuildPath)); + return hr; } /************************************************************************* @@ -1369,7 +1686,7 @@ HRESULT WINAPI SHGetFolderPathW( */ HRESULT WINAPI SHGetFolderPathA( HWND hwndOwner, - int csidl, + int nFolder, HANDLE hToken, DWORD dwFlags, LPSTR pszPath) @@ -1377,17 +1694,157 @@ HRESULT WINAPI SHGetFolderPathA( WCHAR szTemp[MAX_PATH]; HRESULT hr; - if (!pszPath) - return E_INVALIDARG; + TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder); - *pszPath = '\0'; - hr = SHGetFolderPathW(hwndOwner, csidl, hToken, dwFlags, szTemp); - if (SUCCEEDED(hr)) + if (pszPath) + *pszPath = '\0'; + hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp); + if (SUCCEEDED(hr) && pszPath) WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL, NULL); - TRACE("%p,%p,csidl=0x%04x\n",hwndOwner,pszPath,csidl); + return hr; +} +/* For each folder in folders, if its value has not been set in the registry, + * call _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the + * folder's type) to get the unexpanded value first. + * This will create the expanded value in the Shell Folders key, and + * return the unexpanded value. + * Write the unexpanded value to User Shell Folders, and query it with + * SHGetFolderPath to force the creation of the directory if it doesn't + * already exist. + */ +static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken, + LPCWSTR szUserShellFolderPath, const UINT folders[], UINT foldersLen) +{ + UINT i; + WCHAR path[MAX_PATH]; + HRESULT hr = S_OK; + HKEY hKey = NULL; + DWORD dwDisp, dwType, dwPathLen; + LONG ret; + + TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken, + debugstr_w(szUserShellFolderPath), folders, foldersLen); + + ret = RegCreateKeyExW(hRootKey, szUserShellFolderPath, 0, NULL, 0, + KEY_ALL_ACCESS, NULL, &hKey, &dwDisp); + if (ret) + hr = HRESULT_FROM_WIN32(ret); + for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++) + { + dwPathLen = MAX_PATH * sizeof(WCHAR); + if (RegQueryValueExW(hKey, CSIDL_Data[folders[i]].szValueName, NULL, + &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ && + dwType != REG_EXPAND_SZ)) + { + *path = '\0'; + if (CSIDL_Data[folders[i]].type == CSIDL_Type_User) + _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i], + path); + else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers) + _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path); + else + hr = E_FAIL; + if (*path) + { + ret = RegSetValueExW(hKey, CSIDL_Data[folders[i]].szValueName, + 0, REG_EXPAND_SZ, (LPBYTE)path, + (strlenW(path) + 1) * sizeof(WCHAR)); + if (ret) + hr = HRESULT_FROM_WIN32(ret); + else + hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE, + hToken, SHGFP_TYPE_DEFAULT, NULL); + } + } + } + if (hKey) + RegCloseKey(hKey); + + TRACE("returning 0x%08lx\n", hr); + return hr; +} + +static HRESULT _SHRegisterUserShellFolders(BOOL bDefault) +{ + static const UINT folders[] = { + CSIDL_PROGRAMS, + CSIDL_PERSONAL, + CSIDL_FAVORITES, + CSIDL_STARTUP, + CSIDL_RECENT, + CSIDL_SENDTO, + CSIDL_STARTMENU, + CSIDL_DESKTOPDIRECTORY, + CSIDL_NETHOOD, + CSIDL_TEMPLATES, + CSIDL_PRINTHOOD, + CSIDL_COOKIES, + CSIDL_HISTORY, + }; + WCHAR userShellFolderPath[MAX_PATH]; + LPCWSTR pUserShellFolderPath; + HRESULT hr = S_OK; + HKEY hRootKey; + HANDLE hToken; + + TRACE("%s\n", bDefault ? "TRUE" : "FALSE"); + if (bDefault) + { + hToken = (HANDLE)-1; + hRootKey = HKEY_USERS; + strcpyW(userShellFolderPath, DefaultW); + PathAddBackslashW(userShellFolderPath); + strcatW(userShellFolderPath, szSHUserFolders); + pUserShellFolderPath = userShellFolderPath; + } + else + { + hToken = NULL; + hRootKey = HKEY_CURRENT_USER; + pUserShellFolderPath = szSHUserFolders; + } + + hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath, + folders, sizeof(folders) / sizeof(folders[0])); + TRACE("returning 0x%08lx\n", hr); + return hr; +} + +static HRESULT _SHRegisterCommonShellFolders(void) +{ + static const UINT folders[] = { + CSIDL_COMMON_STARTMENU, + CSIDL_COMMON_PROGRAMS, + CSIDL_COMMON_STARTUP, + CSIDL_COMMON_DESKTOPDIRECTORY, + CSIDL_COMMON_FAVORITES, + CSIDL_COMMON_APPDATA, + CSIDL_COMMON_TEMPLATES, + CSIDL_COMMON_DOCUMENTS, + }; + HRESULT hr; + + TRACE("\n"); + hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders, + folders, sizeof(folders) / sizeof(folders[0])); + TRACE("returning 0x%08lx\n", hr); + return hr; +} + +/* Register the default values in the registry, as some apps seem to depend + * on their presence. The set registered was taken from Windows XP. + */ +HRESULT SHELL_RegisterShellFolders(void) +{ + HRESULT hr = _SHRegisterUserShellFolders(TRUE); + + if (SUCCEEDED(hr)) + hr = _SHRegisterUserShellFolders(FALSE); + if (SUCCEEDED(hr)) + hr = _SHRegisterCommonShellFolders(); return hr; } @@ -1397,12 +1854,12 @@ HRESULT WINAPI SHGetFolderPathA( BOOL WINAPI SHGetSpecialFolderPathA ( HWND hwndOwner, LPSTR szPath, - int csidl, + int nFolder, BOOL bCreate) { return (SHGetFolderPathA( hwndOwner, - csidl + (bCreate ? CSIDL_FLAG_CREATE : 0), + nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0, szPath)) == S_OK ? TRUE : FALSE; @@ -1414,12 +1871,12 @@ BOOL WINAPI SHGetSpecialFolderPathA ( BOOL WINAPI SHGetSpecialFolderPathW ( HWND hwndOwner, LPWSTR szPath, - int csidl, + int nFolder, BOOL bCreate) { return (SHGetFolderPathW( hwndOwner, - csidl + (bCreate ? CSIDL_FLAG_CREATE : 0), + nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0, szPath)) == S_OK ? TRUE : FALSE; @@ -1431,45 +1888,52 @@ BOOL WINAPI SHGetSpecialFolderPathW ( BOOL WINAPI SHGetSpecialFolderPathAW ( HWND hwndOwner, LPVOID szPath, - int csidl, + int nFolder, BOOL bCreate) { if (SHELL_OsIsUnicode()) - return SHGetSpecialFolderPathW (hwndOwner, szPath, csidl, bCreate); - return SHGetSpecialFolderPathA (hwndOwner, szPath, csidl, bCreate); + return SHGetSpecialFolderPathW (hwndOwner, szPath, nFolder, bCreate); + return SHGetSpecialFolderPathA (hwndOwner, szPath, nFolder, bCreate); } /************************************************************************* - * SHGetSpecialFolderLocation [SHELL32.@] - * - * gets the folder locations from the registry and creates a pidl - * creates missing reg keys and directories - * - * PARAMS - * hwndOwner [I] - * nFolder [I] CSIDL_xxxxx - * ppidl [O] PIDL of a special folder + * SHGetFolderLocation [SHELL32.@] + * + * NOTES + * Gets the folder locations from the registry and creates a pidl. + * Creates missing reg keys and directories. + * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return + * virtual folders that are handled here. + * + * PARAMS + * hwndOwner [I] + * nFolder [I] CSIDL_xxxxx + * hToken [I] token representing user, or NULL for current user, or -1 for + * default user + * dwReserved [I] must be zero + * ppidl [O] PIDL of a special folder * * NOTES - * In NT5, SHGetSpecialFolderLocation needs the /Recent - * directory. If the directory is missing it returns a x80070002. - * In most cases, this forwards to SHGetSpecialFolderPath, but - * CSIDLs with virtual folders (not real paths) must be handled - * here. */ -HRESULT WINAPI SHGetSpecialFolderLocation( +HRESULT WINAPI SHGetFolderLocation( HWND hwndOwner, - INT nFolder, - LPITEMIDLIST * ppidl) + int nFolder, + HANDLE hToken, + DWORD dwReserved, + LPITEMIDLIST *ppidl) { HRESULT hr = E_INVALIDARG; - TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl); - + TRACE("%p 0x%08x %p 0x%08lx %p\n", + hwndOwner, nFolder, hToken, dwReserved, ppidl); + if (!ppidl) return E_INVALIDARG; + if (dwReserved) + return E_INVALIDARG; + /* The virtual folders' locations are not user-dependent */ *ppidl = NULL; switch (nFolder) { @@ -1501,26 +1965,26 @@ HRESULT WINAPI SHGetSpecialFolderLocation( *ppidl = _ILCreateNetwork(); break; - case CSIDL_ALTSTARTUP: - case CSIDL_COMMON_ALTSTARTUP: - hr = E_FAIL; - break; - - case CSIDL_COMPUTERSNEARME: - hr = E_FAIL; - break; - default: { WCHAR szPath[MAX_PATH]; - if (SHGetSpecialFolderPathW(hwndOwner, szPath, nFolder, TRUE)) + hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, + SHGFP_TYPE_CURRENT, szPath); + if (SUCCEEDED(hr)) { DWORD attributes=0; TRACE("Value=%s\n", debugstr_w(szPath)); hr = SHILCreateFromPathW(szPath, ppidl, &attributes); } + else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + /* unlike SHGetFolderPath, SHGetFolderLocation in shell32 + * version 6.0 returns E_FAIL for non-existing paths + */ + hr = E_FAIL; + } } } if(*ppidl) @@ -1531,38 +1995,24 @@ HRESULT WINAPI SHGetSpecialFolderLocation( } /************************************************************************* - * SHGetFolderLocation [SHELL32.@] + * SHGetSpecialFolderLocation [SHELL32.@] * * NOTES - * the pidl can be a simple one. since we can't get the path out of the pidl - * we have to take all data from the pidl - * Mostly we forward to SHGetSpecialFolderLocation, but a few special cases - * we handle here. + * In NT5, SHGetSpecialFolderLocation needs the /Recent + * directory. */ -HRESULT WINAPI SHGetFolderLocation( - HWND hwnd, - int csidl, - HANDLE hToken, - DWORD dwFlags, - LPITEMIDLIST *ppidl) +HRESULT WINAPI SHGetSpecialFolderLocation( + HWND hwndOwner, + INT nFolder, + LPITEMIDLIST * ppidl) { - HRESULT hr; + HRESULT hr = E_INVALIDARG; + + TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl); - TRACE_(shell)("%p 0x%08x %p 0x%08lx %p\n", - hwnd, csidl, hToken, dwFlags, ppidl); - if (!ppidl) return E_INVALIDARG; - switch (csidl) - { - case CSIDL_ALTSTARTUP: - case CSIDL_COMMON_ALTSTARTUP: - *ppidl = NULL; - hr = S_FALSE; - break; - default: - hr = SHGetSpecialFolderLocation(hwnd, csidl, ppidl); - } + hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl); return hr; } diff --git a/dlls/shell32/shresdef.h b/dlls/shell32/shresdef.h index f9bd69531ca..d7419cc70f7 100644 --- a/dlls/shell32/shresdef.h +++ b/dlls/shell32/shresdef.h @@ -56,6 +56,34 @@ #define IDS_SHUTDOWN_TITLE 42 #define IDS_SHUTDOWN_PROMPT 43 +#define IDS_PROGRAMS 45 +#define IDS_PERSONAL 46 +#define IDS_FAVORITES 47 +#define IDS_STARTUP 48 +#define IDS_RECENT 49 +#define IDS_SENDTO 50 +#define IDS_STARTMENU 51 +#define IDS_MYMUSIC 52 +#define IDS_MYVIDEO 53 +#define IDS_DESKTOPDIRECTORY 54 +#define IDS_NETHOOD 55 +#define IDS_TEMPLATES 56 +#define IDS_APPDATA 57 +#define IDS_PRINTHOOD 58 +#define IDS_LOCAL_APPDATA 59 +#define IDS_INTERNET_CACHE 60 +#define IDS_COOKIES 61 +#define IDS_HISTORY 62 +#define IDS_PROGRAM_FILES 63 +#define IDS_MYPICTURES 64 +#define IDS_PROGRAM_FILES_COMMON 65 +#define IDS_COMMON_DOCUMENTS 66 +#define IDS_ADMINTOOLS 67 +#define IDS_COMMON_MUSIC 68 +#define IDS_COMMON_PICTURES 69 +#define IDS_COMMON_VIDEO 70 +#define IDS_CDBURN_AREA 71 + /* browse for folder dialog box */ #define IDD_STATUS 0x3743 #define IDD_TITLE 0x3742 diff --git a/dlls/shell32/tests/.cvsignore b/dlls/shell32/tests/.cvsignore index 60387822090..c9506bde897 100644 --- a/dlls/shell32/tests/.cvsignore +++ b/dlls/shell32/tests/.cvsignore @@ -1,5 +1,6 @@ Makefile generated.ok +shellpath.ok shlfileop.ok shlfolder.ok string.ok diff --git a/dlls/shell32/tests/Makefile.in b/dlls/shell32/tests/Makefile.in index db5a3879bb1..7dece8f77f3 100644 --- a/dlls/shell32/tests/Makefile.in +++ b/dlls/shell32/tests/Makefile.in @@ -3,11 +3,12 @@ TOPOBJDIR = ../../.. SRCDIR = @srcdir@ VPATH = @srcdir@ TESTDLL = shell32.dll -IMPORTS = shell32 ole32 +IMPORTS = shell32 ole32 shlwapi advapi32 EXTRALIBS = -luuid CTESTS = \ generated.c \ + shellpath.c \ shlfileop.c \ shlfolder.c \ string.c diff --git a/dlls/shell32/tests/shellpath.c b/dlls/shell32/tests/shellpath.c new file mode 100644 index 00000000000..31b3e874b15 --- /dev/null +++ b/dlls/shell32/tests/shellpath.c @@ -0,0 +1,897 @@ +/* + * Unit tests for shell32 SHGet{Special}Folder{Path|Location} functions. + * + * Copyright 2004 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * This is a test program for the SHGet{Special}Folder{Path|Location} functions + * of shell32, that get either a filesytem path or a LPITEMIDLIST (shell + * namespace) path for a given folder (CSIDL value). + * + * FIXME: + * - Need to verify on more systems. + */ +#include +#include +#include "windef.h" +#include "winbase.h" +#include "initguid.h" +#include "shlguid.h" +#include "shlobj.h" +#include "shlwapi.h" +#include "wine/test.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) ) +#endif + +/* from pidl.h, not included here: */ +#ifndef PT_GUID +#define PT_GUID 0x1f /* no path */ +#endif +#ifndef PT_DRIVE2 +#define PT_DRIVE2 0x25 /* has path */ +#endif +#ifndef PT_SHELLEXT +#define PT_SHELLEXT 0x2e /* no path */ +#endif +#ifndef PT_FOLDER +#define PT_FOLDER 0x31 /* has path */ +#endif +#ifndef PT_WORKGRP +#define PT_WORKGRP 0x41 /* no path */ +#endif +#ifndef PT_PRINTERS +#define PT_PRINTERS 0x70 /* no path */ +#endif +/* FIXME: this is used for history/favorites folders; what's a better name? */ +#ifndef PT_IESPECIAL2 +#define PT_IESPECIAL2 0xb1 /* has path */ +#endif + +static GUID CLSID_CommonDocuments = { 0x0000000c, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x1a } }; + +struct shellExpectedValues { + int folder; + BYTE pidlType; +}; + +static HMODULE hShell32; +static HRESULT WINAPI (*pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR); +static HRESULT WINAPI (*pSHGetFolderLocation)(HWND, int, HANDLE, DWORD, + LPITEMIDLIST *); +static BOOL WINAPI (*pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL); +static HRESULT WINAPI (*pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *); +static LPITEMIDLIST WINAPI (*pILFindLastID)(LPCITEMIDLIST); +static int WINAPI (*pSHFileOperationA)(LPSHFILEOPSTRUCTA); +static HRESULT WINAPI (*pSHGetMalloc)(LPMALLOC *); +static DLLVERSIONINFO shellVersion = { 0 }; +static LPMALLOC pMalloc; +static const struct shellExpectedValues requiredShellValues[] = { + { CSIDL_BITBUCKET, PT_GUID }, +/* FIXME: the following fails in Wine, returns type PT_FOLDER + { CSIDL_CONTROLS, PT_SHELLEXT }, + */ + { CSIDL_COOKIES, PT_FOLDER }, + { CSIDL_DESKTOPDIRECTORY, PT_FOLDER }, + { CSIDL_DRIVES, PT_GUID }, + { CSIDL_FAVORITES, PT_FOLDER }, + { CSIDL_FONTS, PT_FOLDER }, +/* FIXME: the following fails in Wine, returns type PT_FOLDER + { CSIDL_HISTORY, PT_IESPECIAL2 }, + */ + { CSIDL_INTERNET, PT_GUID }, + { CSIDL_NETHOOD, PT_FOLDER }, + { CSIDL_NETWORK, PT_GUID }, +/* FIXME: the following fails in Wine, returns type PT_FOLDER + { CSIDL_PRINTERS, PT_PRINTERS }, + */ + { CSIDL_PRINTHOOD, PT_FOLDER }, + { CSIDL_PROGRAMS, PT_FOLDER }, + { CSIDL_RECENT, PT_FOLDER }, + { CSIDL_SENDTO, PT_FOLDER }, + { CSIDL_STARTMENU, PT_FOLDER }, + { CSIDL_STARTUP, PT_FOLDER }, + { CSIDL_TEMPLATES, PT_FOLDER }, +}; +static const struct shellExpectedValues optionalShellValues[] = { +/* FIXME: the following only semi-succeed; they return NULL PIDLs on XP.. hmm. + { CSIDL_ALTSTARTUP, PT_FOLDER }, + { CSIDL_COMMON_ALTSTARTUP, PT_FOLDER }, + { CSIDL_COMMON_OEM_LINKS, PT_FOLDER }, + */ +/* Windows NT-only: */ + { CSIDL_COMMON_DESKTOPDIRECTORY, PT_FOLDER }, + { CSIDL_COMMON_DOCUMENTS, PT_SHELLEXT }, + { CSIDL_COMMON_FAVORITES, PT_FOLDER }, + { CSIDL_COMMON_PROGRAMS, PT_FOLDER }, + { CSIDL_COMMON_STARTMENU, PT_FOLDER }, + { CSIDL_COMMON_STARTUP, PT_FOLDER }, + { CSIDL_COMMON_TEMPLATES, PT_FOLDER }, +/* first appearing in shell32 version 4.71: */ + { CSIDL_APPDATA, PT_FOLDER }, +/* first appearing in shell32 version 4.72: */ + { CSIDL_INTERNET_CACHE, PT_IESPECIAL2 }, +/* first appearing in shell32 version 5.0: */ + { CSIDL_ADMINTOOLS, PT_FOLDER }, + { CSIDL_COMMON_APPDATA, PT_FOLDER }, + { CSIDL_LOCAL_APPDATA, PT_FOLDER }, + { CSIDL_MYDOCUMENTS, PT_FOLDER }, + { CSIDL_MYMUSIC, PT_FOLDER }, + { CSIDL_MYPICTURES, PT_FOLDER }, + { CSIDL_MYVIDEO, PT_FOLDER }, + { CSIDL_PROFILE, PT_FOLDER }, + { CSIDL_PROGRAM_FILES, PT_FOLDER }, + { CSIDL_PROGRAM_FILESX86, PT_FOLDER }, + { CSIDL_PROGRAM_FILES_COMMON, PT_FOLDER }, + { CSIDL_PROGRAM_FILES_COMMONX86, PT_FOLDER }, + { CSIDL_SYSTEM, PT_FOLDER }, + { CSIDL_WINDOWS, PT_FOLDER }, +/* first appearing in shell32 6.0: */ + { CSIDL_CDBURN_AREA, PT_FOLDER }, + { CSIDL_COMMON_MUSIC, PT_FOLDER }, + { CSIDL_COMMON_PICTURES, PT_FOLDER }, + { CSIDL_COMMON_VIDEO, PT_FOLDER }, + { CSIDL_COMPUTERSNEARME, PT_WORKGRP }, + { CSIDL_RESOURCES, PT_FOLDER }, + { CSIDL_RESOURCES_LOCALIZED, PT_FOLDER }, +}; + +static void loadShell32(void) +{ + hShell32 = LoadLibraryA("shell32"); + if (hShell32) + { + HRESULT WINAPI (*pDllGetVersion)(DLLVERSIONINFO *); + + pSHGetFolderPathA = (void *)GetProcAddress(hShell32, + "SHGetFolderPathA"); + pSHGetFolderLocation = (void *)GetProcAddress(hShell32, + "SHGetFolderLocation"); + pSHGetSpecialFolderPathA = (void *)GetProcAddress(hShell32, + "SHGetSpecialFolderPathA"); + pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, + "SHGetSpecialFolderLocation"); + pDllGetVersion = (void *)GetProcAddress(hShell32, "DllGetVersion"); + pILFindLastID = (void *)GetProcAddress(hShell32, "ILFindLastID"); + if (!pILFindLastID) + pILFindLastID = (void *)GetProcAddress(hShell32, (LPCSTR)16); + pSHFileOperationA = (void *)GetProcAddress(hShell32, + "SHFileOperationA"); + pSHGetMalloc = (void *)GetProcAddress(hShell32, "SHGetMalloc"); + + ok(pSHGetMalloc != NULL, "shell32 is missing SHGetMalloc\n"); + if (pSHGetMalloc) + { + HRESULT hr = pSHGetMalloc(&pMalloc); + + ok(SUCCEEDED(hr), "SHGetMalloc failed: 0x%08lx\n", hr); + ok(pMalloc != NULL, "SHGetMalloc returned a NULL IMalloc\n"); + } + + if (pDllGetVersion) + { + shellVersion.cbSize = sizeof(shellVersion); + pDllGetVersion(&shellVersion); + if (winetest_interactive) + printf("shell32 version is %ld.%ld\n", + shellVersion.dwMajorVersion, shellVersion.dwMinorVersion); + } + } +} + +/* A couple utility printing functions */ +static const char *getFolderName(int folder) +{ + static char unknown[17]; + +#define CSIDL_TO_STR(x) case x: return#x; + switch (folder) + { + CSIDL_TO_STR(CSIDL_DESKTOP); + CSIDL_TO_STR(CSIDL_INTERNET); + CSIDL_TO_STR(CSIDL_PROGRAMS); + CSIDL_TO_STR(CSIDL_CONTROLS); + CSIDL_TO_STR(CSIDL_PRINTERS); + CSIDL_TO_STR(CSIDL_PERSONAL); + CSIDL_TO_STR(CSIDL_FAVORITES); + CSIDL_TO_STR(CSIDL_STARTUP); + CSIDL_TO_STR(CSIDL_RECENT); + CSIDL_TO_STR(CSIDL_SENDTO); + CSIDL_TO_STR(CSIDL_BITBUCKET); + CSIDL_TO_STR(CSIDL_STARTMENU); + CSIDL_TO_STR(CSIDL_MYDOCUMENTS); + CSIDL_TO_STR(CSIDL_MYMUSIC); + CSIDL_TO_STR(CSIDL_MYVIDEO); + CSIDL_TO_STR(CSIDL_DESKTOPDIRECTORY); + CSIDL_TO_STR(CSIDL_DRIVES); + CSIDL_TO_STR(CSIDL_NETWORK); + CSIDL_TO_STR(CSIDL_NETHOOD); + CSIDL_TO_STR(CSIDL_FONTS); + CSIDL_TO_STR(CSIDL_TEMPLATES); + CSIDL_TO_STR(CSIDL_COMMON_STARTMENU); + CSIDL_TO_STR(CSIDL_COMMON_PROGRAMS); + CSIDL_TO_STR(CSIDL_COMMON_STARTUP); + CSIDL_TO_STR(CSIDL_COMMON_DESKTOPDIRECTORY); + CSIDL_TO_STR(CSIDL_APPDATA); + CSIDL_TO_STR(CSIDL_PRINTHOOD); + CSIDL_TO_STR(CSIDL_LOCAL_APPDATA); + CSIDL_TO_STR(CSIDL_ALTSTARTUP); + CSIDL_TO_STR(CSIDL_COMMON_ALTSTARTUP); + CSIDL_TO_STR(CSIDL_COMMON_FAVORITES); + CSIDL_TO_STR(CSIDL_INTERNET_CACHE); + CSIDL_TO_STR(CSIDL_COOKIES); + CSIDL_TO_STR(CSIDL_HISTORY); + CSIDL_TO_STR(CSIDL_COMMON_APPDATA); + CSIDL_TO_STR(CSIDL_WINDOWS); + CSIDL_TO_STR(CSIDL_SYSTEM); + CSIDL_TO_STR(CSIDL_PROGRAM_FILES); + CSIDL_TO_STR(CSIDL_MYPICTURES); + CSIDL_TO_STR(CSIDL_PROFILE); + CSIDL_TO_STR(CSIDL_SYSTEMX86); + CSIDL_TO_STR(CSIDL_PROGRAM_FILESX86); + CSIDL_TO_STR(CSIDL_PROGRAM_FILES_COMMON); + CSIDL_TO_STR(CSIDL_PROGRAM_FILES_COMMONX86); + CSIDL_TO_STR(CSIDL_COMMON_TEMPLATES); + CSIDL_TO_STR(CSIDL_COMMON_DOCUMENTS); + CSIDL_TO_STR(CSIDL_COMMON_ADMINTOOLS); + CSIDL_TO_STR(CSIDL_ADMINTOOLS); + CSIDL_TO_STR(CSIDL_CONNECTIONS); + CSIDL_TO_STR(CSIDL_PROFILES); + CSIDL_TO_STR(CSIDL_COMMON_MUSIC); + CSIDL_TO_STR(CSIDL_COMMON_PICTURES); + CSIDL_TO_STR(CSIDL_COMMON_VIDEO); + CSIDL_TO_STR(CSIDL_RESOURCES); + CSIDL_TO_STR(CSIDL_RESOURCES_LOCALIZED); + CSIDL_TO_STR(CSIDL_COMMON_OEM_LINKS); + CSIDL_TO_STR(CSIDL_CDBURN_AREA); + CSIDL_TO_STR(CSIDL_COMPUTERSNEARME); +#undef CSIDL_TO_STR + default: + snprintf(unknown, sizeof(unknown), "unknown (0x%04x)", folder); + return unknown; + } +} + +static const char *printGUID(const GUID *guid) +{ + static char guidSTR[39]; + + if (!guid) return NULL; + + snprintf(guidSTR, sizeof(guidSTR), + "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + return guidSTR; +} + +static void testSHGetFolderLocationInvalidArgs(void) +{ + LPITEMIDLIST pidl; + HRESULT hr; + + if (!pSHGetFolderLocation) return; + + /* check a bogus CSIDL: */ + pidl = NULL; + hr = pSHGetFolderLocation(NULL, 0xeeee, NULL, 0, &pidl); + ok(hr == E_INVALIDARG, + "SHGetFolderLocation(NULL, 0xeeee, NULL, 0, &pidl)\n" + "returned 0x%08lx, expected E_INVALIDARG\n", hr); + if (SUCCEEDED(hr)) + pMalloc->lpVtbl->Free(pMalloc, pidl); + /* check a bogus user token: */ + pidl = NULL; + hr = pSHGetFolderLocation(NULL, CSIDL_FAVORITES, (HANDLE)2, 0, &pidl); + ok(hr == E_FAIL, + "SHGetFolderLocation(NULL, CSIDL_FAVORITES, 2, 0, &pidl)\n" + "returned 0x%08lx, expected E_FAIL\n", hr); + if (SUCCEEDED(hr)) + pMalloc->lpVtbl->Free(pMalloc, pidl); + /* check reserved is not zero: */ + pidl = NULL; + hr = pSHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 1, &pidl); + ok(hr == E_INVALIDARG, + "SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 1, &pidl)\n" + "returned 0x%08lx, expected E_INVALIDARG\n", hr); + if (SUCCEEDED(hr)) + pMalloc->lpVtbl->Free(pMalloc, pidl); + /* a NULL pidl pointer crashes, so don't test it */ +} + +static void testSHGetSpecialFolderLocationInvalidArgs(void) +{ + LPITEMIDLIST pidl = NULL; + HRESULT hr; + + if (!pSHGetSpecialFolderLocation) return; + + /* SHGetSpecialFolderLocation(NULL, 0, NULL) crashes */ + hr = pSHGetSpecialFolderLocation(NULL, 0xeeee, &pidl); + ok(hr == E_INVALIDARG, + "SHGetSpecialFolderLocation(NULL, 0xeeee, &pidl) returned 0x%08lx, " + "expected E_INVALIDARG\n", hr); +} + +static void testSHGetFolderPathInvalidArgs(void) +{ + char path[MAX_PATH]; + HRESULT hr; + + if (!pSHGetFolderPathA) return; + + /* expect 2's a bogus handle, especially since we didn't open it */ + hr = pSHGetFolderPathA(NULL, CSIDL_DESKTOP, (HANDLE)2, + SHGFP_TYPE_DEFAULT, path); + ok(hr == E_FAIL, + "SHGetFolderPathA(NULL, CSIDL_DESKTOP, 2, SHGFP_TYPE_DEFAULT, path)\n" + "returned 0x%08lx, expected E_FAIL\n", hr); + hr = pSHGetFolderPathA(NULL, 0xeeee, NULL, SHGFP_TYPE_DEFAULT, path); + ok(hr == E_INVALIDARG, + "SHGetFolderPathA(NULL, 0xeeee, NULL, SHGFP_TYPE_DEFAULT, path)\n" + "returned 0x%08lx, expected E_INVALIDARG\n", hr); +} + +static void testSHGetSpecialFolderPathInvalidArgs(void) +{ + char path[MAX_PATH]; + BOOL ret; + + if (!pSHGetSpecialFolderPathA) return; + + ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE); + ok(!ret, + "SHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE)\n" + "returned TRUE, expected FALSE\n"); + /* odd but true: calling with a NULL path still succeeds if it's a real + * dir + */ + ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_PROGRAMS, FALSE); + ok(ret, + "SHGetSpecialFolderPathA(NULL, NULL, CSIDL_PROGRAMS, FALSE)\n" + "returned FALSE, expected TRUE\n"); + ret = pSHGetSpecialFolderPathA(NULL, path, 0xeeee, FALSE); + ok(!ret, + "SHGetSpecialFolderPathA(NULL, path, 0xeeee, FALSE)\n" + "returned TRUE, expected FALSE\n"); +} + +static void testApiParameters(void) +{ + testSHGetFolderLocationInvalidArgs(); + testSHGetSpecialFolderLocationInvalidArgs(); + testSHGetFolderPathInvalidArgs(); + testSHGetSpecialFolderPathInvalidArgs(); +} + +/* Returns the folder's PIDL type, or 0xff if one can't be found. */ +static BYTE testSHGetFolderLocation(BOOL optional, int folder) +{ + LPITEMIDLIST pidl; + HRESULT hr; + BYTE ret = 0xff; + + /* treat absence of function as success */ + if (!pSHGetFolderLocation) return TRUE; + + pidl = NULL; + hr = pSHGetFolderLocation(NULL, folder, NULL, 0, &pidl); + ok(SUCCEEDED(hr) || optional, + "SHGetFolderLocation(NULL, %s, NULL, 0, &pidl)\n" + "failed: 0x%08lx\n", getFolderName(folder), hr); + if (SUCCEEDED(hr)) + { + ok(pidl != NULL, + "SHGetFolderLocation(NULL, %s, NULL, 0, &pidl)\n" + "succeeded, but returned pidl is NULL\n", getFolderName(folder)); + if (pidl) + { + LPITEMIDLIST pidlLast = pILFindLastID(pidl); + + ok(pidlLast != NULL, "%s: ILFindLastID failed\n", + getFolderName(folder)); + if (pidlLast) + ret = pidlLast->mkid.abID[0]; + pMalloc->lpVtbl->Free(pMalloc, pidl); + } + } + return ret; +} + +/* Returns the folder's PIDL type, or 0xff if one can't be found. */ +static BYTE testSHGetSpecialFolderLocation(BOOL optional, int folder) +{ + LPITEMIDLIST pidl; + HRESULT hr; + BYTE ret = 0xff; + + /* treat absence of function as success */ + if (!pSHGetSpecialFolderLocation) return TRUE; + + pidl = NULL; + hr = pSHGetSpecialFolderLocation(NULL, folder, &pidl); + ok(SUCCEEDED(hr) || optional, + "SHGetSpecialFolderLocation(NULL, %s, &pidl)\n" + "failed: 0x%08lx\n", getFolderName(folder), hr); + if (SUCCEEDED(hr)) + { + ok(pidl != NULL, + "SHGetSpecialFolderLocation(NULL, %s, &pidl)\n" + "succeeded, but returned pidl is NULL\n", getFolderName(folder)); + if (pidl) + { + LPITEMIDLIST pidlLast = pILFindLastID(pidl); + + ok(pidlLast != NULL, + "%s: ILFindLastID failed\n", getFolderName(folder)); + if (pidlLast) + ret = pidlLast->mkid.abID[0]; + pMalloc->lpVtbl->Free(pMalloc, pidl); + } + } + return ret; +} + +static void testSHGetFolderPath(BOOL optional, int folder) +{ + char path[MAX_PATH]; + HRESULT hr; + + if (!pSHGetFolderPathA) return; + + hr = pSHGetFolderPathA(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path); + ok(SUCCEEDED(hr) || optional, + "SHGetFolderPathA(NULL, %s, NULL, SHGFP_TYPE_CURRENT, path)\n" + "failed: 0x%08lx\n", getFolderName(folder), hr); +} + +static void testSHGetSpecialFolderPath(BOOL optional, int folder) +{ + char path[MAX_PATH]; + BOOL ret; + + if (!pSHGetSpecialFolderPathA) return; + + ret = pSHGetSpecialFolderPathA(NULL, path, folder, FALSE); + if (ret && winetest_interactive) + printf("%s: %s\n", getFolderName(folder), path); + ok(ret || optional, + "SHGetSpecialFolderPathA(NULL, path, %s, FALSE) failed\n", + getFolderName(folder)); +} + +static void testShellValues(const struct shellExpectedValues testEntries[], + int numEntries, BOOL optional) +{ + int i; + + for (i = 0; i < numEntries; i++) + { + BYTE type; + + type = testSHGetFolderLocation(optional, testEntries[i].folder); + ok(type == testEntries[i].pidlType || optional, + "%s has type %d (0x%02x), expected %d (0x%02x)\n", + getFolderName(testEntries[i].folder), type, type, + testEntries[i].pidlType, testEntries[i].pidlType); + type = testSHGetSpecialFolderLocation(optional, testEntries[i].folder); + ok(type == testEntries[i].pidlType || optional, + "%s has type %d (0x%02x), expected %d (0x%02x)\n", + getFolderName(testEntries[i].folder), type, type, + testEntries[i].pidlType, testEntries[i].pidlType); + switch (type) + { + case PT_FOLDER: + case PT_DRIVE2: + case PT_IESPECIAL2: + testSHGetFolderPath(optional, testEntries[i].folder); + testSHGetSpecialFolderPath(optional, testEntries[i].folder); + break; + } + } +} + +/* Attempts to verify that the folder path corresponding to the folder CSIDL + * value has the same value as the environment variable with name envVar. + * Doesn't mind if SHGetSpecialFolderPath fails for folder or if envVar isn't + * set in this environment; different OS and shell version behave differently. + * However, if both are present, fails if envVar's value is not the same + * (byte-for-byte) as what SHGetSpecialFolderPath returns. + */ +static void matchSpecialFolderPathToEnv(int folder, const char *envVar) +{ + char path[MAX_PATH]; + + if (!pSHGetSpecialFolderPathA) return; + + if (pSHGetSpecialFolderPathA(NULL, path, folder, FALSE)) + { + char *envVal = getenv(envVar); + + ok(!envVal || !strcasecmp(envVal, path), + "%%%s%% does not match SHGetSpecialFolderPath:\n" + "%%%s%% is %s\nSHGetSpecialFolderPath returns %s\n", + envVar, envVar, envVal, path); + } +} + +/* Attempts to match the GUID returned by SHGetFolderLocation for folder with + * GUID. Assumes the type of the returned PIDL is in fact a GUID, but doesn't + * fail if it isn't--that check should already have been done. + * Fails if the returned PIDL is a GUID whose value does not match guid. + */ +static void matchGUID(int folder, const GUID *guid) +{ + LPITEMIDLIST pidl; + HRESULT hr; + + if (!pSHGetFolderLocation) return; + if (!guid) return; + + pidl = NULL; + hr = pSHGetFolderLocation(NULL, folder, NULL, 0, &pidl); + if (SUCCEEDED(hr)) + { + LPITEMIDLIST pidlLast = pILFindLastID(pidl); + + if (pidlLast && (pidlLast->mkid.abID[0] == PT_SHELLEXT || + pidlLast->mkid.abID[0] == PT_GUID)) + { + GUID *shellGuid = (GUID *)(pidlLast->mkid.abID + 2); + + ok(IsEqualIID(shellGuid, guid), + "%s: got GUID %s, expected %s\n", getFolderName(folder), + printGUID(shellGuid), printGUID(guid)); + } + pMalloc->lpVtbl->Free(pMalloc, pidl); + } +} + +static void testDesktop(void) +{ + testSHGetFolderPath(FALSE, CSIDL_DESKTOP); + testSHGetSpecialFolderPath(FALSE, CSIDL_DESKTOP); + /* Test the desktop; even though SHITEMID should always contain abID of at + * least one type, when cb is 0 its value is undefined. So don't check + * what the returned type is, just make sure it exists. + */ + testSHGetFolderLocation(FALSE, CSIDL_DESKTOP); + testSHGetSpecialFolderLocation(FALSE, CSIDL_DESKTOP); +} + +static void testPersonal(void) +{ + BYTE type; + + type = testSHGetFolderLocation(FALSE, CSIDL_PERSONAL); + ok(type == PT_FOLDER || type == PT_GUID, + "CSIDL_PERSONAL returned invalid type 0x%02x, " + "expected PT_FOLDER or PT_GUID\n", type); + if (type == PT_FOLDER) + testSHGetFolderPath(FALSE, CSIDL_PERSONAL); + type = testSHGetSpecialFolderLocation(FALSE, CSIDL_PERSONAL); + ok(type == PT_FOLDER || type == PT_GUID, + "CSIDL_PERSONAL returned invalid type 0x%02x, " + "expected PT_FOLDER or PT_GUID\n", type); + if (type == PT_FOLDER) + testSHGetSpecialFolderPath(FALSE, CSIDL_PERSONAL); +} + +/* Checks the PIDL type of all the known values. */ +static void testPidlTypes(void) +{ + testDesktop(); + testPersonal(); + testShellValues(requiredShellValues, ARRAY_SIZE(requiredShellValues), + FALSE); + testShellValues(optionalShellValues, ARRAY_SIZE(optionalShellValues), + TRUE); +} + +/* Verifies various shell virtual folders have the correct well-known GUIDs. */ +static void testGUIDs(void) +{ + matchGUID(CSIDL_BITBUCKET, &CLSID_RecycleBin); + matchGUID(CSIDL_CONTROLS, &CLSID_ControlPanel); + matchGUID(CSIDL_DRIVES, &CLSID_MyComputer); + matchGUID(CSIDL_INTERNET, &CLSID_Internet); + matchGUID(CSIDL_NETWORK, &CLSID_NetworkPlaces); + matchGUID(CSIDL_PERSONAL, &CLSID_MyDocuments); + matchGUID(CSIDL_COMMON_DOCUMENTS, &CLSID_CommonDocuments); +} + +/* Verifies various shell paths match the environment variables to which they + * correspond. + */ +static void testEnvVars(void) +{ + matchSpecialFolderPathToEnv(CSIDL_PROGRAM_FILES, "ProgramFiles"); + matchSpecialFolderPathToEnv(CSIDL_APPDATA, "APPDATA"); + matchSpecialFolderPathToEnv(CSIDL_PROFILE, "USERPROFILE"); + matchSpecialFolderPathToEnv(CSIDL_WINDOWS, "SystemRoot"); + matchSpecialFolderPathToEnv(CSIDL_WINDOWS, "windir"); + matchSpecialFolderPathToEnv(CSIDL_PROGRAM_FILES_COMMON, + "CommonProgramFiles"); + /* this is only set on Wine, but can't hurt to verify it: */ + matchSpecialFolderPathToEnv(CSIDL_SYSTEM, "winsysdir"); +} + +/* Verifies the shell path for CSIDL_WINDOWS matches the return from + * GetWindowsDirectory. If SHGetSpecialFolderPath fails, no harm, no foul--not + * every shell32 version supports CSIDL_WINDOWS. + */ +static void testWinDir(void) +{ + char windowsShellPath[MAX_PATH], windowsDir[MAX_PATH] = { 0 }; + + if (!pSHGetSpecialFolderPathA) return; + + if (pSHGetSpecialFolderPathA(NULL, windowsShellPath, CSIDL_WINDOWS, FALSE)) + { + PathRemoveBackslashA(windowsShellPath); + GetWindowsDirectoryA(windowsDir, sizeof(windowsDir)); + PathRemoveBackslashA(windowsDir); + ok(!strcasecmp(windowsDir, windowsShellPath), + "GetWindowsDirectory does not match SHGetSpecialFolderPath:\n" + "GetWindowsDirectory returns %s\nSHGetSpecialFolderPath returns %s\n", + windowsDir, windowsShellPath); + } +} + +/* Verifies the shell path for CSIDL_SYSTEM and CSIDL_SYSTEMX86 matches the + * return from GetSystemDirectory. If SHGetSpecialFolderPath fails, no harm, + * no foul--not every shell32 version supports CSIDL_SYSTEM. + */ +static void testSystemDir(void) +{ + char systemShellPath[MAX_PATH], systemDir[MAX_PATH] = { 0 }; + + if (!pSHGetSpecialFolderPathA) return; + + GetSystemDirectoryA(systemDir, sizeof(systemDir)); + PathRemoveBackslashA(systemDir); + if (pSHGetSpecialFolderPathA(NULL, systemShellPath, CSIDL_SYSTEM, FALSE)) + { + PathRemoveBackslashA(systemShellPath); + ok(!strcasecmp(systemDir, systemShellPath), + "GetSystemDirectory does not match SHGetSpecialFolderPath:\n" + "GetSystemDirectory returns %s\nSHGetSpecialFolderPath returns %s\n", + systemDir, systemShellPath); + } + /* check CSIDL_SYSTEMX86; note that this isn't always present, so don't + * worry if it fails + */ + if (pSHGetSpecialFolderPathA(NULL, systemShellPath, CSIDL_SYSTEMX86, FALSE)) + { + PathRemoveBackslashA(systemShellPath); + ok(!strcasecmp(systemDir, systemShellPath), + "GetSystemDirectory does not match SHGetSpecialFolderPath:\n" + "GetSystemDirectory returns %s\nSHGetSpecialFolderPath returns %s\n", + systemDir, systemShellPath); + } +} + +/* Globals used by subprocesses */ +static int myARGC; +static char **myARGV; +static char base[MAX_PATH]; +static char selfname[MAX_PATH]; + +static int init(void) +{ + myARGC = winetest_get_mainargs(&myARGV); + if (!GetCurrentDirectoryA(sizeof(base), base)) return 0; + strcpy(selfname, myARGV[0]); + return 1; +} + +/* Subprocess helper 1: test what happens when CSIDL_FAVORITES is set to a + * non-existent directory. + */ +static void testNonExistentPath1(void) +{ + HRESULT hr; + LPITEMIDLIST pidl; + char path[MAX_PATH]; + + /* test some failure cases first: */ + hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES, NULL, + SHGFP_TYPE_CURRENT, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "SHGetFolderPath returned 0x%08lx, expected 0x80070002\n", hr); + pidl = NULL; + hr = pSHGetFolderLocation(NULL, CSIDL_FAVORITES, NULL, 0, + &pidl); + ok(hr == E_FAIL, + "SHGetFolderLocation returned 0x%08lx, expected E_FAIL\n", hr); + if (SUCCEEDED(hr) && pidl) + pMalloc->lpVtbl->Free(pMalloc, pidl); + ok(!pSHGetSpecialFolderPathA(NULL, path, CSIDL_FAVORITES, FALSE), + "SHGetSpecialFolderPath succeeded, expected failure\n"); + pidl = NULL; + hr = pSHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl); + ok(hr == E_FAIL, "SHGetFolderLocation returned 0x%08lx, expected E_FAIL\n", + hr); + if (SUCCEEDED(hr) && pidl) + pMalloc->lpVtbl->Free(pMalloc, pidl); + /* now test success: */ + hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL, + SHGFP_TYPE_CURRENT, path); + if (SUCCEEDED(hr)) + { + BOOL ret; + + if (winetest_interactive) + printf("CSIDL_FAVORITES was changed to %s\n", path); + ret = CreateDirectoryA(path, NULL); + ok(!ret, + "CreateDirectoryA succeeded but should have failed " + "with ERROR_ALREADY_EXISTS\n"); + if (!ret) + ok(GetLastError() == ERROR_ALREADY_EXISTS, + "CreateDirectoryA failed with %ld, " + "expected ERROR_ALREADY_EXISTS\n", + GetLastError()); + } + ok(SUCCEEDED(hr), + "SHGetFolderPath(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, " + "NULL, SHGFP_TYPE_CURRENT, path)\nfailed: 0x%08lx\n", hr); +} + +/* Subprocess helper 2: make sure SHGetFolderPath still succeeds when the + * original value of CSIDL_FAVORITES is restored. + */ +static void testNonExistentPath2(void) +{ + HRESULT hr; + + hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL, + SHGFP_TYPE_CURRENT, NULL); + ok(SUCCEEDED(hr), "SHGetFolderPath failed: 0x%08lx\n", hr); +} + +static void doChild(const char *arg) +{ + if (arg[0] == '1') + testNonExistentPath1(); + else if (arg[0] == '2') + testNonExistentPath2(); +} + +/* Tests the return values from the various shell functions both with and + * without the use of the CSIDL_FLAG_CREATE flag. This flag only appeared in + * version 5 of the shell, so don't test unless it's at least version 5. + * The test reads a value from the registry, modifies it, calls + * SHGetFolderPath once with the CSIDL_FLAG_CREATE flag, and immediately + * afterward without it. Then it restores the registry and deletes the folder + * that was created. + * One oddity with respect to restoration: shell32 caches somehow, so it needs + * to be reloaded in order to see the correct (restored) value. + * Some APIs unrelated to the ones under test may fail, but I expect they're + * covered by other unit tests; I just print out something about failure to + * help trace what's going on. + * FIXME: this is basically a no-op under Wine, since it only reports shell32 + * version 4.72. + */ +static void testNonExistentPath(void) +{ + static const char userShellFolders[] = + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders"; + char originalPath[MAX_PATH], modifiedPath[MAX_PATH]; + HKEY key; + + if (!pSHGetFolderPathA) return; + if (!pSHGetFolderLocation) return; + if (!pSHGetSpecialFolderPathA) return; + if (!pSHGetSpecialFolderLocation) return; + if (!pSHFileOperationA) return; + if (shellVersion.dwMajorVersion < 5) return; + + if (!RegOpenKeyExA(HKEY_CURRENT_USER, userShellFolders, 0, KEY_ALL_ACCESS, + &key)) + { + DWORD len, type; + + len = sizeof(originalPath); + if (!RegQueryValueExA(key, "Favorites", NULL, &type, + (LPBYTE)&originalPath, &len)) + { + size_t len = strlen(originalPath); + + memcpy(modifiedPath, originalPath, len); + modifiedPath[len++] = '2'; + modifiedPath[len++] = '\0'; + if (winetest_interactive) + printf("Changing CSIDL_FAVORITES to %s\n", modifiedPath); + if (!RegSetValueExA(key, "Favorites", 0, type, modifiedPath, len)) + { + char buffer[MAX_PATH]; + STARTUPINFOA startup; + PROCESS_INFORMATION info; + HRESULT hr; + + snprintf(buffer, sizeof(buffer), "%s tests/shellpath.c 1", + selfname); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.dwFlags = SW_SHOWNORMAL; + CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, + &startup, &info); + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, + "child process termination\n"); + + /* Query the path to be able to delete it below */ + hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES, NULL, + SHGFP_TYPE_CURRENT, modifiedPath); + ok(SUCCEEDED(hr), "SHGetFolderPathA failed: 0x%08lx\n", hr); + + /* restore original values: */ + if (winetest_interactive) + printf("Restoring CSIDL_FAVORITES to %s\n", originalPath); + RegSetValueExA(key, "Favorites", 0, type, originalPath, + strlen(originalPath) + 1); + RegFlushKey(key); + + snprintf(buffer, sizeof(buffer), "%s tests/shellpath.c 2", + selfname); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.dwFlags = SW_SHOWNORMAL; + CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, + &startup, &info); + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, + "child process termination\n"); + + ok(RemoveDirectoryA(modifiedPath), + "RemoveDirectoryA failed: %ld\n", GetLastError()); + } + } + else if (winetest_interactive) + printf("RegQueryValueExA(key, Favorites, ...) failed\n"); + if (key) + RegCloseKey(key); + } + else if (winetest_interactive) + printf("RegOpenKeyExA(HKEY_CURRENT_USER, %s, ...) failed\n", + userShellFolders); +} + +START_TEST(shellpath) +{ + if (!init()) return; + + loadShell32(); + if (!hShell32) return; + + if (myARGC >= 3) + doChild(myARGV[2]); + else + { + /* first test various combinations of parameters: */ + testApiParameters(); + + /* check known values: */ + testPidlTypes(); + testGUIDs(); + testEnvVars(); + testWinDir(); + testSystemDir(); + testNonExistentPath(); + } +} diff --git a/include/winuser.h b/include/winuser.h index 9a242c29a8f..79782f0dfab 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -513,6 +513,7 @@ typedef struct /***** Dialogs *****/ +#define IS_INTRESOURCE(x) (((ULONG_PTR)(x) >> 16) == 0) #define MAKEINTRESOURCEA(i) (LPSTR)((ULONG_PTR)((WORD)(i))) #define MAKEINTRESOURCEW(i) (LPWSTR)((ULONG_PTR)((WORD)(i)))