Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

864 lines
29 KiB

  1. #include "precomp.hxx"
  2. #pragma hdrstop
  3. #include "util.h"
  4. #include "dll.h"
  5. #include "resource.h"
  6. #include "prop.h"
  7. #include <shellids.h> // IDH_ values
  8. #include "shlguidp.h"
  9. #include "inetreg.h"
  10. #include "strsafe.h"
  11. typedef struct {
  12. HWND hDlg;
  13. BOOL bDirty;
  14. BOOL bInitDone;
  15. BOOL bSetToDefault;
  16. TCHAR szFolder[MAX_PATH];
  17. UINT csidl;
  18. } CUSTINFO;
  19. const static DWORD rgdwHelpTarget[] = {
  20. IDD_TARGET_TXT, IDH_MYDOCS_TARGET,
  21. IDD_TARGET, IDH_MYDOCS_TARGET,
  22. IDD_FIND, IDH_MYDOCS_FIND_TARGET,
  23. IDD_BROWSE, IDH_MYDOCS_BROWSE,
  24. IDD_RESET, IDH_MYDOCS_RESET,
  25. 0, 0
  26. };
  27. // Scans a desktop.ini file for sections to see if all of them are empty...
  28. BOOL IsDesktopIniEmpty(LPCTSTR pIniFile)
  29. {
  30. TCHAR szSections[1024]; // for section names
  31. if (GetPrivateProfileSectionNames(szSections, ARRAYSIZE(szSections), pIniFile))
  32. {
  33. for (LPTSTR pTmp = szSections; *pTmp; pTmp += lstrlen(pTmp) + 1)
  34. {
  35. TCHAR szSection[1024]; // for section key names and values
  36. GetPrivateProfileSection(pTmp, szSection, ARRAYSIZE(szSection), pIniFile);
  37. if (szSection[0])
  38. {
  39. return FALSE;
  40. }
  41. }
  42. }
  43. return TRUE;
  44. }
  45. void CleanupSystemFolder(LPCTSTR pszPath)
  46. {
  47. TCHAR szIniFile[MAX_PATH];
  48. PathCombine(szIniFile, pszPath, TEXT("desktop.ini"));
  49. DWORD dwAttrb;
  50. if (PathFileExistsAndAttributes(szIniFile, &dwAttrb))
  51. {
  52. // Remove CLSID2, InfoTip, Icon
  53. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID2"), NULL, szIniFile);
  54. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("InfoTip"), NULL, szIniFile);
  55. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("IconFile"), NULL, szIniFile);
  56. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("IconIndex"), NULL, szIniFile);
  57. // get rid of delete on copy entries to see if we can generate an empty .ini file
  58. WritePrivateProfileSection(TEXT("DeleteOnCopy"), NULL, szIniFile);
  59. if (IsDesktopIniEmpty(szIniFile))
  60. {
  61. dwAttrb &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
  62. SetFileAttributes(szIniFile, dwAttrb);
  63. DeleteFile(szIniFile);
  64. }
  65. // see if we can cleanout an old thumbs.db file too
  66. // so we have a better chance of deleting an empty folder
  67. PathCombine(szIniFile, pszPath, TEXT("thumbs.db"));
  68. DeleteFile(szIniFile);
  69. PathUnmakeSystemFolder(pszPath);
  70. }
  71. // in case it is empty try to delete it
  72. // this will fail if there are contents in the folder
  73. if (RemoveDirectory(pszPath))
  74. {
  75. // it is gone, let people know
  76. SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, pszPath, NULL);
  77. }
  78. else
  79. {
  80. // attribute bits changed for this folder, refresh views of it
  81. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pszPath, NULL);
  82. }
  83. }
  84. HRESULT ChangeFolderPath(UINT csidl, LPCTSTR pszNew, LPCTSTR pszOld)
  85. {
  86. HRESULT hr = SHSetFolderPath(csidl, NULL, 0, pszNew);
  87. if (SUCCEEDED(hr))
  88. {
  89. // now we can cleanup the old folder... now that we have the new folder
  90. // established
  91. if (*pszOld)
  92. {
  93. CleanupSystemFolder(pszOld);
  94. }
  95. // force the per user init stuff on the new folder
  96. TCHAR szPath[MAX_PATH];
  97. hr = SHGetFolderPath(NULL, csidl | CSIDL_FLAG_CREATE | CSIDL_FLAG_PER_USER_INIT, NULL, SHGFP_TYPE_CURRENT, szPath);
  98. if (SUCCEEDED(hr))
  99. {
  100. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, szPath, NULL);
  101. }
  102. }
  103. return hr;
  104. }
  105. // test to see if pszToTest is a sub folder of pszFolder
  106. BOOL PathIsDirectChildOf(LPCTSTR pszFolder, LPCTSTR pszMaybeChild)
  107. {
  108. return PATH_IS_CHILD == ComparePaths(pszMaybeChild, pszFolder);
  109. }
  110. LPTSTR GetMessageTitle(CUSTINFO *pci, LPTSTR psz, UINT cch)
  111. {
  112. TCHAR szFormat[64], szName[MAX_PATH];
  113. LoadString(g_hInstance, IDS_PROP_ERROR_TITLE, szFormat, ARRAYSIZE(szFormat));
  114. GetFolderDisplayName(pci->csidl, szName, ARRAYSIZE(szName));
  115. StringCchPrintf(psz, cch, szFormat, szName);
  116. return psz;
  117. }
  118. void GetTargetExpandedPath(HWND hDlg, LPTSTR pszPath, UINT cch)
  119. {
  120. *pszPath = 0;
  121. TCHAR szUnExPath[MAX_PATH];
  122. if (GetDlgItemText(hDlg, IDD_TARGET, szUnExPath, ARRAYSIZE(szUnExPath)))
  123. {
  124. // Turn "c:" into "c:\", but don't change other paths:
  125. PathAddBackslash(szUnExPath); // safe to ignore return
  126. PathRemoveBackslash(szUnExPath);
  127. SHExpandEnvironmentStrings(szUnExPath, pszPath, cch);
  128. }
  129. }
  130. // Check known key in the registry to see if policy has disabled changing
  131. // of My Docs location.
  132. BOOL PolicyAllowsFolderPathChange(CUSTINFO *pci)
  133. {
  134. BOOL bChange = TRUE;
  135. if (pci->csidl == CSIDL_PERSONAL)
  136. {
  137. HKEY hkey;
  138. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), 0, KEY_READ, &hkey))
  139. {
  140. bChange = (ERROR_SUCCESS != RegQueryValueEx(hkey, TEXT("DisablePersonalDirChange"), NULL, NULL, NULL, NULL));
  141. RegCloseKey(hkey);
  142. }
  143. }
  144. return bChange;
  145. }
  146. BOOL InitTargetPage(HWND hDlg, LPARAM lParam)
  147. {
  148. CUSTINFO *pci = (CUSTINFO *)LocalAlloc(LPTR, sizeof(*pci));
  149. if (pci)
  150. {
  151. TCHAR szPath[MAX_PATH];
  152. TCHAR szFormat[MAX_PATH];
  153. TCHAR szText[ARRAYSIZE(szFormat) + MAX_NAME_LEN];
  154. TCHAR szName[MAX_PATH];
  155. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pci);
  156. pci->hDlg = hDlg;
  157. pci->csidl = CSIDL_PERSONAL;
  158. // Fill in title/instructions...
  159. GetFolderDisplayName(pci->csidl, szName, ARRAYSIZE(szName));
  160. if (lstrlen(szName) > MAX_NAME_LEN)
  161. {
  162. StringCchCopy(&szName[MAX_NAME_LEN], ARRAYSIZE(szName) - MAX_NAME_LEN, TEXT("...")); // already bounds checked above
  163. }
  164. LoadString(g_hInstance, IDS_PROP_INSTRUCTIONS, szFormat, ARRAYSIZE(szFormat));
  165. StringCchPrintf(szText, ARRAYSIZE(szText), szFormat, szName);
  166. SetDlgItemText(hDlg, IDD_INSTRUCTIONS, szText);
  167. // Limit edit field to MAX_PATH-13 characters. Why -13?
  168. // Well, 13 is the number of characters in a DOS style 8.3
  169. // filename with a '\', and CreateDirectory will fail if you try to create
  170. // a directory that can't at least contain 8.3 file names.
  171. SendDlgItemMessage(hDlg, IDD_TARGET, EM_SETLIMITTEXT, MAX_DIR_PATH, 0);
  172. // Check whether path can be changed
  173. if (PolicyAllowsFolderPathChange(pci))
  174. {
  175. SHAutoComplete(GetDlgItem(hDlg, IDD_TARGET), SHACF_FILESYS_DIRS);
  176. }
  177. else
  178. {
  179. // Make edit field read only
  180. SendDlgItemMessage(hDlg, IDD_TARGET, EM_SETREADONLY, (WPARAM)TRUE, 0);
  181. ShowWindow(GetDlgItem(hDlg, IDD_RESET), SW_HIDE);
  182. ShowWindow(GetDlgItem(hDlg, IDD_FIND), SW_HIDE);
  183. ShowWindow(GetDlgItem(hDlg, IDD_BROWSE), SW_HIDE);
  184. }
  185. SHGetFolderPath(NULL, pci->csidl | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath);
  186. if (szPath[0])
  187. {
  188. PathRemoveBackslash(szPath); // keep path without trailing backslash
  189. StringCchCopy(pci->szFolder, ARRAYSIZE(pci->szFolder), szPath);
  190. SetDlgItemText(hDlg, IDD_TARGET, szPath);
  191. }
  192. LPITEMIDLIST pidl;
  193. if (SUCCEEDED(SHGetFolderLocation(NULL, pci->csidl | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &pidl)))
  194. {
  195. SHFILEINFO sfi;
  196. SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_PIDL);
  197. if (sfi.hIcon)
  198. {
  199. if (sfi.hIcon = (HICON)SendDlgItemMessage(hDlg, IDD_ITEMICON, STM_SETICON, (WPARAM)sfi.hIcon, 0))
  200. {
  201. DestroyIcon(sfi.hIcon);
  202. }
  203. }
  204. ILFree(pidl);
  205. }
  206. pci->bInitDone = TRUE;
  207. }
  208. return pci ? TRUE : FALSE;
  209. }
  210. const UINT c_rgRedirectCanidates[] =
  211. {
  212. CSIDL_MYPICTURES,
  213. CSIDL_MYMUSIC,
  214. CSIDL_MYVIDEO,
  215. CSIDL_MYDOCUMENTS,
  216. };
  217. int MoveFilesForRedirect(HWND hdlg, LPCTSTR pszNewPath, LPCTSTR pszOldPath)
  218. {
  219. int iRet = 0; // success
  220. // since we use FOF_RENAMEONCOLLISION when moving files from the old location
  221. // to the new we want to special case target folders if they are the shell special
  222. // folders that may live under the folder being redirected
  223. // this code implements a merge of those folders doing "rename on collision" at the
  224. // level below the folder. this keeps us from generating "copy of xxx" for each of
  225. // the special folders
  226. for (UINT i = 0; (iRet == 0) && (i < ARRAYSIZE(c_rgRedirectCanidates)); i++)
  227. {
  228. TCHAR szOld[MAX_PATH];
  229. if (SUCCEEDED(SHGetFolderPath(NULL, c_rgRedirectCanidates[i] | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szOld)) &&
  230. PathIsDirectChildOf(pszOldPath, szOld))
  231. {
  232. TCHAR szDestPath[MAX_PATH] = {0}; // zero init for SHFileOperation()
  233. PathCombine(szDestPath, pszNewPath, PathFindFileName(szOld));
  234. DWORD dwAtt;
  235. if (PathFileExistsAndAttributes(szDestPath, &dwAtt) &&
  236. (FILE_ATTRIBUTE_DIRECTORY & dwAtt))
  237. {
  238. // reset the folder with the system before the move
  239. ChangeFolderPath(c_rgRedirectCanidates[i], szDestPath, szOld);
  240. // the above may have emptied and deleted the old location
  241. // but if not we need to move the contents
  242. if (PathFileExistsAndAttributes(szOld, &dwAtt))
  243. {
  244. // Move items in current MyPics to new location
  245. TCHAR szSrcPath[MAX_PATH + 1] = {0}; // +1 for double null
  246. PathCombine(szSrcPath, szOld, TEXT("*.*"));
  247. SHFILEOPSTRUCT fo = {0};
  248. fo.hwnd = hdlg;
  249. fo.wFunc = FO_MOVE;
  250. fo.fFlags = FOF_RENAMEONCOLLISION;
  251. fo.pFrom = szSrcPath;
  252. fo.pTo = szDestPath;
  253. iRet = SHFileOperation(&fo);
  254. if ((0 == iRet) && !fo.fAnyOperationsAborted)
  255. {
  256. // since the above was a full move no files should
  257. // be left behind so this should work
  258. if (RemoveDirectory(szOld))
  259. SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, szOld, NULL);
  260. }
  261. }
  262. }
  263. }
  264. }
  265. // above failed or canceled?
  266. if (0 == iRet)
  267. {
  268. // move the rest of the stuff
  269. TCHAR szSrcPath[MAX_PATH + 1] = {0}; // +1 for double null
  270. PathCombine(szSrcPath, pszOldPath, TEXT("*.*"));
  271. TCHAR szDestPath[MAX_PATH] = {0}; // zero init for dbl null term
  272. StringCchCopy(szDestPath, ARRAYSIZE(szDestPath), pszNewPath);
  273. SHFILEOPSTRUCT fo = {0};
  274. fo.hwnd = hdlg;
  275. fo.wFunc = FO_MOVE;
  276. fo.fFlags = FOF_RENAMEONCOLLISION; // don't want any "replace file" prompts
  277. fo.pFrom = szSrcPath;
  278. fo.pTo = szDestPath;
  279. iRet = SHFileOperation(&fo);
  280. if (0 == iRet)
  281. {
  282. // if the above worked we try to clean up the old path
  283. // now that it it empty
  284. if (RemoveDirectory(pszOldPath))
  285. SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, pszOldPath, NULL);
  286. }
  287. }
  288. return iRet;
  289. }
  290. // Ask the user if they want to create the directory of a given path.
  291. // Returns TRUE if the user decided to create it, FALSE if not.
  292. // If TRUE, the dir attributes are returned in pdwAttr.
  293. BOOL QueryCreateTheDirectory(CUSTINFO *pci, LPCTSTR pPath, DWORD *pdwAttr)
  294. {
  295. *pdwAttr = 0;
  296. UINT id = IDYES;
  297. if (pci->bSetToDefault)
  298. id = IDYES;
  299. else
  300. id = ShellMessageBox(g_hInstance, pci->hDlg, MAKEINTRESOURCE(IDS_CREATE_FOLDER), MAKEINTRESOURCE(IDS_CREATE_FOLDER_TITLE),
  301. MB_YESNO | MB_ICONQUESTION, pPath);
  302. if (IDYES == id)
  303. {
  304. // user asked us to create the folder
  305. if (ERROR_SUCCESS == SHCreateDirectoryEx(pci->hDlg, pPath, NULL))
  306. *pdwAttr = GetFileAttributes(pPath);
  307. }
  308. return IDYES == id;
  309. }
  310. void _MaybeUnpinOldFolder(LPCTSTR pszPath, HWND hwnd, BOOL fPromptUnPin)
  311. {
  312. //
  313. // Convert the path to canonical UNC form (the CSC and CSCUI
  314. // functions require the path to be in this form)
  315. //
  316. // WNetGetUniversalName fails if you give it a path that's already
  317. // in canonical UNC form, so in the failure case just try using
  318. // pszPath. CSCQueryFileStatus will validate it.
  319. //
  320. LPCTSTR pszUNC;
  321. struct {
  322. UNIVERSAL_NAME_INFO uni;
  323. TCHAR szBuf[MAX_PATH];
  324. } s;
  325. DWORD cbBuf = sizeof(s);
  326. if (ERROR_SUCCESS == WNetGetUniversalName(pszPath, UNIVERSAL_NAME_INFO_LEVEL,
  327. &s, &cbBuf))
  328. {
  329. pszUNC = s.uni.lpUniversalName;
  330. }
  331. else
  332. {
  333. pszUNC = pszPath;
  334. }
  335. //
  336. // Ask CSC if the folder is pinned for this user
  337. //
  338. DWORD dwHintFlags = 0;
  339. if (CSCQueryFileStatus(pszUNC, NULL, NULL, &dwHintFlags))
  340. {
  341. if (dwHintFlags & FLAG_CSC_HINT_PIN_USER)
  342. {
  343. //
  344. // Yes; figure out if we should unpin it
  345. //
  346. BOOL fUnpin;
  347. if (fPromptUnPin)
  348. {
  349. //
  350. // Give the unconverted path name in the message box, since
  351. // that's the name the user knows
  352. //
  353. UINT id = ShellMessageBox(g_hInstance, hwnd,
  354. MAKEINTRESOURCE(IDS_UNPIN_OLDTARGET), MAKEINTRESOURCE(IDS_UNPIN_OLD_TITLE),
  355. MB_YESNO | MB_ICONQUESTION | MB_TOPMOST | MB_DEFBUTTON2,
  356. pszPath);
  357. fUnpin = (id == IDNO);
  358. }
  359. else
  360. {
  361. fUnpin = TRUE;
  362. }
  363. if (fUnpin)
  364. {
  365. CSCUIRemoveFolderFromCache(pszUNC, 0, NULL, 0);
  366. }
  367. }
  368. }
  369. }
  370. void ComputChildrenOf(LPCTSTR pszOld, UINT rgChildren[], UINT sizeArray)
  371. {
  372. UINT iCanidate = 0;
  373. ZeroMemory(rgChildren, sizeof(rgChildren[0]) * sizeArray);
  374. for (UINT i = 0; i < ARRAYSIZE(c_rgRedirectCanidates); i++)
  375. {
  376. TCHAR szPath[MAX_PATH];
  377. if (SUCCEEDED(SHGetFolderPath(NULL, c_rgRedirectCanidates[i] | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath)))
  378. {
  379. if (PathIsDirectChildOf(pszOld, szPath))
  380. {
  381. if (iCanidate < sizeArray)
  382. {
  383. rgChildren[iCanidate++] = c_rgRedirectCanidates[i];
  384. }
  385. }
  386. }
  387. }
  388. }
  389. // if csidl DEFAULT VALUE ends up under the new folder we reset that folder
  390. // to that value
  391. HRESULT ResetSubFolderDefault(LPCTSTR pszNew, UINT csidl, LPCTSTR pszOldPath)
  392. {
  393. HRESULT hr = S_OK;
  394. // note: getting the default value for this path, not the current value!
  395. TCHAR szDefault[MAX_PATH];
  396. if (S_OK == SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_DEFAULT, szDefault))
  397. {
  398. if (PathIsDirectChildOf(pszNew, szDefault))
  399. {
  400. hr = SHSetFolderPath(csidl, NULL, 0, szDefault);
  401. if (SUCCEEDED(hr))
  402. {
  403. // we've written the registry, that is enough to cleanup the old folder
  404. if (*pszOldPath)
  405. CleanupSystemFolder(pszOldPath);
  406. hr = SHGetFolderPath(NULL, csidl | CSIDL_FLAG_CREATE | CSIDL_FLAG_PER_USER_INIT, NULL, SHGFP_TYPE_CURRENT, szDefault);
  407. }
  408. }
  409. }
  410. return hr;
  411. }
  412. void ResetNonMovedFolders(LPCTSTR pszNew, UINT rgChildren[], UINT sizeArray)
  413. {
  414. for (UINT i = 0; i < sizeArray; i++)
  415. {
  416. // for all of these folders that were sub folders of the old location
  417. // and are now not sub folders we try to restore them to the default
  418. TCHAR szPath[MAX_PATH];
  419. if (rgChildren[i] &&
  420. SUCCEEDED(SHGetFolderPath(NULL, rgChildren[i] | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath)) &&
  421. !PathIsDirectChildOf(pszNew, szPath))
  422. {
  423. ResetSubFolderDefault(pszNew, rgChildren[i], szPath);
  424. }
  425. }
  426. }
  427. void _DoApply(CUSTINFO *pci)
  428. {
  429. LONG lres = PSNRET_NOERROR;
  430. TCHAR szNewFolder[MAX_PATH];
  431. DWORD dwAttr;
  432. GetTargetExpandedPath(pci->hDlg, szNewFolder, ARRAYSIZE(szNewFolder));
  433. if (pci->bDirty && (lstrcmpi(szNewFolder, pci->szFolder) != 0))
  434. {
  435. TCHAR szPropTitle[MAX_PATH + 32];
  436. DWORD dwRes = IsPathGoodMyDocsPath(pci->hDlg, szNewFolder);
  437. // all of the special cases
  438. switch (dwRes)
  439. {
  440. case PATH_IS_DESKTOP: // desktop is not good
  441. ShellMessageBox(g_hInstance, pci->hDlg,
  442. MAKEINTRESOURCE(IDS_NODESKTOP_FOLDERS), GetMessageTitle(pci, szPropTitle, ARRAYSIZE(szPropTitle)),
  443. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  444. lres = PSNRET_INVALID_NOCHANGEPAGE;
  445. break;
  446. case PATH_IS_SYSTEM:
  447. case PATH_IS_WINDOWS: // these would be bad
  448. ShellMessageBox(g_hInstance, pci->hDlg,
  449. MAKEINTRESOURCE(IDS_NOWINDIR_FOLDER), GetMessageTitle(pci, szPropTitle, ARRAYSIZE(szPropTitle)),
  450. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  451. lres = PSNRET_INVALID_NOCHANGEPAGE;
  452. break;
  453. case PATH_IS_PROFILE: // profile is bad
  454. ShellMessageBox(g_hInstance, pci->hDlg,
  455. MAKEINTRESOURCE(IDS_NOPROFILEDIR_FOLDER), GetMessageTitle(pci, szPropTitle, ARRAYSIZE(szPropTitle)),
  456. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  457. lres = PSNRET_INVALID_NOCHANGEPAGE;
  458. break;
  459. case PATH_IS_NONEXISTENT:
  460. case PATH_IS_NONDIR:
  461. case PATH_IS_GOOD:
  462. dwAttr = GetFileAttributes(szNewFolder);
  463. if (dwAttr == 0xFFFFFFFF)
  464. {
  465. // Ask user if we should create the directory...
  466. if (!QueryCreateTheDirectory(pci, szNewFolder, &dwAttr))
  467. {
  468. // They don't want to create the directory.. break here
  469. lres = PSNRET_INVALID_NOCHANGEPAGE;
  470. break;
  471. }
  472. }
  473. if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
  474. {
  475. if (lstrcmpi(szNewFolder, pci->szFolder))
  476. {
  477. UINT rgChildren[10];
  478. ComputChildrenOf(pci->szFolder, rgChildren, ARRAYSIZE(rgChildren));
  479. if (SUCCEEDED(ChangeFolderPath(pci->csidl, szNewFolder, pci->szFolder)))
  480. {
  481. BOOL fNewSubdirOfOld = PathIsEqualOrSubFolder(pci->szFolder, szNewFolder);
  482. BOOL fPromptUnPin = TRUE;
  483. if (fNewSubdirOfOld)
  484. {
  485. // can't move old content to a subdir
  486. ShellMessageBox(g_hInstance, pci->hDlg,
  487. MAKEINTRESOURCE(IDS_CANT_MOVE_TO_SUBDIR), MAKEINTRESOURCE(IDS_MOVE_DOCUMENTS_TITLE),
  488. MB_OK | MB_ICONINFORMATION | MB_TOPMOST,
  489. pci->szFolder);
  490. }
  491. else if (IDYES == ShellMessageBox(g_hInstance, pci->hDlg,
  492. MAKEINTRESOURCE(IDS_MOVE_DOCUMENTS),
  493. MAKEINTRESOURCE(IDS_MOVE_DOCUMENTS_TITLE),
  494. MB_YESNO | MB_ICONQUESTION | MB_TOPMOST,
  495. pci->szFolder, szNewFolder))
  496. {
  497. // move old mydocs content -- returns 0 on success
  498. if (0 == MoveFilesForRedirect(pci->hDlg, szNewFolder, pci->szFolder))
  499. {
  500. // Move succeeded, the old target dir is now empty, so
  501. // no need to prompt about unpinning it (just go ahead
  502. // and do it).
  503. fPromptUnPin = FALSE;
  504. }
  505. else
  506. {
  507. // move failure
  508. ShellMessageBox(g_hInstance, pci->hDlg,
  509. MAKEINTRESOURCE(IDS_MOVE_ERROR), MAKEINTRESOURCE(IDS_MOVE_ERROR_TITLE),
  510. MB_OK | MB_ICONSTOP | MB_TOPMOST,
  511. szNewFolder, pci->szFolder);
  512. }
  513. }
  514. ResetNonMovedFolders(szNewFolder, rgChildren, ARRAYSIZE(rgChildren));
  515. if (!fNewSubdirOfOld && pci->szFolder[0])
  516. {
  517. // If the old folder was pinned, offer to unpin it.
  518. //
  519. // Do this only if new target is not a subdir of the
  520. // old target, since otherwise we'd end up unpinning
  521. // the new target as well
  522. _MaybeUnpinOldFolder(pci->szFolder, pci->hDlg, fPromptUnPin);
  523. }
  524. }
  525. else
  526. {
  527. ShellMessageBox(g_hInstance, pci->hDlg,
  528. MAKEINTRESOURCE(IDS_GENERAL_BADDIR), MAKEINTRESOURCE(IDS_INVALID_TITLE),
  529. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  530. lres = PSNRET_INVALID_NOCHANGEPAGE;
  531. }
  532. }
  533. }
  534. else if (dwAttr)
  535. {
  536. DWORD id = IDS_NONEXISTENT_FOLDER;
  537. // The user entered a path that doesn't exist or isn't a
  538. // directory...
  539. if (dwAttr != 0xFFFFFFFF)
  540. {
  541. id = IDS_NOT_DIRECTORY;
  542. }
  543. ShellMessageBox(g_hInstance, pci->hDlg,
  544. IntToPtr_(LPTSTR, id), MAKEINTRESOURCE(IDS_INVALID_TITLE),
  545. MB_OK | MB_ICONERROR | MB_TOPMOST);
  546. lres = PSNRET_INVALID_NOCHANGEPAGE;
  547. }
  548. else
  549. {
  550. ShellMessageBox(g_hInstance, pci->hDlg,
  551. MAKEINTRESOURCE(IDS_GENERAL_BADDIR), MAKEINTRESOURCE(IDS_INVALID_TITLE),
  552. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  553. lres = PSNRET_INVALID_NOCHANGEPAGE;
  554. }
  555. break;
  556. default:
  557. // the path to something isn't allowed
  558. ShellMessageBox(g_hInstance, pci->hDlg,
  559. MAKEINTRESOURCE(IDS_NOTALLOWED_FOLDERS), GetMessageTitle(pci, szPropTitle, ARRAYSIZE(szPropTitle)),
  560. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  561. lres = PSNRET_INVALID_NOCHANGEPAGE;
  562. break;
  563. }
  564. }
  565. if (lres == PSNRET_NOERROR)
  566. {
  567. pci->bDirty = FALSE;
  568. StringCchCopy(pci->szFolder, ARRAYSIZE(pci->szFolder), szNewFolder);
  569. }
  570. SetWindowLongPtr(pci->hDlg, DWLP_MSGRESULT, lres);
  571. }
  572. int _BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
  573. {
  574. switch (uMsg)
  575. {
  576. case BFFM_INITIALIZED:
  577. // Set the caption. ('Select a destination')
  578. TCHAR szTitle[100];
  579. LoadString(g_hInstance, IDS_BROWSE_CAPTION, szTitle, ARRAYSIZE(szTitle));
  580. SetWindowText(hwnd, szTitle);
  581. break;
  582. case BFFM_SELCHANGED:
  583. if (lParam)
  584. {
  585. TCHAR szPath[MAX_PATH];
  586. szPath[0] = 0;
  587. SHGetPathFromIDList((LPITEMIDLIST)lParam, szPath);
  588. DWORD dwRes = IsPathGoodMyDocsPath(hwnd, szPath);
  589. if (dwRes == PATH_IS_GOOD || dwRes == PATH_IS_MYDOCS)
  590. {
  591. SendMessage(hwnd, BFFM_ENABLEOK, 0, (LPARAM)TRUE);
  592. SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, 0);
  593. }
  594. else
  595. {
  596. TCHAR szStatus[128];
  597. SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
  598. szStatus[0] = 0;
  599. LoadString(g_hInstance, IDS_NOSHELLEXT_FOLDERS, szStatus, ARRAYSIZE(szStatus));
  600. SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)szStatus);
  601. }
  602. }
  603. break;
  604. }
  605. return 0;
  606. }
  607. void _MakeDirty(CUSTINFO *pci)
  608. {
  609. pci->bDirty = TRUE;
  610. pci->bSetToDefault = FALSE;
  611. PropSheet_Changed(GetParent(pci->hDlg), pci->hDlg);
  612. }
  613. void _DoFind(CUSTINFO *pci)
  614. {
  615. TCHAR szPath[MAX_PATH];
  616. GetTargetExpandedPath(pci->hDlg, szPath, ARRAYSIZE(szPath));
  617. LPITEMIDLIST pidl = ILCreateFromPath(szPath);
  618. if (pidl)
  619. {
  620. SHOpenFolderAndSelectItems(pidl, 0, NULL, 0);
  621. ILFree(pidl);
  622. }
  623. else
  624. {
  625. ShellMessageBox(g_hInstance, pci->hDlg,
  626. MAKEINTRESOURCE(IDS_GENERAL_BADDIR), MAKEINTRESOURCE(IDS_INVALID_TITLE),
  627. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  628. }
  629. }
  630. void _DoBrowse(CUSTINFO *pci)
  631. {
  632. BROWSEINFO bi = {0};
  633. TCHAR szTitle[128];
  634. LoadString(g_hInstance, IDS_BROWSE_TITLE, szTitle, ARRAYSIZE(szTitle));
  635. bi.hwndOwner = pci->hDlg;
  636. bi.lpszTitle = szTitle;
  637. bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE | BIF_UAHINT;
  638. bi.lpfn = _BrowseCallbackProc;
  639. // the default root for this folder is MyDocs so we don't need to set that up.
  640. LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
  641. if (pidl)
  642. {
  643. TCHAR szName[MAX_PATH];
  644. if (SHGetPathFromIDList(pidl, szName))
  645. {
  646. SetDlgItemText(pci->hDlg, IDD_TARGET, szName);
  647. _MakeDirty(pci);
  648. }
  649. ILFree(pidl);
  650. }
  651. }
  652. void DoReset(CUSTINFO *pci)
  653. {
  654. TCHAR szPath[MAX_PATH];
  655. if (S_OK == SHGetFolderPath(NULL, pci->csidl, NULL, SHGFP_TYPE_DEFAULT, szPath))
  656. {
  657. SetDlgItemText(pci->hDlg, IDD_TARGET, szPath);
  658. _MakeDirty(pci);
  659. pci->bSetToDefault = TRUE; // to avoid prompt to create
  660. }
  661. }
  662. INT_PTR CALLBACK TargetDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  663. {
  664. CUSTINFO *pci = (CUSTINFO *)GetWindowLongPtr(hDlg, DWLP_USER);
  665. switch (uMsg)
  666. {
  667. case WM_INITDIALOG:
  668. InitTargetPage(hDlg, lParam);
  669. return 1;
  670. case WM_DESTROY:
  671. LocalFree(pci);
  672. SetWindowLongPtr(hDlg, DWLP_USER, 0);
  673. return 1;
  674. case WM_COMMAND:
  675. switch (GET_WM_COMMAND_ID(wParam, lParam))
  676. {
  677. case IDD_RESET:
  678. DoReset(pci);
  679. return 1;
  680. case IDD_TARGET:
  681. if ((GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE) && pci && (pci->bInitDone) && (!pci->bDirty))
  682. {
  683. _MakeDirty(pci);
  684. }
  685. return 1;
  686. case IDD_FIND:
  687. _DoFind(pci);
  688. return 1;
  689. case IDD_BROWSE:
  690. _DoBrowse(pci);
  691. return 1;
  692. }
  693. break;
  694. case WM_HELP: /* F1 or title-bar help button */
  695. if ((((LPHELPINFO)lParam)->iCtrlId != IDD_ITEMICON) &&
  696. (((LPHELPINFO)lParam)->iCtrlId != IDD_INSTRUCTIONS) &&
  697. (((LPHELPINFO)lParam)->iCtrlId != IDC_TARGET_GBOX))
  698. {
  699. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle,
  700. NULL, HELP_WM_HELP, (DWORD_PTR) rgdwHelpTarget);
  701. }
  702. break;
  703. case WM_CONTEXTMENU: /* right mouse click */
  704. {
  705. POINT p;
  706. HWND hwndChild;
  707. INT ctrlid;
  708. //
  709. // Get the point where the user clicked...
  710. //
  711. p.x = GET_X_LPARAM(lParam);
  712. p.y = GET_Y_LPARAM(lParam);
  713. //
  714. // Now, map that to a child control if possible...
  715. //
  716. ScreenToClient(hDlg, &p);
  717. hwndChild = ChildWindowFromPoint((HWND)wParam, p);
  718. ctrlid = GetDlgCtrlID(hwndChild);
  719. //
  720. // Don't put up the help context menu for the items
  721. // that don't have help...
  722. //
  723. if ((ctrlid != IDD_ITEMICON) &&
  724. (ctrlid != IDD_INSTRUCTIONS))
  725. {
  726. WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (DWORD_PTR)rgdwHelpTarget);
  727. }
  728. }
  729. break;
  730. case WM_NOTIFY:
  731. switch (((NMHDR *)lParam)->code)
  732. {
  733. case PSN_APPLY:
  734. _DoApply(pci);
  735. return 1;
  736. }
  737. break;
  738. }
  739. return 0;
  740. }