Source code of Windows XP (NT5)
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.

3479 lines
119 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "propsht.h"
  4. #include <winbase.h>
  5. #include <shellids.h>
  6. #include "util.h" // for GetFileDescription
  7. #include "prshtcpp.h" // for progress dlg and recursive apply
  8. #include "shlexec.h" // for SIDKEYNAME
  9. #include "datautil.h"
  10. #include <efsui.h> // for EfsDetail
  11. #include "ascstr.h" // for IAssocStore
  12. // drivesx.c
  13. STDAPI_(DWORD) PathGetClusterSize(LPCTSTR pszPath);
  14. STDAPI_(DWORD) DrivesPropertiesThreadProc(void *pv);
  15. // version.c
  16. STDAPI_(void) AddVersionPage(LPCTSTR pszFilePath, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
  17. // link.c
  18. STDAPI_(BOOL) AddLinkPage(LPCTSTR pszFile, LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam);
  19. BOOL _IsBiDiCalendar(void);
  20. const DWORD aFileGeneralHelpIds[] = {
  21. IDD_LINE_1, NO_HELP,
  22. IDD_LINE_2, NO_HELP,
  23. IDD_LINE_3, NO_HELP,
  24. IDD_ITEMICON, IDH_FPROP_GEN_ICON,
  25. IDD_NAMEEDIT, IDH_FPROP_GEN_NAME,
  26. IDC_CHANGEFILETYPE, IDH_FPROP_GEN_CHANGE,
  27. IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
  28. IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
  29. IDD_OPENSWITH_TXT, IDH_FPROP_GEN_OPENSWITH,
  30. IDD_OPENSWITH, IDH_FPROP_GEN_OPENSWITH,
  31. IDD_LOCATION_TXT, IDH_FPROP_GEN_LOCATION,
  32. IDD_LOCATION, IDH_FPROP_GEN_LOCATION,
  33. IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
  34. IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
  35. IDD_FILESIZE_COMPRESSED, IDH_FPROP_GEN_COMPRESSED_SIZE,
  36. IDD_FILESIZE_COMPRESSED_TXT, IDH_FPROP_GEN_COMPRESSED_SIZE,
  37. IDD_CONTAINS_TXT, IDH_FPROP_FOLDER_CONTAINS,
  38. IDD_CONTAINS, IDH_FPROP_FOLDER_CONTAINS,
  39. IDD_CREATED_TXT, IDH_FPROP_GEN_DATE_CREATED,
  40. IDD_CREATED, IDH_FPROP_GEN_DATE_CREATED,
  41. IDD_LASTMODIFIED_TXT, IDH_FPROP_GEN_LASTCHANGE,
  42. IDD_LASTMODIFIED, IDH_FPROP_GEN_LASTCHANGE,
  43. IDD_LASTACCESSED_TXT, IDH_FPROP_GEN_LASTACCESS,
  44. IDD_LASTACCESSED, IDH_FPROP_GEN_LASTACCESS,
  45. IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
  46. IDD_READONLY, IDH_FPROP_GEN_READONLY,
  47. IDD_HIDDEN, IDH_FPROP_GEN_HIDDEN,
  48. IDD_ARCHIVE, IDH_FPROP_GEN_ARCHIVE,
  49. IDC_ADVANCED, IDH_FPROP_GEN_ADVANCED,
  50. IDC_DRV_PROPERTIES, IDH_FPROP_GEN_MOUNTEDPROP,
  51. IDD_FILETYPE_TARGET, IDH_FPROP_GEN_MOUNTEDTARGET,
  52. IDC_DRV_TARGET, IDH_FPROP_GEN_MOUNTEDTARGET,
  53. 0, 0
  54. };
  55. const DWORD aFolderGeneralHelpIds[] = {
  56. IDD_LINE_1, NO_HELP,
  57. IDD_LINE_2, NO_HELP,
  58. IDD_LINE_3, NO_HELP,
  59. IDD_ITEMICON, IDH_FPROP_GEN_ICON,
  60. IDD_NAMEEDIT, IDH_FPROP_GEN_NAME,
  61. IDC_CHANGEFILETYPE, IDH_FPROP_GEN_CHANGE,
  62. IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
  63. IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
  64. IDD_OPENSWITH_TXT, IDH_FPROP_GEN_OPENSWITH,
  65. IDD_OPENSWITH, IDH_FPROP_GEN_OPENSWITH,
  66. IDD_LOCATION_TXT, IDH_FPROP_GEN_LOCATION,
  67. IDD_LOCATION, IDH_FPROP_GEN_LOCATION,
  68. IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
  69. IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
  70. IDD_FILESIZE_COMPRESSED, IDH_FPROP_GEN_COMPRESSED_SIZE,
  71. IDD_FILESIZE_COMPRESSED_TXT, IDH_FPROP_GEN_COMPRESSED_SIZE,
  72. IDD_CONTAINS_TXT, IDH_FPROP_FOLDER_CONTAINS,
  73. IDD_CONTAINS, IDH_FPROP_FOLDER_CONTAINS,
  74. IDD_CREATED_TXT, IDH_FPROP_GEN_DATE_CREATED,
  75. IDD_CREATED, IDH_FPROP_GEN_DATE_CREATED,
  76. IDD_LASTMODIFIED_TXT, IDH_FPROP_GEN_LASTCHANGE,
  77. IDD_LASTMODIFIED, IDH_FPROP_GEN_LASTCHANGE,
  78. IDD_LASTACCESSED_TXT, IDH_FPROP_GEN_LASTACCESS,
  79. IDD_LASTACCESSED, IDH_FPROP_GEN_LASTACCESS,
  80. IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
  81. IDD_READONLY, IDH_FPROP_GEN_FOLDER_READONLY,
  82. IDD_HIDDEN, IDH_FPROP_GEN_HIDDEN,
  83. IDD_ARCHIVE, IDH_FPROP_GEN_ARCHIVE,
  84. IDC_ADVANCED, IDH_FPROP_GEN_ADVANCED,
  85. IDC_DRV_PROPERTIES, IDH_FPROP_GEN_MOUNTEDPROP,
  86. IDD_FILETYPE_TARGET, IDH_FPROP_GEN_MOUNTEDTARGET,
  87. IDC_DRV_TARGET, IDH_FPROP_GEN_MOUNTEDTARGET,
  88. 0, 0
  89. };
  90. const DWORD aMultiPropHelpIds[] = {
  91. IDD_LINE_1, NO_HELP,
  92. IDD_LINE_2, NO_HELP,
  93. IDD_ITEMICON, IDH_FPROP_GEN_ICON,
  94. IDD_CONTAINS, IDH_MULTPROP_NAME,
  95. IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
  96. IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
  97. IDD_LOCATION_TXT, IDH_FPROP_GEN_LOCATION,
  98. IDD_LOCATION, IDH_FPROP_GEN_LOCATION,
  99. IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
  100. IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
  101. IDD_FILESIZE_COMPRESSED, IDH_FPROP_GEN_COMPRESSED_SIZE,
  102. IDD_FILESIZE_COMPRESSED_TXT, IDH_FPROP_GEN_COMPRESSED_SIZE,
  103. IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
  104. IDD_READONLY, IDH_FPROP_GEN_READONLY,
  105. IDD_HIDDEN, IDH_FPROP_GEN_HIDDEN,
  106. IDD_ARCHIVE, IDH_FPROP_GEN_ARCHIVE,
  107. IDC_ADVANCED, IDH_FPROP_GEN_ADVANCED,
  108. 0, 0
  109. };
  110. const DWORD aAdvancedHelpIds[] = {
  111. IDD_ITEMICON, NO_HELP,
  112. IDC_MANAGEFILES_TXT, NO_HELP,
  113. IDD_MANAGEFOLDERS_TXT, NO_HELP,
  114. IDD_ARCHIVE, IDH_FPROP_GEN_ARCHIVE,
  115. IDD_INDEX, IDH_FPROP_GEN_INDEX,
  116. IDD_COMPRESS, IDH_FPROP_GEN_COMPRESSED,
  117. IDD_ENCRYPT, IDH_FPROP_GEN_ENCRYPT,
  118. 0, 0
  119. };
  120. FOLDERCONTENTSINFO* Create_FolderContentsInfo()
  121. {
  122. FOLDERCONTENTSINFO *pfci = (FOLDERCONTENTSINFO*)LocalAlloc(LPTR, sizeof(*pfci));
  123. if (pfci)
  124. {
  125. pfci->_cRef = 1;
  126. }
  127. return pfci;
  128. }
  129. void Free_FolderContentsInfoMembers(FOLDERCONTENTSINFO* pfci)
  130. {
  131. if (pfci->hida)
  132. {
  133. GlobalFree(pfci->hida);
  134. pfci->hida = NULL;
  135. }
  136. }
  137. LONG AddRef_FolderContentsInfo(FOLDERCONTENTSINFO* pfci)
  138. {
  139. ASSERTMSG(pfci != NULL, "AddRef_FolderContentsInfo: caller passed a null pfci");
  140. if (pfci)
  141. {
  142. return InterlockedIncrement(&pfci->_cRef);
  143. }
  144. return 0;
  145. }
  146. LONG Release_FolderContentsInfo(FOLDERCONTENTSINFO* pfci)
  147. {
  148. if (pfci)
  149. {
  150. if (InterlockedDecrement(&pfci->_cRef))
  151. {
  152. return pfci->_cRef;
  153. }
  154. Free_FolderContentsInfoMembers(pfci);
  155. LocalFree(pfci);
  156. }
  157. return 0;
  158. }
  159. void UpdateSizeCount(FILEPROPSHEETPAGE * pfpsp)
  160. {
  161. TCHAR szNum[32], szNum1[64];
  162. LPTSTR pszFmt = ShellConstructMessageString(HINST_THISDLL,
  163. MAKEINTRESOURCE(pfpsp->pfci->cbSize ? IDS_SIZEANDBYTES : IDS_SIZE),
  164. ShortSizeFormat64(pfpsp->pfci->cbSize, szNum, ARRAYSIZE(szNum)),
  165. AddCommas64(pfpsp->pfci->cbSize, szNum1, ARRAYSIZE(szNum1)));
  166. if (pszFmt)
  167. {
  168. SetDlgItemText(pfpsp->hDlg, IDD_FILESIZE, pszFmt);
  169. LocalFree(pszFmt);
  170. }
  171. pszFmt = ShellConstructMessageString(HINST_THISDLL,
  172. MAKEINTRESOURCE(pfpsp->pfci->cbActualSize ? IDS_SIZEANDBYTES : IDS_SIZE),
  173. ShortSizeFormat64(pfpsp->pfci->cbActualSize, szNum, ARRAYSIZE(szNum)),
  174. AddCommas64(pfpsp->pfci->cbActualSize, szNum1, ARRAYSIZE(szNum1)));
  175. if (pszFmt)
  176. {
  177. SetDlgItemText(pfpsp->hDlg, IDD_FILESIZE_COMPRESSED, pszFmt);
  178. LocalFree(pszFmt);
  179. }
  180. pszFmt = ShellConstructMessageString(HINST_THISDLL,
  181. MAKEINTRESOURCE(IDS_NUMFILES),
  182. AddCommas(pfpsp->pfci->cFiles, szNum, ARRAYSIZE(szNum)),
  183. AddCommas(pfpsp->pfci->cFolders, szNum1, ARRAYSIZE(szNum1)));
  184. if (pszFmt && !pfpsp->fMountedDrive)
  185. {
  186. SetDlgItemText(pfpsp->hDlg, IDD_CONTAINS, pszFmt);
  187. LocalFree(pszFmt);
  188. }
  189. }
  190. STDAPI_(BOOL) HIDA_FillFindData(HIDA hida, UINT iItem, LPTSTR pszPath, WIN32_FIND_DATA *pfd, BOOL fReturnCompressedSize)
  191. {
  192. BOOL fRet = FALSE; // assume error
  193. *pszPath = 0; // assume error
  194. LPITEMIDLIST pidl = HIDA_ILClone(hida, iItem);
  195. if (pidl)
  196. {
  197. if (SHGetPathFromIDList(pidl, pszPath))
  198. {
  199. if (pfd)
  200. {
  201. HANDLE h = FindFirstFile(pszPath, pfd);
  202. if (h == INVALID_HANDLE_VALUE)
  203. {
  204. // error, zero the bits
  205. ZeroMemory(pfd, sizeof(*pfd));
  206. }
  207. else
  208. {
  209. FindClose(h);
  210. // if the user wants the compressed file size, and compression is supported, then go get it
  211. if (fReturnCompressedSize && (pfd->dwFileAttributes & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE)))
  212. {
  213. pfd->nFileSizeLow = SHGetCompressedFileSize(pszPath, &pfd->nFileSizeHigh);
  214. }
  215. }
  216. }
  217. fRet = TRUE;
  218. }
  219. ILFree(pidl);
  220. }
  221. return fRet;
  222. }
  223. DWORD CALLBACK SizeThreadProc(void *pv)
  224. {
  225. FOLDERCONTENTSINFO* pfci = (FOLDERCONTENTSINFO*)pv;
  226. pfci->cbSize = 0;
  227. pfci->cbActualSize = 0;
  228. pfci->cFiles = 0;
  229. pfci->cFolders = 0;
  230. if (pfci->bContinue && pfci->hDlg)
  231. {
  232. // update the dialog every 1/4 second
  233. SetTimer(pfci->hDlg, IDT_SIZE, 250, NULL);
  234. }
  235. TCHAR szPath[MAX_PATH];
  236. for (UINT iItem = 0; HIDA_FillFindData(pfci->hida, iItem, szPath, &pfci->fd, FALSE) && pfci->bContinue; iItem++)
  237. {
  238. if (pfci->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  239. {
  240. FolderSize(szPath, pfci);
  241. if (pfci->fMultipleFiles)
  242. {
  243. // for multiple file/folder properties, count myself
  244. pfci->cFolders++;
  245. }
  246. }
  247. else
  248. { // file selected
  249. ULARGE_INTEGER ulSize, ulSizeOnDisk;
  250. DWORD dwClusterSize = PathGetClusterSize(szPath);
  251. // if compression is supported, we check to see if the file is sparse or compressed
  252. if (pfci->fIsCompressionAvailable && (pfci->fd.dwFileAttributes & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE)))
  253. {
  254. ulSizeOnDisk.LowPart = SHGetCompressedFileSize(szPath, &ulSizeOnDisk.HighPart);
  255. }
  256. else
  257. {
  258. // not compressed or sparse, so just round to the cluster size
  259. ulSizeOnDisk.LowPart = pfci->fd.nFileSizeLow;
  260. ulSizeOnDisk.HighPart = pfci->fd.nFileSizeHigh;
  261. ulSizeOnDisk.QuadPart = ROUND_TO_CLUSTER(ulSizeOnDisk.QuadPart, dwClusterSize);
  262. }
  263. // add the size in
  264. ulSize.LowPart = pfci->fd.nFileSizeLow;
  265. ulSize.HighPart = pfci->fd.nFileSizeHigh;
  266. pfci->cbSize += ulSize.QuadPart;
  267. // add the size on disk in
  268. pfci->cbActualSize += ulSizeOnDisk.QuadPart;
  269. // increment the # of files
  270. pfci->cFiles++;
  271. }
  272. // set this so the progress bar knows how much total work there is to do
  273. // ISSUE RAID BUG - 120446 - Need to Guard access to pfci->ulTotalNumberOfBytes.QuadParts
  274. pfci->ulTotalNumberOfBytes.QuadPart = pfci->cbActualSize;
  275. } // end of For Loop.
  276. if (pfci->bContinue && pfci->hDlg)
  277. {
  278. KillTimer(pfci->hDlg, IDT_SIZE);
  279. // make sure that there is a WM_TIMER message in the queue so we will get the "final" results
  280. PostMessage(pfci->hDlg, WM_TIMER, (WPARAM)IDT_SIZE, (LPARAM)NULL);
  281. }
  282. pfci->fIsSizeThreadAlive = FALSE;
  283. Release_FolderContentsInfo(pfci);
  284. return 0;
  285. }
  286. DWORD CALLBACK SizeThread_AddRefCallBack(void *pv)
  287. {
  288. FOLDERCONTENTSINFO* pfci = (FOLDERCONTENTSINFO *)pv;
  289. AddRef_FolderContentsInfo(pfci);
  290. pfci->fIsSizeThreadAlive = TRUE;
  291. return 0;
  292. }
  293. void CreateSizeThread(FILEPROPSHEETPAGE * pfpsp)
  294. {
  295. if (pfpsp->pfci->bContinue)
  296. {
  297. if (!pfpsp->pfci->fIsSizeThreadAlive)
  298. {
  299. SHCreateThread(SizeThreadProc, pfpsp->pfci, CTF_COINIT, SizeThread_AddRefCallBack);
  300. }
  301. else
  302. {
  303. // previous size thread still running, so bail
  304. }
  305. }
  306. }
  307. void KillSizeThread(FILEPROPSHEETPAGE * pfpsp)
  308. {
  309. // signal the thread to stop
  310. pfpsp->pfci->bContinue = FALSE;
  311. }
  312. DWORD GetVolumeFlags(LPCTSTR pszPath, OUT OPTIONAL LPTSTR pszFileSys, int cchFileSys)
  313. {
  314. TCHAR szRoot[MAX_PATH];
  315. /* Is this mounted point, e.g. c:\ or c:\hostfolder\ */
  316. if (!PathGetMountPointFromPath(pszPath, szRoot, ARRAYSIZE(szRoot)))
  317. {
  318. //no
  319. lstrcpyn(szRoot, pszPath, ARRAYSIZE(szRoot));
  320. PathStripToRoot(szRoot);
  321. }
  322. // GetVolumeInformation requires a trailing backslash. Append one
  323. PathAddBackslash(szRoot);
  324. if (pszFileSys)
  325. *pszFileSys = 0 ;
  326. DWORD dwVolumeFlags;
  327. if (GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, &dwVolumeFlags, pszFileSys, cchFileSys))
  328. {
  329. return dwVolumeFlags;
  330. }
  331. else
  332. {
  333. return 0;
  334. }
  335. }
  336. //
  337. // This function sets the initial file attributes based on the dwFlagsAND / dwFlagsOR
  338. // for the multiple file case
  339. //
  340. void SetInitialFileAttribs(FILEPROPSHEETPAGE* pfpsp, DWORD dwFlagsAND, DWORD dwFlagsOR)
  341. {
  342. DWORD dwTriState = dwFlagsAND ^ dwFlagsOR; // this dword now has all the bits that are in the BST_INDETERMINATE state
  343. #ifdef DEBUG
  344. // the pfpsp struct should have been zero inited, make sure that our ATTRIBUTESTATE
  345. // structs are zero inited
  346. ATTRIBUTESTATE asTemp = {0};
  347. ASSERT(memcmp(&pfpsp->asInitial, &asTemp, sizeof(pfpsp->asInitial)) == 0);
  348. #endif // DEBUG
  349. // set the inital state based on the flags
  350. if (dwTriState & FILE_ATTRIBUTE_READONLY)
  351. {
  352. pfpsp->asInitial.fReadOnly = BST_INDETERMINATE;
  353. }
  354. else if (dwFlagsAND & FILE_ATTRIBUTE_READONLY)
  355. {
  356. pfpsp->asInitial.fReadOnly = BST_CHECKED;
  357. }
  358. if (dwTriState & FILE_ATTRIBUTE_HIDDEN)
  359. {
  360. pfpsp->asInitial.fHidden = BST_INDETERMINATE;
  361. }
  362. else if (dwFlagsAND & FILE_ATTRIBUTE_HIDDEN)
  363. {
  364. pfpsp->asInitial.fHidden = BST_CHECKED;
  365. }
  366. if (dwTriState & FILE_ATTRIBUTE_ARCHIVE)
  367. {
  368. pfpsp->asInitial.fArchive = BST_INDETERMINATE;
  369. }
  370. else if (dwFlagsAND & FILE_ATTRIBUTE_ARCHIVE)
  371. {
  372. pfpsp->asInitial.fArchive = BST_CHECKED;
  373. }
  374. if (dwTriState & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
  375. {
  376. pfpsp->asInitial.fIndex = BST_INDETERMINATE;
  377. }
  378. else if (!(dwFlagsAND & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
  379. {
  380. pfpsp->asInitial.fIndex = BST_CHECKED;
  381. }
  382. if (dwTriState & FILE_ATTRIBUTE_COMPRESSED)
  383. {
  384. pfpsp->asInitial.fCompress = BST_INDETERMINATE;
  385. }
  386. else if (dwFlagsAND & FILE_ATTRIBUTE_COMPRESSED)
  387. {
  388. pfpsp->asInitial.fCompress = BST_CHECKED;
  389. }
  390. if (dwTriState & FILE_ATTRIBUTE_ENCRYPTED)
  391. {
  392. pfpsp->asInitial.fEncrypt = BST_INDETERMINATE;
  393. }
  394. else if (dwFlagsAND & FILE_ATTRIBUTE_ENCRYPTED)
  395. {
  396. pfpsp->asInitial.fEncrypt = BST_CHECKED;
  397. }
  398. }
  399. //
  400. // Updates the size fields for single and multiple file property sheets.
  401. //
  402. // NOTE: if you have the the WIN32_FIND_DATA already, then pass it for perf
  403. //
  404. STDAPI_(void) UpdateSizeField(FILEPROPSHEETPAGE* pfpsp, WIN32_FIND_DATA* pfd)
  405. {
  406. WIN32_FIND_DATA wfd;
  407. if (pfpsp->pfci->fMultipleFiles)
  408. {
  409. // multiple selection case
  410. // create the size and # of files thread
  411. CreateSizeThread(pfpsp);
  412. }
  413. else
  414. {
  415. // if the caller didn't pass pfd, then go get the WIN32_FIND_DATA now
  416. if (!pfd)
  417. {
  418. HANDLE hFind = FindFirstFile(pfpsp->szPath, &wfd);
  419. if (hFind == INVALID_HANDLE_VALUE)
  420. {
  421. // if this failed we should clear out all the values as to not show garbage on the screen.
  422. ZeroMemory(&wfd, sizeof(wfd));
  423. }
  424. else
  425. {
  426. FindClose(hFind);
  427. }
  428. pfd = &wfd;
  429. }
  430. if (pfpsp->fMountedDrive)
  431. {
  432. // mounted drive case
  433. SetDateTimeText(pfpsp->hDlg, IDD_CREATED, &pfd->ftCreationTime);
  434. }
  435. else if (pfpsp->fIsDirectory)
  436. {
  437. // single folder case, in the UI we call this "Modified"
  438. // but since NTFS updates ftModified when the contents of the
  439. // folder changes (FAT does not) we use ftCreationTime as the
  440. // stable end user notiion of "Modified"
  441. SetDateTimeText(pfpsp->hDlg, IDD_CREATED, &pfd->ftCreationTime);
  442. // create the size and # of files thread
  443. CreateSizeThread(pfpsp);
  444. }
  445. else
  446. {
  447. TCHAR szNum1[MAX_COMMA_AS_K_SIZE];
  448. TCHAR szNum2[MAX_COMMA_NUMBER_SIZE];
  449. ULARGE_INTEGER ulSize = { pfd->nFileSizeLow, pfd->nFileSizeHigh };
  450. DWORD dwClusterSize = PathGetClusterSize(pfpsp->szPath);
  451. // fill in the "Size:" field
  452. LPTSTR pszFmt = ShellConstructMessageString(HINST_THISDLL,
  453. MAKEINTRESOURCE(ulSize.QuadPart ? IDS_SIZEANDBYTES : IDS_SIZE),
  454. ShortSizeFormat64(ulSize.QuadPart, szNum1, ARRAYSIZE(szNum1)),
  455. AddCommas64(ulSize.QuadPart, szNum2, ARRAYSIZE(szNum2)));
  456. if (pszFmt)
  457. {
  458. SetDlgItemText(pfpsp->hDlg, IDD_FILESIZE, pszFmt);
  459. LocalFree(pszFmt);
  460. }
  461. //
  462. // fill in the "Size on disk:" field
  463. //
  464. if (pfd->dwFileAttributes & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE))
  465. {
  466. // the file is compressed or sparse, so for "size on disk" use the compressed size
  467. ulSize.LowPart = SHGetCompressedFileSize(pfpsp->szPath, &ulSize.HighPart);
  468. }
  469. else
  470. {
  471. // the file isint comrpessed so just round to the cluster size for the "size on disk"
  472. ulSize.LowPart = pfd->nFileSizeLow;
  473. ulSize.HighPart = pfd->nFileSizeHigh;
  474. ulSize.QuadPart = ROUND_TO_CLUSTER(ulSize.QuadPart, dwClusterSize);
  475. }
  476. pszFmt = ShellConstructMessageString(HINST_THISDLL,
  477. MAKEINTRESOURCE(ulSize.QuadPart ? IDS_SIZEANDBYTES : IDS_SIZE),
  478. ShortSizeFormat64(ulSize.QuadPart, szNum1, ARRAYSIZE(szNum1)),
  479. AddCommas64(ulSize.QuadPart, szNum2, ARRAYSIZE(szNum2)));
  480. if (pszFmt && !pfpsp->fMountedDrive)
  481. {
  482. SetDlgItemText(pfpsp->hDlg, IDD_FILESIZE_COMPRESSED, pszFmt);
  483. LocalFree(pszFmt);
  484. }
  485. //
  486. // we always touch the file in the process of getting its info, so the
  487. // ftLastAccessTime is always TODAY, which makes this field pretty useless...
  488. // date and time
  489. SetDateTimeText(pfpsp->hDlg, IDD_CREATED, &pfd->ftCreationTime);
  490. SetDateTimeText(pfpsp->hDlg, IDD_LASTMODIFIED, &pfd->ftLastWriteTime);
  491. {
  492. // FAT implementation doesn't support last accessed time (gets the date right, but not the time),
  493. // so we won't display it
  494. DWORD dwFlags = FDTF_LONGDATE | FDTF_RELATIVE;
  495. if (NULL == StrStrI(pfpsp->szFileSys, TEXT("FAT")))
  496. dwFlags |= FDTF_LONGTIME; // for non FAT file systems
  497. SetDateTimeTextEx(pfpsp->hDlg, IDD_LASTACCESSED, &pfd->ftLastAccessTime, dwFlags);
  498. }
  499. }
  500. }
  501. }
  502. //
  503. // Descriptions:
  504. // This function fills fields of the multiple object property sheet.
  505. //
  506. BOOL InitMultiplePrsht(FILEPROPSHEETPAGE* pfpsp)
  507. {
  508. SHFILEINFO sfi;
  509. TCHAR szBuffer[MAX_PATH+1];
  510. BOOL fMultipleType = FALSE;
  511. BOOL fSameLocation = TRUE;
  512. DWORD dwFlagsOR = 0; // start all clear
  513. DWORD dwFlagsAND = (DWORD)-1; // start all set
  514. DWORD dwVolumeFlagsAND = (DWORD)-1; // start all set
  515. TCHAR szType[MAX_PATH];
  516. TCHAR szDirPath[MAX_PATH];
  517. szDirPath[0] = 0;
  518. szType[0] = 0;
  519. // For all the selected files compare their types and get their attribs
  520. for (int iItem = 0; HIDA_FillFindData(pfpsp->pfci->hida, iItem, szBuffer, NULL, FALSE); iItem++)
  521. {
  522. DWORD dwFileAttributes = GetFileAttributes(szBuffer);
  523. dwFlagsAND &= dwFileAttributes;
  524. dwFlagsOR |= dwFileAttributes;
  525. // process types only if we haven't already found that there are several types
  526. if (!fMultipleType)
  527. {
  528. SHGetFileInfo((LPTSTR)IDA_GetIDListPtr((LPIDA)GlobalLock(pfpsp->pfci->hida), iItem), 0,
  529. &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_TYPENAME);
  530. if (szType[0] == 0)
  531. lstrcpyn(szType, sfi.szTypeName, ARRAYSIZE(szType));
  532. else
  533. fMultipleType = lstrcmp(szType, sfi.szTypeName) != 0;
  534. }
  535. dwVolumeFlagsAND &= GetVolumeFlags(szBuffer, pfpsp->szFileSys, ARRAYSIZE(pfpsp->szFileSys));
  536. // check to see if the files are in the same location
  537. if (fSameLocation)
  538. {
  539. PathRemoveFileSpec(szBuffer);
  540. if (szDirPath[0] == 0)
  541. lstrcpyn(szDirPath, szBuffer, ARRAYSIZE(szDirPath));
  542. else
  543. fSameLocation = (lstrcmpi(szDirPath, szBuffer) == 0);
  544. }
  545. }
  546. if ((dwVolumeFlagsAND & FS_FILE_ENCRYPTION) && !SHRestricted(REST_NOENCRYPTION))
  547. {
  548. // all the files are on volumes that support encryption (eg NTFS)
  549. pfpsp->fIsEncryptionAvailable = TRUE;
  550. }
  551. if (dwVolumeFlagsAND & FS_FILE_COMPRESSION)
  552. {
  553. pfpsp->pfci->fIsCompressionAvailable = TRUE;
  554. }
  555. //
  556. // HACK (reinerf) - we dont have a FS_SUPPORTS_INDEXING so we
  557. // use the FILE_SUPPORTS_SPARSE_FILES flag, because native index support
  558. // appeared first on NTFS5 volumes, at the same time sparse file support
  559. // was implemented.
  560. //
  561. if (dwVolumeFlagsAND & FILE_SUPPORTS_SPARSE_FILES)
  562. {
  563. // yup, we are on NTFS5 or greater
  564. pfpsp->fIsIndexAvailable = TRUE;
  565. }
  566. // if any of the files was a directory, then we set this flag
  567. if (dwFlagsOR & FILE_ATTRIBUTE_DIRECTORY)
  568. {
  569. pfpsp->fIsDirectory = TRUE;
  570. }
  571. // setup all the flags based on what we found out
  572. SetInitialFileAttribs(pfpsp, dwFlagsAND, dwFlagsOR);
  573. // set the current attributes to the same as the initial
  574. pfpsp->asCurrent = pfpsp->asInitial;
  575. //
  576. // now setup all the controls on the dialog based on the attribs
  577. // that we have
  578. //
  579. // check for multiple file types
  580. if (fMultipleType)
  581. {
  582. LoadString(HINST_THISDLL, IDS_MULTIPLETYPES, szBuffer, ARRAYSIZE(szBuffer));
  583. }
  584. else
  585. {
  586. LoadString(HINST_THISDLL, IDS_ALLOFTYPE, szBuffer, ARRAYSIZE(szBuffer));
  587. StrCatBuff(szBuffer, szType, ARRAYSIZE(szBuffer));
  588. }
  589. SetDlgItemText(pfpsp->hDlg, IDD_FILETYPE, szBuffer);
  590. if (fSameLocation)
  591. {
  592. LoadString(HINST_THISDLL, IDS_ALLIN, szBuffer, ARRAYSIZE(szBuffer));
  593. StrCatBuff(szBuffer, szDirPath, ARRAYSIZE(szBuffer));
  594. lstrcpyn(pfpsp->szPath, szDirPath, ARRAYSIZE(pfpsp->szPath));
  595. }
  596. else
  597. {
  598. LoadString(HINST_THISDLL, IDS_VARFOLDERS, szBuffer, ARRAYSIZE(szBuffer));
  599. }
  600. //Keep Functionality same as NT4 by avoiding PathCompactPath.
  601. SetDlgItemTextWithToolTip(pfpsp->hDlg, IDD_LOCATION, szBuffer, &pfpsp->hwndTip);
  602. //
  603. // check the ReadOnly and Hidden checkboxes, they always appear on the general tab
  604. //
  605. if (pfpsp->asInitial.fReadOnly == BST_INDETERMINATE)
  606. {
  607. SendDlgItemMessage(pfpsp->hDlg, IDD_READONLY, BM_SETSTYLE, BS_AUTO3STATE, 0);
  608. }
  609. CheckDlgButton(pfpsp->hDlg, IDD_READONLY, pfpsp->asCurrent.fReadOnly);
  610. if (pfpsp->asInitial.fHidden == BST_INDETERMINATE)
  611. {
  612. SendDlgItemMessage(pfpsp->hDlg, IDD_HIDDEN, BM_SETSTYLE, BS_AUTO3STATE, 0);
  613. }
  614. CheckDlgButton(pfpsp->hDlg, IDD_HIDDEN, pfpsp->asCurrent.fHidden);
  615. // to avoid people making SYSTEM files HIDDEN (SYSTEM HIDDEN files are
  616. // never show to the user) we don't let people make SYSTEM files HIDDEN
  617. if (dwFlagsOR & FILE_ATTRIBUTE_SYSTEM)
  618. EnableWindow(GetDlgItem(pfpsp->hDlg, IDD_HIDDEN), FALSE);
  619. // Archive is only on the general tab for FAT, otherwise it is under the "Advanced attributes"
  620. // and FAT volumes dont have the "Advanced attributes" button.
  621. if (pfpsp->pfci->fIsCompressionAvailable || pfpsp->fIsEncryptionAvailable)
  622. {
  623. // if compression is available, then we must be on NTFS
  624. DestroyWindow(GetDlgItem(pfpsp->hDlg, IDD_ARCHIVE));
  625. }
  626. else
  627. {
  628. // we are on FAT/FAT32, so get rid of the "Advanced attributes" button, and set the inital Archive state
  629. DestroyWindow(GetDlgItem(pfpsp->hDlg, IDC_ADVANCED));
  630. if (pfpsp->asInitial.fArchive == BST_INDETERMINATE)
  631. {
  632. SendDlgItemMessage(pfpsp->hDlg, IDD_ARCHIVE, BM_SETSTYLE, BS_AUTO3STATE, 0);
  633. }
  634. CheckDlgButton(pfpsp->hDlg, IDD_ARCHIVE, pfpsp->asCurrent.fArchive);
  635. }
  636. UpdateSizeField(pfpsp, NULL);
  637. return TRUE;
  638. }
  639. void Free_DlgDependentFilePropSheetPage(FILEPROPSHEETPAGE* pfpsp)
  640. {
  641. // this frees the members that are dependent on pfpsp->hDlg still
  642. // being valid
  643. if (pfpsp)
  644. {
  645. ASSERT(IsWindow(pfpsp->hDlg)); // our window had better still be valid!
  646. ReplaceDlgIcon(pfpsp->hDlg, IDD_ITEMICON, NULL);
  647. if (pfpsp->pfci && !pfpsp->pfci->fMultipleFiles)
  648. {
  649. // single-file specific members
  650. if (!pfpsp->fIsDirectory)
  651. {
  652. // cleanup the typeicon for non-folders
  653. ReplaceDlgIcon(pfpsp->hDlg, IDD_TYPEICON, NULL);
  654. }
  655. }
  656. }
  657. }
  658. void Free_DlgIndepFilePropSheetPage(FILEPROPSHEETPAGE *pfpsp)
  659. {
  660. if (pfpsp)
  661. {
  662. IAssocStore* pas = (IAssocStore *)pfpsp->pAssocStore;
  663. if (pas)
  664. {
  665. delete pas;
  666. pfpsp->pAssocStore = NULL;
  667. }
  668. Release_FolderContentsInfo(pfpsp->pfci);
  669. pfpsp->pfci = NULL;
  670. ILFree(pfpsp->pidl);
  671. pfpsp->pidl = NULL;
  672. ILFree(pfpsp->pidlTarget);
  673. pfpsp->pidlTarget = NULL;
  674. }
  675. }
  676. //
  677. // Descriptions:
  678. // Callback for the property sheet code
  679. //
  680. UINT CALLBACK FilePrshtCallback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
  681. {
  682. if (uMsg == PSPCB_RELEASE)
  683. {
  684. FILEPROPSHEETPAGE * pfpsp = (FILEPROPSHEETPAGE *)ppsp;
  685. // Careful! pfpsp can be NULL in low memory situations
  686. if (pfpsp)
  687. {
  688. KillSizeThread(pfpsp);
  689. Free_DlgIndepFilePropSheetPage(pfpsp);
  690. }
  691. }
  692. return 1;
  693. }
  694. //
  695. // DESCRIPTION:
  696. //
  697. // Opens the file for compression. It handles the case where a READONLY
  698. // file is trying to be compressed or uncompressed. Since read only files
  699. // cannot be opened for WRITE_DATA, it temporarily resets the file to NOT
  700. // be READONLY in order to open the file, and then sets it back once the
  701. // file has been compressed.
  702. //
  703. // Taken from WinFile module wffile.c without change. Originally from
  704. // G. Kimura's compact.c. Now taken from shcompui without change.
  705. //
  706. // ARGUMENTS:
  707. //
  708. // phFile
  709. // Address of file handle variable for handle of open file if
  710. // successful.
  711. //
  712. // szFile
  713. // Name string of file to be opened.
  714. //
  715. // RETURNS:
  716. //
  717. // TRUE = File successfully opened. Handle in *phFile.
  718. // FALSE = File couldn't be opened. *phFile == INVALID_HANDLE_VALUE
  719. //
  720. ///////////////////////////////////////////////////////////////////////////////
  721. BOOL OpenFileForCompress(HANDLE *phFile, LPCTSTR szFile)
  722. {
  723. //
  724. // Try to open the file - READ_DATA | WRITE_DATA.
  725. //
  726. if ((*phFile = CreateFile(szFile,
  727. FILE_READ_DATA | FILE_WRITE_DATA,
  728. FILE_SHARE_READ | FILE_SHARE_WRITE,
  729. NULL,
  730. OPEN_EXISTING,
  731. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
  732. NULL)) != INVALID_HANDLE_VALUE)
  733. {
  734. //
  735. // Successfully opened the file.
  736. //
  737. return TRUE;
  738. }
  739. if (GetLastError() != ERROR_ACCESS_DENIED)
  740. {
  741. return FALSE;
  742. }
  743. //
  744. // Try to open the file - READ_ATTRIBUTES | WRITE_ATTRIBUTES.
  745. //
  746. HANDLE hAttr = CreateFile(szFile,
  747. FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
  748. FILE_SHARE_READ | FILE_SHARE_WRITE,
  749. NULL,
  750. OPEN_EXISTING,
  751. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
  752. NULL);
  753. if (hAttr == INVALID_HANDLE_VALUE)
  754. {
  755. return FALSE;
  756. }
  757. //
  758. // See if the READONLY attribute is set.
  759. //
  760. BY_HANDLE_FILE_INFORMATION fi;
  761. if ((!GetFileInformationByHandle(hAttr, &fi)) ||
  762. (!(fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)))
  763. {
  764. //
  765. // If the file could not be open for some reason other than that
  766. // the readonly attribute was set, then fail.
  767. //
  768. CloseHandle(hAttr);
  769. return FALSE;
  770. }
  771. //
  772. // Turn OFF the READONLY attribute.
  773. //
  774. fi.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
  775. if (!SetFileAttributes(szFile, fi.dwFileAttributes))
  776. {
  777. CloseHandle(hAttr);
  778. return FALSE;
  779. }
  780. //
  781. // Try again to open the file - READ_DATA | WRITE_DATA.
  782. //
  783. *phFile = CreateFile(szFile,
  784. FILE_READ_DATA | FILE_WRITE_DATA,
  785. FILE_SHARE_READ | FILE_SHARE_WRITE,
  786. NULL,
  787. OPEN_EXISTING,
  788. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
  789. NULL);
  790. //
  791. // Close the file handle opened for READ_ATTRIBUTE | WRITE_ATTRIBUTE.
  792. //
  793. CloseHandle(hAttr);
  794. //
  795. // Make sure the open succeeded. If it still couldn't be opened with
  796. // the readonly attribute turned off, then fail.
  797. //
  798. if (*phFile == INVALID_HANDLE_VALUE)
  799. {
  800. return FALSE;
  801. }
  802. //
  803. // Turn the READONLY attribute back ON.
  804. //
  805. fi.dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
  806. if (!SetFileAttributes(szFile, fi.dwFileAttributes))
  807. {
  808. CloseHandle(*phFile);
  809. *phFile = INVALID_HANDLE_VALUE;
  810. return FALSE;
  811. }
  812. //
  813. // Return success. A valid file handle is in *phFile.
  814. //
  815. return TRUE;
  816. }
  817. // One half second (500 ms = 0.5s)
  818. #define ENCRYPT_RETRY_PERIOD 500
  819. // Retry 4 times (at least 2s)
  820. #define ENCRYPT_MAX_RETRIES 4
  821. //
  822. // This function encrypts/decrypts a file. If the readonly bit is set, the
  823. // function will clear it and encrypt/decrypt and then set the RO bit back
  824. // We will also remove/replace the system bit for known encryptable system
  825. // files
  826. //
  827. // szPath a string that has the full path to the file
  828. // fCompress TRUE - compress the file
  829. // FALSE - decompress the file
  830. //
  831. //
  832. // return: TRUE - the file was sucessfully encryped/decryped
  833. // FALSE - the file could not be encryped/decryped
  834. //
  835. STDAPI_(BOOL) SHEncryptFile(LPCTSTR pszPath, BOOL fEncrypt)
  836. {
  837. BOOL bRet = fEncrypt ? EncryptFile(pszPath) : DecryptFile(pszPath, 0);
  838. if (!bRet)
  839. {
  840. DWORD dwLastError = GetLastError();
  841. DWORD dwAttribs = GetFileAttributes(pszPath);
  842. // Check to see if the attributes are blocking the encryption and we can change them
  843. if (dwAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))
  844. {
  845. BOOL fStripAttribs = TRUE;
  846. if (dwAttribs & FILE_ATTRIBUTE_SYSTEM)
  847. {
  848. fStripAttribs = FALSE;
  849. // We can only strip attributes if it is a know encryptable system file
  850. WCHAR szStream[MAX_PATH + 13];
  851. StrCpyNW(szStream, pszPath, ARRAYSIZE(szStream));
  852. StrCatW(szStream, TEXT(":encryptable"));
  853. HANDLE hStream = CreateFile(szStream, GENERIC_READ, NULL, NULL, OPEN_EXISTING, NULL, NULL);
  854. if (hStream != INVALID_HANDLE_VALUE)
  855. {
  856. CloseHandle(hStream);
  857. fStripAttribs = TRUE;
  858. }
  859. }
  860. if (fStripAttribs)
  861. {
  862. if (SetFileAttributes(pszPath, dwAttribs & ~FILE_ATTRIBUTE_READONLY & ~FILE_ATTRIBUTE_SYSTEM))
  863. {
  864. int i = 0;
  865. while (!bRet && i < ENCRYPT_MAX_RETRIES)
  866. {
  867. bRet = fEncrypt ? EncryptFile(pszPath) : DecryptFile(pszPath, 0);
  868. if (!bRet)
  869. {
  870. i++;
  871. Sleep(ENCRYPT_RETRY_PERIOD);
  872. }
  873. }
  874. SetFileAttributes(pszPath, dwAttribs);
  875. }
  876. }
  877. }
  878. // If we failed after all this, make sure we return the right error code
  879. if (!bRet)
  880. {
  881. ASSERT(dwLastError != ERROR_SUCCESS);
  882. SetLastError(dwLastError);
  883. }
  884. }
  885. return bRet;
  886. }
  887. //
  888. // This function compresses/uncompresses a file.
  889. //
  890. // szPath a string that has the full path to the file
  891. // fCompress TRUE - compress the file
  892. // FALSE - decompress the file
  893. //
  894. //
  895. // return: TRUE - the file was sucessfully compressed/uncompressed
  896. // FALSE - the file could not be compressed/uncompressed
  897. //
  898. BOOL CompressFile(LPCTSTR pzsPath, BOOL fCompress)
  899. {
  900. DWORD dwAttribs = GetFileAttributes(pzsPath);
  901. if (dwAttribs & FILE_ATTRIBUTE_ENCRYPTED)
  902. {
  903. // We will fail to compress/decompress the file if is encryped. We don't want
  904. // to bother the user w/ error messages in this case (since encryption "takes
  905. // presidence" over compression), so we just return success.
  906. return TRUE;
  907. }
  908. HANDLE hFile;
  909. if (OpenFileForCompress(&hFile, pzsPath))
  910. {
  911. USHORT uState = fCompress ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
  912. ULONG Length;
  913. BOOL bRet = DeviceIoControl(hFile,
  914. FSCTL_SET_COMPRESSION,
  915. &uState,
  916. sizeof(USHORT),
  917. NULL,
  918. 0,
  919. &Length,
  920. FALSE);
  921. CloseHandle(hFile);
  922. return bRet;
  923. }
  924. else
  925. {
  926. // couldnt get a file handle
  927. return FALSE;
  928. }
  929. }
  930. BOOL IsValidFileName(LPCTSTR pszFileName)
  931. {
  932. if (!pszFileName || !pszFileName[0])
  933. {
  934. return FALSE;
  935. }
  936. LPCTSTR psz = pszFileName;
  937. do
  938. {
  939. // we are only passed the file name, so its ok to use PIVC_LFN_NAME
  940. if (!PathIsValidChar(*psz, PIVC_LFN_NAME))
  941. {
  942. // found a non-legal character
  943. return FALSE;
  944. }
  945. psz = CharNext(psz);
  946. }
  947. while (*psz);
  948. // didn't find any illegal characters
  949. return TRUE;
  950. }
  951. // renames the file, or checks to see if it could be renamed if fCommit == FALSE
  952. BOOL ApplyRename(FILEPROPSHEETPAGE* pfpsp, BOOL fCommit)
  953. {
  954. ASSERT(pfpsp->fRename);
  955. TCHAR szNewName[MAX_PATH];
  956. Edit_GetText(GetDlgItem(pfpsp->hDlg, IDD_NAMEEDIT), szNewName, ARRAYSIZE(szNewName));
  957. if (StrCmpC(pfpsp->szInitialName, szNewName) != 0)
  958. {
  959. // the name could be changed from C:\foo.txt to C:\FOO.txt, this is
  960. // technically the same name to PathFileExists, but we should allow it
  961. // anyway
  962. BOOL fCaseChange = (lstrcmpi(pfpsp->szInitialName, szNewName) == 0);
  963. // get the dir where the file lives
  964. TCHAR szDir[MAX_PATH];
  965. lstrcpyn(szDir, pfpsp->szPath, ARRAYSIZE(szDir));
  966. PathRemoveFileSpec(szDir);
  967. // find out the old name with the extension (we cant use pfpsp->szInitialName here,
  968. // because it might not have had the extension depending on the users view|options settings)
  969. LPCTSTR pszOldName = PathFindFileName(pfpsp->szPath);
  970. if (!pfpsp->fShowExtension)
  971. {
  972. // the extension is hidden, so add it to the new path the user typed
  973. LPCTSTR pszExt = PathFindExtension(pfpsp->szPath);
  974. if (*pszExt)
  975. {
  976. // Note that we can't call PathAddExtension, because it removes the existing extension.
  977. lstrcatn(szNewName, pszExt, ARRAYSIZE(szNewName));
  978. }
  979. }
  980. // is this a test or is it the real thing? (test needed so we can put up error UI before we get
  981. // the PSN_LASTCHANCEAPPLY)
  982. if (fCommit)
  983. {
  984. if (SHRenameFileEx(pfpsp->hDlg, NULL, szDir, pszOldName, szNewName) == ERROR_SUCCESS)
  985. {
  986. SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_FLUSH | SHCNF_PATH, pszOldName, szNewName);
  987. }
  988. else
  989. {
  990. return FALSE; // dont need error ui because SHRenameFile takes care of that for us.
  991. }
  992. }
  993. else
  994. {
  995. TCHAR szNewPath[MAX_PATH];
  996. PathCombine(szNewPath, szDir, szNewName);
  997. if (!IsValidFileName(szNewName) || (PathFileExists(szNewPath) && !fCaseChange))
  998. {
  999. LRESULT lRet = SHRenameFileEx(pfpsp->hDlg, NULL, szDir, pszOldName, szNewName);
  1000. if (lRet == ERROR_SUCCESS)
  1001. {
  1002. // Whoops, I guess we really CAN rename the file (this case can happen if the user
  1003. // tries to add a whole bunch of .'s to the end of a folder name).
  1004. // Rename it back so we can succeed when we call this fn. again with fCommit = TRUE;
  1005. lRet = SHRenameFileEx(NULL, NULL, szDir, szNewName, pszOldName);
  1006. ASSERT(lRet == ERROR_SUCCESS);
  1007. return TRUE;
  1008. }
  1009. // SHRenameFileEx put up the error UI for us, so just return false.
  1010. return FALSE;
  1011. }
  1012. }
  1013. // we dont bother doing anything if the rename succeeded since we only do renames
  1014. // if the dialog is about to close (user hit "ok")
  1015. }
  1016. return TRUE;
  1017. }
  1018. //
  1019. // this is the dlg proc for Attribute Errors
  1020. //
  1021. // returns
  1022. //
  1023. // IDCANCEL - user clicked abort
  1024. // IDRETRY - user clicked retry
  1025. // IDIGNORE - user clicked ignore
  1026. // IDIGNOREALL - user clikced ignore all
  1027. //
  1028. BOOL_PTR CALLBACK FailedApplyAttribDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1029. {
  1030. switch (uMsg)
  1031. {
  1032. case WM_INITDIALOG:
  1033. {
  1034. ATTRIBUTEERROR* pae = (ATTRIBUTEERROR*)lParam;
  1035. TCHAR szPath[MAX_PATH];
  1036. lstrcpyn(szPath, pae->pszPath, ARRAYSIZE(szPath));
  1037. // Modify very long path names so that they fit into the message box.
  1038. // get the size of the text boxes
  1039. RECT rc;
  1040. GetWindowRect(GetDlgItem(hDlg, IDD_NAME), &rc);
  1041. PathCompactPath(NULL, szPath, rc.right - rc.left);
  1042. SetDlgItemText(hDlg, IDD_NAME, szPath);
  1043. // Default message if FormatMessage doesn't recognize dwLastError
  1044. TCHAR szTemplate[MAX_PATH];
  1045. LoadString(HINST_THISDLL, IDS_UNKNOWNERROR, szTemplate, ARRAYSIZE(szTemplate));
  1046. TCHAR szErrorMsg[MAX_PATH];
  1047. wnsprintf(szErrorMsg, ARRAYSIZE(szErrorMsg), szTemplate, pae->dwLastError);
  1048. // Try the system error message
  1049. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, pae->dwLastError, 0, szErrorMsg, ARRAYSIZE(szErrorMsg), NULL);
  1050. SetDlgItemText(hDlg, IDD_ERROR_TXT, szErrorMsg);
  1051. EnableWindow(hDlg, TRUE);
  1052. break;
  1053. }
  1054. case WM_COMMAND:
  1055. {
  1056. UINT uCtrlID = GET_WM_COMMAND_ID(wParam, lParam);
  1057. switch (uCtrlID)
  1058. {
  1059. case IDIGNOREALL: // = 10 (this comes from shell32.rc, the rest come from winuser.h)
  1060. case IDCANCEL: // = 2
  1061. case IDRETRY: // = 4
  1062. case IDIGNORE: // = 5
  1063. EndDialog(hDlg, uCtrlID);
  1064. return TRUE;
  1065. break;
  1066. default:
  1067. return FALSE;
  1068. }
  1069. break;
  1070. }
  1071. default :
  1072. return FALSE;
  1073. }
  1074. return FALSE;
  1075. }
  1076. //
  1077. // This function displays the "and error has occured [abort] [retry] [ignore] [ignore all]" message
  1078. // If the user hits abort, then we return FALSE so that our caller knows to abort the operation
  1079. //
  1080. // returns the id of the button pressed (one of: IDIGNOREALL, IDIGNORE, IDCANCEL, IDRETRY)
  1081. //
  1082. int FailedApplyAttribsErrorDlg(HWND hWndParent, ATTRIBUTEERROR* pae)
  1083. {
  1084. // Put up the error message box - ABORT, RETRY, IGNORE, IGNORE ALL.
  1085. int iRet = (int)DialogBoxParam(HINST_THISDLL,
  1086. MAKEINTRESOURCE(DLG_ATTRIBS_ERROR),
  1087. hWndParent,
  1088. FailedApplyAttribDlgProc,
  1089. (LPARAM)pae);
  1090. //
  1091. // if the user hits the ESC key or the little X thingy, then
  1092. // iRet = 0, so we set iRet = IDCANCEL
  1093. //
  1094. if (!iRet)
  1095. {
  1096. iRet = IDCANCEL;
  1097. }
  1098. return iRet;
  1099. }
  1100. //
  1101. // we check to see if this is a known bad file that we skip applying attribs to
  1102. //
  1103. BOOL IsBadAttributeFile(LPCTSTR pszFile, FILEPROPSHEETPAGE* pfpsp)
  1104. {
  1105. const static LPTSTR s_rgszBadFiles[] = {
  1106. {TEXT("pagefile.sys")},
  1107. {TEXT("hiberfil.sys")},
  1108. {TEXT("ntldr")},
  1109. {TEXT("ntdetect.com")},
  1110. {TEXT("explorer.exe")},
  1111. {TEXT("System Volume Information")},
  1112. {TEXT("cmldr")},
  1113. {TEXT("desktop.ini")},
  1114. {TEXT("ntuser.dat")},
  1115. {TEXT("ntuser.dat.log")},
  1116. {TEXT("ntuser.pol")},
  1117. {TEXT("usrclass.dat")},
  1118. {TEXT("usrclass.dat.log")}};
  1119. LPTSTR pszFileName = PathFindFileName(pszFile);
  1120. for (int i = 0; i < ARRAYSIZE(s_rgszBadFiles); i++)
  1121. {
  1122. if (lstrcmpi(s_rgszBadFiles[i], pszFileName) == 0)
  1123. {
  1124. // this file matched on of the "bad" files that we dont apply attributes to
  1125. return TRUE;
  1126. }
  1127. }
  1128. // ok to muck with this file
  1129. return FALSE;
  1130. }
  1131. // This is the encryption warning callback dlg proc
  1132. BOOL_PTR CALLBACK EncryptionWarningDlgProc(HWND hDlgWarning, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1133. {
  1134. switch (uMsg)
  1135. {
  1136. case WM_INITDIALOG:
  1137. {
  1138. LPCTSTR pszPath = (LPCTSTR)lParam;
  1139. SetWindowPtr(hDlgWarning, DWLP_USER, (void*) pszPath);
  1140. // set the initial state of the radio buttons
  1141. CheckDlgButton(hDlgWarning, IDC_ENCRYPT_PARENTFOLDER, TRUE);
  1142. break;
  1143. }
  1144. case WM_COMMAND:
  1145. {
  1146. if ((LOWORD(wParam) == IDOK) && (IsDlgButtonChecked(hDlgWarning, IDC_ENCRYPT_PARENTFOLDER) == BST_CHECKED))
  1147. {
  1148. LPTSTR pszPath = (LPTSTR) GetWindowPtr(hDlgWarning, DWLP_USER);
  1149. if (pszPath)
  1150. {
  1151. LPITEMIDLIST pidl = ILCreateFromPath(pszPath);
  1152. if (pidl)
  1153. {
  1154. SHChangeNotifySuspendResume(TRUE, pidl, TRUE, 0);
  1155. }
  1156. RetryEncryptParentFolder:
  1157. if (!SHEncryptFile(pszPath, TRUE))
  1158. {
  1159. ATTRIBUTEERROR ae = {pszPath, GetLastError()};
  1160. if (FailedApplyAttribsErrorDlg(hDlgWarning, &ae) == IDRETRY)
  1161. {
  1162. goto RetryEncryptParentFolder;
  1163. }
  1164. }
  1165. if (pidl)
  1166. {
  1167. SHChangeNotifySuspendResume(FALSE, pidl, TRUE, 0);
  1168. ILFree(pidl);
  1169. }
  1170. }
  1171. }
  1172. break;
  1173. }
  1174. }
  1175. // we want the MessageBoxCheckExDlgProc have a crack at everything as well,
  1176. // so return false here
  1177. return FALSE;
  1178. }
  1179. //
  1180. // This function warns the user that they are encrypting a file that is not in and encrypted
  1181. // folder. Most editors (MS word included), do a "safe-save" where they rename the file being
  1182. // edited, and then save the new modified version out, and then delete the old original. This
  1183. // causes an encrypted document that is NOT in an encrypted folder to become decrypted so we
  1184. // warn the user here.
  1185. //
  1186. // returns:
  1187. // TRUE - the user hit "ok" (either compress just the file, or the parent folder as well)
  1188. // FALSE - the user hit "cancel"
  1189. //
  1190. int WarnUserAboutDecryptedParentFolder(LPCTSTR pszPath, HWND hWndParent)
  1191. {
  1192. // check for the root case (no parent), or the directory case
  1193. if (PathIsRoot(pszPath) || PathIsDirectory(pszPath))
  1194. return TRUE;
  1195. int iRet = IDOK; // assume everything is okidokey
  1196. // first check to see if the parent folder is encrypted
  1197. TCHAR szParentFolder[MAX_PATH];
  1198. lstrcpyn(szParentFolder, pszPath, ARRAYSIZE(szParentFolder));
  1199. PathRemoveFileSpec(szParentFolder);
  1200. DWORD dwAttribs = GetFileAttributes(szParentFolder);
  1201. if ((dwAttribs != (DWORD)-1) && !(dwAttribs & FILE_ATTRIBUTE_ENCRYPTED) && !PathIsRoot(szParentFolder))
  1202. {
  1203. // the parent folder is NOT encrypted and the parent folder isin't the root, so warn the user
  1204. iRet = SHMessageBoxCheckEx(hWndParent, HINST_THISDLL, MAKEINTRESOURCE(DLG_ENCRYPTWARNING), EncryptionWarningDlgProc,
  1205. (void *)szParentFolder, IDOK, TEXT("EncryptionWarning"));
  1206. }
  1207. return (iRet == IDOK);
  1208. }
  1209. //
  1210. // Sets attributes of a file based on the info in pfpsp
  1211. //
  1212. // szFilename - the name of the file to compress
  1213. //
  1214. // pfpsp - the filepropsheetpage info
  1215. //
  1216. // hwndParent - Parent hwnd in case we need to put up some ui
  1217. //
  1218. // pbSomethingChanged - pointer to a bool that says whether or not something actually was
  1219. // changed during the operation.
  1220. // TRUE - we applied at leaset one attribute
  1221. // FALSE - we didnt change anything (either an error or all the attribs already matched)
  1222. //
  1223. // return value: TRUE - the operation was sucessful
  1224. // FALSE - there was an error and the user hit cancel to abort the operation
  1225. //
  1226. //
  1227. // NOTE: the caller of this function must take care of generating the SHChangeNotifies so that
  1228. // we dont end up blindly sending them for every file in a dir (the caller will send
  1229. // one for just that dir). That is why we have the pbSomethingChanged variable.
  1230. //
  1231. STDAPI_(BOOL) ApplyFileAttributes(LPCTSTR pszPath, FILEPROPSHEETPAGE* pfpsp, HWND hwndParent, BOOL* pbSomethingChanged)
  1232. {
  1233. DWORD dwLastError = ERROR_SUCCESS;
  1234. BOOL bCallSetFileAttributes = FALSE;
  1235. LPITEMIDLIST pidl = NULL;
  1236. // assume nothing changed to start with
  1237. *pbSomethingChanged = 0;
  1238. if ((pfpsp->fRecursive || pfpsp->pfci->fMultipleFiles) && IsBadAttributeFile(pszPath, pfpsp))
  1239. {
  1240. // we are doing a recursive operation or a multiple file operation, so we skip files
  1241. // that we dont want to to mess with because they will ususally give error dialogs
  1242. if (pfpsp->pProgressDlg)
  1243. {
  1244. // since we are skipping this file, we subtract its size from both
  1245. // ulTotal and ulCompleted. This will make sure the progress bar isint
  1246. // messed up by files like pagefile.sys who are huge but get "compressed"
  1247. // in milliseconds.
  1248. ULARGE_INTEGER ulTemp;
  1249. ulTemp.LowPart = pfpsp->fd.nFileSizeLow;
  1250. ulTemp.HighPart = pfpsp->fd.nFileSizeHigh;
  1251. // guard against underflow
  1252. if (pfpsp->ulNumberOfBytesDone.QuadPart < ulTemp.QuadPart)
  1253. {
  1254. pfpsp->ulNumberOfBytesDone.QuadPart = 0;
  1255. }
  1256. else
  1257. {
  1258. pfpsp->ulNumberOfBytesDone.QuadPart -= ulTemp.QuadPart;
  1259. }
  1260. pfpsp->pfci->ulTotalNumberOfBytes.QuadPart -= ulTemp.QuadPart;
  1261. UpdateProgressBar(pfpsp);
  1262. }
  1263. // return telling the user everying is okidokey
  1264. return TRUE;
  1265. }
  1266. RetryApplyAttribs:
  1267. DWORD dwInitialAttributes = GetFileAttributes(pszPath);
  1268. if (dwInitialAttributes == -1)
  1269. {
  1270. // we were unable to get the file attribues, doh!
  1271. dwLastError = GetLastError();
  1272. goto RaiseErrorMsg;
  1273. }
  1274. if (pfpsp->pProgressDlg)
  1275. {
  1276. // update the progress dialog file name
  1277. SetProgressDlgPath(pfpsp, pszPath, TRUE);
  1278. }
  1279. //
  1280. // we only allow attribs that SetFileAttributes can handle
  1281. //
  1282. DWORD dwNewAttributes = (dwInitialAttributes & (FILE_ATTRIBUTE_READONLY |
  1283. FILE_ATTRIBUTE_HIDDEN |
  1284. FILE_ATTRIBUTE_ARCHIVE |
  1285. FILE_ATTRIBUTE_OFFLINE |
  1286. FILE_ATTRIBUTE_SYSTEM |
  1287. FILE_ATTRIBUTE_TEMPORARY |
  1288. FILE_ATTRIBUTE_NOT_CONTENT_INDEXED));
  1289. BOOL bIsSuperHidden = IS_SYSTEM_HIDDEN(dwInitialAttributes);
  1290. if (pfpsp->asInitial.fReadOnly != pfpsp->asCurrent.fReadOnly)
  1291. {
  1292. // don't allow changing of folders read only bit, since this is a trigger
  1293. // for shell special folder stuff like thumbnails, etc.
  1294. if (!(dwInitialAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1295. {
  1296. if (pfpsp->asCurrent.fReadOnly)
  1297. {
  1298. dwNewAttributes |= FILE_ATTRIBUTE_READONLY;
  1299. }
  1300. else
  1301. {
  1302. dwNewAttributes &= ~FILE_ATTRIBUTE_READONLY;
  1303. }
  1304. bCallSetFileAttributes = TRUE;
  1305. }
  1306. }
  1307. //
  1308. // don't allow setting of hidden on system files, as this will make them disappear for good.
  1309. //
  1310. if (pfpsp->asInitial.fHidden != pfpsp->asCurrent.fHidden && !(dwNewAttributes & FILE_ATTRIBUTE_SYSTEM))
  1311. {
  1312. if (pfpsp->asCurrent.fHidden)
  1313. {
  1314. dwNewAttributes |= FILE_ATTRIBUTE_HIDDEN;
  1315. }
  1316. else
  1317. {
  1318. dwNewAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
  1319. }
  1320. bCallSetFileAttributes = TRUE;
  1321. }
  1322. if (pfpsp->asInitial.fArchive != pfpsp->asCurrent.fArchive)
  1323. {
  1324. if (pfpsp->asCurrent.fArchive)
  1325. {
  1326. dwNewAttributes |= FILE_ATTRIBUTE_ARCHIVE;
  1327. }
  1328. else
  1329. {
  1330. dwNewAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
  1331. }
  1332. bCallSetFileAttributes = TRUE;
  1333. }
  1334. if (pfpsp->asInitial.fIndex != pfpsp->asCurrent.fIndex)
  1335. {
  1336. if (pfpsp->asCurrent.fIndex)
  1337. {
  1338. dwNewAttributes &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
  1339. }
  1340. else
  1341. {
  1342. dwNewAttributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
  1343. }
  1344. bCallSetFileAttributes = TRUE;
  1345. }
  1346. // did something change that we need to call SetFileAttributes for?
  1347. if (bCallSetFileAttributes)
  1348. {
  1349. if (SetFileAttributes(pszPath, dwNewAttributes))
  1350. {
  1351. // success! set fSomethingChanged so we know to send out
  1352. // a changenotify
  1353. *pbSomethingChanged = TRUE;
  1354. }
  1355. else
  1356. {
  1357. // get the last error value now so we know why it failed
  1358. dwLastError = GetLastError();
  1359. goto RaiseErrorMsg;
  1360. }
  1361. }
  1362. // We need to be careful about the order we compress/encrypt in since these
  1363. // operations are mutually exclusive.
  1364. // We therefore do the uncompressing/decrypting first
  1365. if ((pfpsp->asInitial.fCompress != pfpsp->asCurrent.fCompress) &&
  1366. (pfpsp->asCurrent.fCompress == BST_UNCHECKED))
  1367. {
  1368. if (!CompressFile(pszPath, FALSE))
  1369. {
  1370. // get the last error value now so we know why it failed
  1371. dwLastError = GetLastError();
  1372. goto RaiseErrorMsg;
  1373. }
  1374. else
  1375. {
  1376. // success
  1377. *pbSomethingChanged = TRUE;
  1378. }
  1379. }
  1380. if ((pfpsp->asInitial.fEncrypt != pfpsp->asCurrent.fEncrypt) &&
  1381. (pfpsp->asCurrent.fEncrypt == BST_UNCHECKED))
  1382. {
  1383. BOOL fSucceeded = SHEncryptFile(pszPath, FALSE); // try to decrypt the file
  1384. if (!fSucceeded)
  1385. {
  1386. // get the last error value now so we know why it failed
  1387. dwLastError = GetLastError();
  1388. if (ERROR_SHARING_VIOLATION == dwLastError)
  1389. {
  1390. // Encrypt/Decrypt needs exclusive access to the file, this is a problem if we
  1391. // initiate encrypt for a folder from Explorer, then most probably the folder will
  1392. // be opened. We don't do "SHChangeNotifySuspendResume" right away for perf reasons,
  1393. // we wait for it to fail and then we try again. (stephstm)
  1394. ASSERT(pidl == NULL);
  1395. pidl = ILCreateFromPath(pszPath);
  1396. if (pidl)
  1397. {
  1398. SHChangeNotifySuspendResume(TRUE, pidl, TRUE, 0);
  1399. }
  1400. // retry to decrypt after the suspend
  1401. fSucceeded = SHEncryptFile(pszPath, FALSE);
  1402. if (!fSucceeded)
  1403. {
  1404. // get the last error value now so we know why it failed
  1405. dwLastError = GetLastError();
  1406. }
  1407. }
  1408. }
  1409. if (fSucceeded)
  1410. {
  1411. // success
  1412. *pbSomethingChanged = TRUE;
  1413. dwLastError = ERROR_SUCCESS;
  1414. }
  1415. else
  1416. {
  1417. ASSERT(dwLastError != ERROR_SUCCESS);
  1418. goto RaiseErrorMsg;
  1419. }
  1420. }
  1421. // now check for encrypt/compress
  1422. if ((pfpsp->asInitial.fCompress != pfpsp->asCurrent.fCompress) &&
  1423. (pfpsp->asCurrent.fCompress == BST_CHECKED))
  1424. {
  1425. if (!CompressFile(pszPath, TRUE))
  1426. {
  1427. // get the last error value now so we know why it failed
  1428. dwLastError = GetLastError();
  1429. goto RaiseErrorMsg;
  1430. }
  1431. else
  1432. {
  1433. // success
  1434. *pbSomethingChanged = TRUE;
  1435. }
  1436. }
  1437. if ((pfpsp->asInitial.fEncrypt != pfpsp->asCurrent.fEncrypt) &&
  1438. (pfpsp->asCurrent.fEncrypt == BST_CHECKED))
  1439. {
  1440. // only prompt for encrypting the parent folder on non-recursive operations
  1441. if (!pfpsp->fRecursive && !WarnUserAboutDecryptedParentFolder(pszPath, hwndParent))
  1442. {
  1443. // user cancled the operation
  1444. return FALSE;
  1445. }
  1446. BOOL fSucceeded = SHEncryptFile(pszPath, TRUE); // try to encrypt the file
  1447. if (!fSucceeded)
  1448. {
  1449. // get the last error value now so we know why it failed
  1450. dwLastError = GetLastError();
  1451. if (ERROR_SHARING_VIOLATION == dwLastError)
  1452. {
  1453. // Encrypt/Decrypt needs exclusive access to the file, this is a problem if we
  1454. // initiate encrypt for a folder from Explorer, then most probably the folder will
  1455. // be opened. We don't do "SHChangeNotifySuspendResume" right away for perf reasons,
  1456. // we wait for it to fail and then we try again. (stephstm)
  1457. ASSERT(pidl == NULL);
  1458. pidl = ILCreateFromPath(pszPath);
  1459. if (pidl)
  1460. {
  1461. SHChangeNotifySuspendResume(TRUE, pidl, TRUE, 0);
  1462. }
  1463. // retry to encrypt after the suspend
  1464. fSucceeded = SHEncryptFile(pszPath, TRUE);
  1465. if (!fSucceeded)
  1466. {
  1467. // get the last error value now so we know why it failed
  1468. dwLastError = GetLastError();
  1469. }
  1470. }
  1471. }
  1472. if (fSucceeded)
  1473. {
  1474. // success
  1475. *pbSomethingChanged = TRUE;
  1476. dwLastError = ERROR_SUCCESS;
  1477. }
  1478. else
  1479. {
  1480. ASSERT(dwLastError != ERROR_SUCCESS);
  1481. goto RaiseErrorMsg;
  1482. }
  1483. }
  1484. RaiseErrorMsg:
  1485. if (pidl)
  1486. {
  1487. SHChangeNotifySuspendResume(FALSE, pidl, TRUE, 0);
  1488. ILFree(pidl);
  1489. pidl = NULL;
  1490. }
  1491. // if we are ignoring all errors or we dont have an hwnd to use as a parent,
  1492. // then dont show any error msgs.
  1493. if (pfpsp->fIgnoreAllErrors || !hwndParent)
  1494. {
  1495. dwLastError = ERROR_SUCCESS;
  1496. }
  1497. // If kernel threw up an error dialog (such as "the disk is write proctected")
  1498. // and the user hit "abort" then return false to avoid a second error dialog
  1499. if (dwLastError == ERROR_REQUEST_ABORTED)
  1500. {
  1501. return FALSE;
  1502. }
  1503. // put up the error dlg if necessary, but not for super hidden files
  1504. if (dwLastError != ERROR_SUCCESS)
  1505. {
  1506. // !PathIsRoot is required, since the root path (eg c:\) is superhidden by default even after formatting a drive,
  1507. // why the filesystem thinks that the root should be +s +r after a format is a mystery to me...
  1508. if (bIsSuperHidden && !ShowSuperHidden() && !PathIsRoot(pszPath))
  1509. {
  1510. dwLastError = ERROR_SUCCESS;
  1511. }
  1512. else
  1513. {
  1514. ATTRIBUTEERROR ae;
  1515. ae.pszPath = pszPath;
  1516. ae.dwLastError = dwLastError;
  1517. int iRet = FailedApplyAttribsErrorDlg(hwndParent, &ae);
  1518. switch (iRet)
  1519. {
  1520. case IDRETRY:
  1521. // we clear out dwError and try again
  1522. dwLastError = ERROR_SUCCESS;
  1523. goto RetryApplyAttribs;
  1524. break;
  1525. case IDIGNOREALL:
  1526. pfpsp->fIgnoreAllErrors = TRUE;
  1527. dwLastError = ERROR_SUCCESS;
  1528. break;
  1529. case IDIGNORE:
  1530. dwLastError = ERROR_SUCCESS;
  1531. break;
  1532. case IDCANCEL:
  1533. default:
  1534. break;
  1535. }
  1536. }
  1537. }
  1538. // update the progress bar
  1539. if (pfpsp->pProgressDlg)
  1540. {
  1541. ULARGE_INTEGER ulTemp;
  1542. // it is the callers responsibility to make sure that pfpsp->fd is filled with
  1543. // the proper information for the file we are applying attributes to.
  1544. ulTemp.LowPart = pfpsp->fd.nFileSizeLow;
  1545. ulTemp.HighPart = pfpsp->fd.nFileSizeHigh;
  1546. pfpsp->ulNumberOfBytesDone.QuadPart += ulTemp.QuadPart;
  1547. UpdateProgressBar(pfpsp);
  1548. }
  1549. return (dwLastError == ERROR_SUCCESS) ? TRUE : FALSE;
  1550. }
  1551. //
  1552. // Set the text of a dialog item and attach a tooltip if necessary.
  1553. //
  1554. STDAPI_(void) SetDlgItemTextWithToolTip(HWND hDlg, UINT id, LPCTSTR pszText, HWND *phwnd)
  1555. {
  1556. HWND hwnd = GetDlgItem(hDlg, id);
  1557. if (hwnd)
  1558. {
  1559. SetWindowText(hwnd, pszText);
  1560. RECT rc;
  1561. HDC hDC;
  1562. if (GetClientRect(hwnd, &rc) && (hDC = GetDC(hDlg)) != NULL)
  1563. {
  1564. HFONT hFont = GetWindowFont(hwnd);
  1565. if (hFont)
  1566. {
  1567. // set the dlg font into the DC so we can calc the size
  1568. hFont = (HFONT)SelectObject(hDC, hFont);
  1569. SIZE size = {0};
  1570. GetTextExtentPoint32(hDC, pszText, lstrlen(pszText), &size);
  1571. // restore the prev. hFont
  1572. SelectObject(hDC, hFont);
  1573. if (size.cx > rc.right)
  1574. {
  1575. // our text size is bigger than the dlg width, so its clipped
  1576. if (*phwnd == NULL)
  1577. {
  1578. *phwnd = CreateWindow(TOOLTIPS_CLASS,
  1579. c_szNULL,
  1580. WS_POPUP | TTS_NOPREFIX,
  1581. CW_USEDEFAULT,
  1582. CW_USEDEFAULT,
  1583. CW_USEDEFAULT,
  1584. CW_USEDEFAULT,
  1585. hDlg,
  1586. NULL,
  1587. HINST_THISDLL,
  1588. NULL);
  1589. }
  1590. if (*phwnd)
  1591. {
  1592. TOOLINFO ti;
  1593. ti.cbSize = sizeof(ti);
  1594. ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
  1595. ti.hwnd = hDlg;
  1596. ti.uId = (UINT_PTR)hwnd;
  1597. ti.lpszText = (LPTSTR)pszText; // const -> non const
  1598. ti.hinst = HINST_THISDLL;
  1599. SendMessage(*phwnd, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1600. }
  1601. }
  1602. }
  1603. ReleaseDC(hDlg, hDC);
  1604. }
  1605. }
  1606. }
  1607. void UpdateTriStateCheckboxes(FILEPROPSHEETPAGE* pfpsp)
  1608. {
  1609. // we turn off tristate after applying attibs for those things that were tri-state
  1610. // initially but are not anymore since we sucessfully applied the attributes
  1611. if (pfpsp->hDlg)
  1612. {
  1613. if (pfpsp->asInitial.fReadOnly == BST_INDETERMINATE && pfpsp->asCurrent.fReadOnly != BST_INDETERMINATE)
  1614. {
  1615. SendDlgItemMessage(pfpsp->hDlg, IDD_READONLY, BM_SETSTYLE, BS_AUTOCHECKBOX, 0);
  1616. }
  1617. if (pfpsp->asInitial.fHidden == BST_INDETERMINATE && pfpsp->asCurrent.fHidden != BST_INDETERMINATE)
  1618. {
  1619. SendDlgItemMessage(pfpsp->hDlg, IDD_HIDDEN, BM_SETSTYLE, BS_AUTOCHECKBOX, 0);
  1620. }
  1621. // Archive is only on the general tab for files on FAT/FAT32 volumes
  1622. if (!pfpsp->pfci->fIsCompressionAvailable && pfpsp->asInitial.fArchive == BST_INDETERMINATE && pfpsp->asCurrent.fArchive != BST_INDETERMINATE)
  1623. {
  1624. SendDlgItemMessage(pfpsp->hDlg, IDD_ARCHIVE, BM_SETSTYLE, BS_AUTOCHECKBOX, 0);
  1625. }
  1626. }
  1627. }
  1628. //
  1629. // This applies the attributes to the selected files (multiple file case)
  1630. //
  1631. // return value:
  1632. // TRUE We sucessfully applied all the attributes
  1633. // FALSE The user hit cancel, and we stoped
  1634. //
  1635. STDAPI_(BOOL) ApplyMultipleFileAttributes(FILEPROPSHEETPAGE* pfpsp)
  1636. {
  1637. BOOL bRet = FALSE;
  1638. // create the progress dialog. This may fail if out of memory. If it does fail, we will
  1639. // abort the operation because it will also probably fail if out of memory.
  1640. if (CreateAttributeProgressDlg(pfpsp))
  1641. {
  1642. BOOL bSomethingChanged = FALSE;
  1643. bRet = TRUE;
  1644. // make sure that HIDA_FillFindDatat returns the compressed size, else our progress est will be way off
  1645. TCHAR szPath[MAX_PATH];
  1646. for (int iItem = 0; HIDA_FillFindData(pfpsp->pfci->hida, iItem, szPath, &pfpsp->fd, TRUE); iItem++)
  1647. {
  1648. if (HasUserCanceledAttributeProgressDlg(pfpsp))
  1649. {
  1650. // the user hit cancel on the progress dlg, so stop
  1651. bRet = FALSE;
  1652. break;
  1653. }
  1654. if (pfpsp->fRecursive && (pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1655. {
  1656. // apply attribs to the subfolders
  1657. bRet = ApplyRecursiveFolderAttribs(szPath, pfpsp);
  1658. // send out a notification for the whole dir, regardless if the user hit cancel since
  1659. // something could have changed
  1660. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, szPath, NULL);
  1661. }
  1662. else
  1663. {
  1664. HWND hwndParent = NULL;
  1665. // if we have a progress hwnd, try to use it as our parent. This will fail
  1666. // if the progress dialog isn't being displayed yet.
  1667. IUnknown_GetWindow((IUnknown*)pfpsp->pProgressDlg, &hwndParent);
  1668. if (!hwndParent)
  1669. {
  1670. // the progress dlg isint here yet, so use the property page hwnd
  1671. hwndParent = GetParent(pfpsp->hDlg);
  1672. }
  1673. // apply the attribs to this item only
  1674. bRet = ApplyFileAttributes(szPath, pfpsp, hwndParent, &bSomethingChanged);
  1675. if (bSomethingChanged)
  1676. {
  1677. // something changed, so send out a notification for that file
  1678. DeleteFileThumbnail(szPath);
  1679. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, szPath, NULL);
  1680. }
  1681. }
  1682. }
  1683. // destroy the progress dialog
  1684. DestroyAttributeProgressDlg(pfpsp);
  1685. if (bRet)
  1686. {
  1687. // since we just sucessfully applied attribs, reset any tri-state checkboxes as necessary
  1688. UpdateTriStateCheckboxes(pfpsp);
  1689. // the user did NOT hit cancel, so update the prop sheet to reflect the new attribs
  1690. pfpsp->asInitial = pfpsp->asCurrent;
  1691. }
  1692. // flush any change-notifications we generated
  1693. SHChangeNotifyHandleEvents();
  1694. }
  1695. return bRet;
  1696. }
  1697. STDAPI_(BOOL) ApplySingleFileAttributes(FILEPROPSHEETPAGE* pfpsp)
  1698. {
  1699. BOOL bRet = TRUE;
  1700. BOOL bSomethingChanged = FALSE;
  1701. if (!pfpsp->fRecursive)
  1702. {
  1703. bRet = ApplyFileAttributes(pfpsp->szPath, pfpsp, GetParent(pfpsp->hDlg), &bSomethingChanged);
  1704. if (bSomethingChanged)
  1705. {
  1706. // something changed, so generate a notification for the item
  1707. DeleteFileThumbnail(pfpsp->szPath);
  1708. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pfpsp->szPath, NULL);
  1709. }
  1710. }
  1711. else
  1712. {
  1713. // We only should be doing a recursive operation if we have a directory!
  1714. ASSERT(pfpsp->fIsDirectory);
  1715. // create the progress dialog. This may fail if out of memory. If it does fail, we will
  1716. // abort the operation because it will also probably fail if out of memory.
  1717. if (CreateAttributeProgressDlg(pfpsp))
  1718. {
  1719. // apply attribs to this folder & sub files/folders
  1720. bRet = ApplyRecursiveFolderAttribs(pfpsp->szPath, pfpsp);
  1721. // HACKHACK: send out a notification for the item so that defview will refresh properly
  1722. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pfpsp->szPath, NULL);
  1723. // send out a notification for the whole dir, regardless of the return value since
  1724. // something could have changed even if the user hit cancel
  1725. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, pfpsp->szPath, NULL);
  1726. DestroyAttributeProgressDlg(pfpsp);
  1727. }
  1728. else
  1729. {
  1730. bRet = FALSE;
  1731. }
  1732. }
  1733. if (bRet)
  1734. {
  1735. // since we just sucessfully applied attribs, reset any tri-state checkboxes as necessary
  1736. UpdateTriStateCheckboxes(pfpsp);
  1737. // the user did NOT hit cancel, so update the prop sheet to reflect the new attribs
  1738. pfpsp->asInitial = pfpsp->asCurrent;
  1739. // (reinerf) need to update the size fields (eg file was just compressed)
  1740. }
  1741. // handle any events we may have generated
  1742. SHChangeNotifyHandleEvents();
  1743. return bRet;
  1744. }
  1745. //
  1746. // this function sets the string that tells the user what attributes they are about to
  1747. // apply
  1748. //
  1749. BOOL SetAttributePromptText(HWND hDlgRecurse, FILEPROPSHEETPAGE* pfpsp)
  1750. {
  1751. TCHAR szAttribsToApply[MAX_PATH];
  1752. TCHAR szTemp[MAX_PATH];
  1753. szAttribsToApply[0] = 0;
  1754. if (pfpsp->asInitial.fReadOnly != pfpsp->asCurrent.fReadOnly)
  1755. {
  1756. if (pfpsp->asCurrent.fReadOnly)
  1757. EVAL(LoadString(HINST_THISDLL, IDS_READONLY, szTemp, ARRAYSIZE(szTemp)));
  1758. else
  1759. EVAL(LoadString(HINST_THISDLL, IDS_NOTREADONLY, szTemp, ARRAYSIZE(szTemp)));
  1760. StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
  1761. }
  1762. if (pfpsp->asInitial.fHidden != pfpsp->asCurrent.fHidden)
  1763. {
  1764. if (pfpsp->asCurrent.fHidden)
  1765. EVAL(LoadString(HINST_THISDLL, IDS_HIDE, szTemp, ARRAYSIZE(szTemp)));
  1766. else
  1767. EVAL(LoadString(HINST_THISDLL, IDS_UNHIDE, szTemp, ARRAYSIZE(szTemp)));
  1768. StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
  1769. }
  1770. if (pfpsp->asInitial.fArchive != pfpsp->asCurrent.fArchive)
  1771. {
  1772. if (pfpsp->asCurrent.fArchive)
  1773. EVAL(LoadString(HINST_THISDLL, IDS_ARCHIVE, szTemp, ARRAYSIZE(szTemp)));
  1774. else
  1775. EVAL(LoadString(HINST_THISDLL, IDS_UNARCHIVE, szTemp, ARRAYSIZE(szTemp)));
  1776. StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
  1777. }
  1778. if (pfpsp->asInitial.fIndex != pfpsp->asCurrent.fIndex)
  1779. {
  1780. if (pfpsp->asCurrent.fIndex)
  1781. EVAL(LoadString(HINST_THISDLL, IDS_INDEX, szTemp, ARRAYSIZE(szTemp)));
  1782. else
  1783. EVAL(LoadString(HINST_THISDLL, IDS_DISABLEINDEX, szTemp, ARRAYSIZE(szTemp)));
  1784. StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
  1785. }
  1786. if (pfpsp->asInitial.fCompress != pfpsp->asCurrent.fCompress)
  1787. {
  1788. if (pfpsp->asCurrent.fCompress)
  1789. EVAL(LoadString(HINST_THISDLL, IDS_COMPRESS, szTemp, ARRAYSIZE(szTemp)));
  1790. else
  1791. EVAL(LoadString(HINST_THISDLL, IDS_UNCOMPRESS, szTemp, ARRAYSIZE(szTemp)));
  1792. StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
  1793. }
  1794. if (pfpsp->asInitial.fEncrypt != pfpsp->asCurrent.fEncrypt)
  1795. {
  1796. if (pfpsp->asCurrent.fEncrypt)
  1797. EVAL(LoadString(HINST_THISDLL, IDS_ENCRYPT, szTemp, ARRAYSIZE(szTemp)));
  1798. else
  1799. EVAL(LoadString(HINST_THISDLL, IDS_DECRYPT, szTemp, ARRAYSIZE(szTemp)));
  1800. StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
  1801. }
  1802. if (!*szAttribsToApply)
  1803. {
  1804. // nothing changed bail
  1805. return FALSE;
  1806. }
  1807. // remove the trailing ", "
  1808. int iLength = lstrlen(szAttribsToApply);
  1809. ASSERT(iLength >= 3);
  1810. lstrcpy(&szAttribsToApply[iLength - 2], TEXT("\0"));
  1811. SetDlgItemText(hDlgRecurse, IDD_ATTRIBSTOAPPLY, szAttribsToApply);
  1812. return TRUE;
  1813. }
  1814. //
  1815. // This dlg proc is for the prompt to ask the user if they want to have their changes apply
  1816. // to only the directories, or all files/folders within the directories.
  1817. //
  1818. BOOL_PTR CALLBACK RecursivePromptDlgProc(HWND hDlgRecurse, UINT uMessage, WPARAM wParam, LPARAM lParam)
  1819. {
  1820. FILEPROPSHEETPAGE* pfpsp = (FILEPROPSHEETPAGE *)GetWindowLongPtr(hDlgRecurse, DWLP_USER);
  1821. switch (uMessage)
  1822. {
  1823. case WM_INITDIALOG:
  1824. {
  1825. SetWindowLongPtr(hDlgRecurse, DWLP_USER, lParam);
  1826. pfpsp = (FILEPROPSHEETPAGE *)lParam;
  1827. // set the initial state of the radio button
  1828. CheckDlgButton(hDlgRecurse, IDD_RECURSIVE, TRUE);
  1829. // set the IDD_ATTRIBSTOAPPLY based on what attribs we are applying
  1830. if (!SetAttributePromptText(hDlgRecurse, pfpsp))
  1831. {
  1832. // we should not get here because we check for the no attribs
  1833. // to apply earlier
  1834. ASSERT(FALSE);
  1835. EndDialog(hDlgRecurse, TRUE);
  1836. }
  1837. // load either "this folder" or "the selected items"
  1838. TCHAR szFolderText[MAX_PATH];
  1839. LoadString(HINST_THISDLL, pfpsp->pfci->fMultipleFiles ? IDS_THESELECTEDITEMS : IDS_THISFOLDER, szFolderText, ARRAYSIZE(szFolderText));
  1840. // set the IDD_RECURSIVE_TXT text to have "this folder" or "the selected items"
  1841. TCHAR szFormatString[MAX_PATH];
  1842. GetDlgItemText(hDlgRecurse, IDD_RECURSIVE_TXT, szFormatString, ARRAYSIZE(szFormatString));
  1843. TCHAR szDlgText[MAX_PATH];
  1844. wsprintf(szDlgText, szFormatString, szFolderText);
  1845. SetDlgItemText(hDlgRecurse, IDD_RECURSIVE_TXT, szDlgText);
  1846. // set the IDD_NOTRECURSIVE raido button text to have "this folder" or "the selected items"
  1847. GetDlgItemText(hDlgRecurse, IDD_NOTRECURSIVE, szFormatString, ARRAYSIZE(szFormatString));
  1848. wsprintf(szDlgText, szFormatString, szFolderText);
  1849. SetDlgItemText(hDlgRecurse, IDD_NOTRECURSIVE, szDlgText);
  1850. // set the IDD_RECURSIVE raido button text to have "this folder" or "the selected items"
  1851. GetDlgItemText(hDlgRecurse, IDD_RECURSIVE, szFormatString, ARRAYSIZE(szFormatString));
  1852. wsprintf(szDlgText, szFormatString, szFolderText);
  1853. SetDlgItemText(hDlgRecurse, IDD_RECURSIVE, szDlgText);
  1854. return TRUE;
  1855. }
  1856. case WM_COMMAND:
  1857. {
  1858. UINT uCtrlID = GET_WM_COMMAND_ID(wParam, lParam);
  1859. switch (uCtrlID)
  1860. {
  1861. case IDOK:
  1862. pfpsp->fRecursive = (IsDlgButtonChecked(hDlgRecurse, IDD_RECURSIVE) == BST_CHECKED);
  1863. // fall through
  1864. case IDCANCEL:
  1865. EndDialog(hDlgRecurse, (uCtrlID == IDCANCEL) ? FALSE : TRUE);
  1866. break;
  1867. }
  1868. }
  1869. default:
  1870. return FALSE;
  1871. }
  1872. }
  1873. //
  1874. // This wndproc handles the "Advanced Attributes..." button on the general tab for
  1875. //
  1876. // return - FALSE: the user hit cancle
  1877. // TRUE: the user hit ok
  1878. //
  1879. BOOL_PTR CALLBACK AdvancedFileAttribsDlgProc(HWND hDlgAttribs, UINT uMessage, WPARAM wParam, LPARAM lParam)
  1880. {
  1881. FILEPROPSHEETPAGE* pfpsp = (FILEPROPSHEETPAGE *)GetWindowLongPtr(hDlgAttribs, DWLP_USER);
  1882. switch (uMessage)
  1883. {
  1884. case WM_INITDIALOG:
  1885. {
  1886. SetWindowLongPtr(hDlgAttribs, DWLP_USER, lParam);
  1887. pfpsp = (FILEPROPSHEETPAGE *)lParam;
  1888. // set the initial state of the checkboxes
  1889. if (pfpsp->asInitial.fArchive == BST_INDETERMINATE)
  1890. {
  1891. SendDlgItemMessage(hDlgAttribs, IDD_ARCHIVE, BM_SETSTYLE, BS_AUTO3STATE, 0);
  1892. }
  1893. CheckDlgButton(hDlgAttribs, IDD_ARCHIVE, pfpsp->asCurrent.fArchive);
  1894. if (pfpsp->asInitial.fIndex == BST_INDETERMINATE)
  1895. {
  1896. SendDlgItemMessage(hDlgAttribs, IDD_INDEX, BM_SETSTYLE, BS_AUTO3STATE, 0);
  1897. }
  1898. CheckDlgButton(hDlgAttribs, IDD_INDEX, pfpsp->asCurrent.fIndex);
  1899. if (pfpsp->asInitial.fCompress == BST_INDETERMINATE)
  1900. {
  1901. SendDlgItemMessage(hDlgAttribs, IDD_COMPRESS, BM_SETSTYLE, BS_AUTO3STATE, 0);
  1902. }
  1903. CheckDlgButton(hDlgAttribs, IDD_COMPRESS, pfpsp->asCurrent.fCompress);
  1904. if (pfpsp->asInitial.fEncrypt == BST_INDETERMINATE)
  1905. {
  1906. SendDlgItemMessage(hDlgAttribs, IDD_ENCRYPT, BM_SETSTYLE, BS_AUTO3STATE, 0);
  1907. }
  1908. CheckDlgButton(hDlgAttribs, IDD_ENCRYPT, pfpsp->asCurrent.fEncrypt);
  1909. // assert that compression and encryption are mutually exclusive
  1910. ASSERT(!((pfpsp->asCurrent.fCompress == BST_CHECKED) && (pfpsp->asCurrent.fEncrypt == BST_CHECKED)));
  1911. // gray any checkboxs that are not supported by this filesystem
  1912. EnableWindow(GetDlgItem(hDlgAttribs, IDD_INDEX), pfpsp->fIsIndexAvailable);
  1913. EnableWindow(GetDlgItem(hDlgAttribs, IDD_COMPRESS), pfpsp->pfci->fIsCompressionAvailable);
  1914. EnableWindow(GetDlgItem(hDlgAttribs, IDD_ENCRYPT), pfpsp->fIsEncryptionAvailable);
  1915. if (pfpsp->fIsEncryptionAvailable &&
  1916. pfpsp->asInitial.fEncrypt &&
  1917. !pfpsp->fIsDirectory &&
  1918. !pfpsp->pfci->fMultipleFiles)
  1919. {
  1920. // we only support the Advanced button for the single folder case
  1921. EnableWindow(GetDlgItem(hDlgAttribs, IDC_ADVANCED), TRUE);
  1922. }
  1923. else
  1924. {
  1925. EnableWindow(GetDlgItem(hDlgAttribs, IDC_ADVANCED), FALSE);
  1926. }
  1927. // load either "this folder" or "the selected items"
  1928. TCHAR szFolderText[MAX_PATH];
  1929. LoadString(HINST_THISDLL, pfpsp->pfci->fMultipleFiles ? IDS_THESELECTEDITEMS : IDS_THISFOLDER, szFolderText, ARRAYSIZE(szFolderText));
  1930. // set the IDC_MANAGEFILES_TXT text to have "this folder" or "the selected items"
  1931. TCHAR szFormatString[MAX_PATH];
  1932. GetDlgItemText(hDlgAttribs, IDC_MANAGEFILES_TXT, szFormatString, ARRAYSIZE(szFormatString));
  1933. TCHAR szDlgText[MAX_PATH];
  1934. wsprintf(szDlgText, szFormatString, szFolderText);
  1935. SetDlgItemText(hDlgAttribs, IDC_MANAGEFILES_TXT, szDlgText);
  1936. return TRUE;
  1937. }
  1938. case WM_HELP:
  1939. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aAdvancedHelpIds);
  1940. break;
  1941. case WM_CONTEXTMENU:
  1942. if ((int)SendMessage(hDlgAttribs, WM_NCHITTEST, 0, lParam) != HTCLIENT)
  1943. {
  1944. // not in our client area, so don't process it
  1945. return FALSE;
  1946. }
  1947. WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aAdvancedHelpIds);
  1948. break;
  1949. case WM_COMMAND:
  1950. {
  1951. UINT uCtrlID = GET_WM_COMMAND_ID(wParam, lParam);
  1952. switch (uCtrlID)
  1953. {
  1954. case IDD_COMPRESS:
  1955. // encrypt and compress are mutually exclusive
  1956. if (IsDlgButtonChecked(hDlgAttribs, IDD_COMPRESS) == BST_CHECKED)
  1957. {
  1958. // the user checked compress so uncheck the encrypt checkbox
  1959. CheckDlgButton(hDlgAttribs, IDD_ENCRYPT, BST_UNCHECKED);
  1960. }
  1961. break;
  1962. case IDD_ENCRYPT:
  1963. // encrypt and compress are mutually exclusive
  1964. if (IsDlgButtonChecked(hDlgAttribs, IDD_ENCRYPT) == BST_CHECKED)
  1965. {
  1966. // the user checked encrypt, so uncheck the compression checkbox
  1967. CheckDlgButton(hDlgAttribs, IDD_COMPRESS, BST_UNCHECKED);
  1968. if (!pfpsp->pfci->fMultipleFiles && pfpsp->asInitial.fEncrypt)
  1969. {
  1970. EnableWindow(GetDlgItem(hDlgAttribs, IDC_ADVANCED), TRUE);
  1971. }
  1972. }
  1973. else
  1974. {
  1975. EnableWindow(GetDlgItem(hDlgAttribs, IDC_ADVANCED), FALSE);
  1976. }
  1977. break;
  1978. case IDC_ADVANCED:
  1979. ASSERT(pfpsp->fIsEncryptionAvailable && pfpsp->asInitial.fEncrypt && !pfpsp->pfci->fMultipleFiles);
  1980. // bring up the EfsDetail dialog
  1981. EfsDetail(hDlgAttribs, pfpsp->szPath);
  1982. break;
  1983. case IDOK:
  1984. pfpsp->asCurrent.fArchive = IsDlgButtonChecked(hDlgAttribs, IDD_ARCHIVE);
  1985. if (pfpsp->asCurrent.fArchive == BST_INDETERMINATE)
  1986. {
  1987. // if its indeterminate, it better had been indeterminate to start with
  1988. ASSERT(pfpsp->asInitial.fArchive == BST_INDETERMINATE);
  1989. }
  1990. pfpsp->asCurrent.fIndex = IsDlgButtonChecked(hDlgAttribs, IDD_INDEX);
  1991. if (pfpsp->asCurrent.fIndex == BST_INDETERMINATE)
  1992. {
  1993. // if its indeterminate, it better had been indeterminate to start with
  1994. ASSERT(pfpsp->asInitial.fIndex == BST_INDETERMINATE);
  1995. }
  1996. pfpsp->asCurrent.fCompress = IsDlgButtonChecked(hDlgAttribs, IDD_COMPRESS);
  1997. if (pfpsp->asCurrent.fCompress == BST_INDETERMINATE)
  1998. {
  1999. // if its indeterminate, it better had been indeterminate to start with
  2000. ASSERT(pfpsp->asInitial.fCompress == BST_INDETERMINATE);
  2001. }
  2002. pfpsp->asCurrent.fEncrypt = IsDlgButtonChecked(hDlgAttribs, IDD_ENCRYPT);
  2003. if (pfpsp->asCurrent.fEncrypt == BST_INDETERMINATE)
  2004. {
  2005. // if its indeterminate, it better had been indeterminate to start with
  2006. ASSERT(pfpsp->asInitial.fEncrypt == BST_INDETERMINATE);
  2007. }
  2008. // fall through...
  2009. case IDCANCEL:
  2010. ReplaceDlgIcon(hDlgAttribs, IDD_ITEMICON, NULL);
  2011. EndDialog(hDlgAttribs, (uCtrlID == IDCANCEL) ? FALSE : TRUE);
  2012. break;
  2013. }
  2014. }
  2015. default:
  2016. return FALSE;
  2017. }
  2018. return TRUE;
  2019. }
  2020. //
  2021. // Descriptions:
  2022. // This is the dialog procedure for multiple object property sheet.
  2023. //
  2024. BOOL_PTR CALLBACK MultiplePrshtDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
  2025. {
  2026. FILEPROPSHEETPAGE * pfpsp = (FILEPROPSHEETPAGE *)GetWindowLongPtr(hDlg, DWLP_USER);
  2027. switch (uMessage)
  2028. {
  2029. case WM_INITDIALOG:
  2030. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  2031. pfpsp = (FILEPROPSHEETPAGE *)lParam;
  2032. pfpsp->hDlg = hDlg;
  2033. pfpsp->pfci->hDlg = hDlg;
  2034. InitMultiplePrsht(pfpsp);
  2035. break;
  2036. case WM_HELP:
  2037. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(void *)aMultiPropHelpIds);
  2038. break;
  2039. case WM_CONTEXTMENU:
  2040. if ((int)SendMessage(hDlg, WM_NCHITTEST, 0, lParam) != HTCLIENT)
  2041. {
  2042. // not in our client area, so don't process it
  2043. return FALSE;
  2044. }
  2045. WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aMultiPropHelpIds);
  2046. break;
  2047. case WM_TIMER:
  2048. UpdateSizeCount(pfpsp);
  2049. break;
  2050. case WM_COMMAND:
  2051. switch (GET_WM_COMMAND_ID(wParam, lParam))
  2052. {
  2053. case IDD_READONLY:
  2054. case IDD_HIDDEN:
  2055. case IDD_ARCHIVE:
  2056. break;
  2057. case IDC_ADVANCED:
  2058. // the dialog box returns fase if the user hit cancel, and true if they hit ok,
  2059. // so if they cancelled, return immediately and don't send the PSM_CHANGED message
  2060. // because nothing actually changed
  2061. if (!DialogBoxParam(HINST_THISDLL,
  2062. MAKEINTRESOURCE(pfpsp->fIsDirectory ? DLG_FOLDERATTRIBS : DLG_FILEATTRIBS),
  2063. hDlg,
  2064. AdvancedFileAttribsDlgProc,
  2065. (LPARAM)pfpsp))
  2066. {
  2067. // the user has cancled
  2068. return TRUE;
  2069. }
  2070. break;
  2071. default:
  2072. return TRUE;
  2073. }
  2074. // check to see if we need to enable the Apply button
  2075. if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
  2076. {
  2077. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
  2078. }
  2079. break;
  2080. case WM_DESTROY:
  2081. Free_DlgDependentFilePropSheetPage(pfpsp);
  2082. break;
  2083. case WM_NOTIFY:
  2084. switch (((NMHDR *)lParam)->code)
  2085. {
  2086. case PSN_APPLY:
  2087. {
  2088. //
  2089. // Get the final state of the checkboxes
  2090. //
  2091. pfpsp->asCurrent.fReadOnly = IsDlgButtonChecked(hDlg, IDD_READONLY);
  2092. if (pfpsp->asCurrent.fReadOnly == BST_INDETERMINATE)
  2093. {
  2094. // if its indeterminate, it better had been indeterminate to start with
  2095. ASSERT(pfpsp->asInitial.fReadOnly == BST_INDETERMINATE);
  2096. }
  2097. pfpsp->asCurrent.fHidden = IsDlgButtonChecked(hDlg, IDD_HIDDEN);
  2098. if (pfpsp->asCurrent.fHidden == BST_INDETERMINATE)
  2099. {
  2100. // if its indeterminate, it better had been indeterminate to start with
  2101. ASSERT(pfpsp->asInitial.fHidden == BST_INDETERMINATE);
  2102. }
  2103. if (!pfpsp->pfci->fIsCompressionAvailable)
  2104. {
  2105. // at least one of the files is on FAT, so the Archive checkbox is on the general page
  2106. pfpsp->asCurrent.fArchive = IsDlgButtonChecked(hDlg, IDD_ARCHIVE);
  2107. if (pfpsp->asCurrent.fArchive == BST_INDETERMINATE)
  2108. {
  2109. // if its indeterminate, it better had been indeterminate to start with
  2110. ASSERT(pfpsp->asInitial.fArchive == BST_INDETERMINATE);
  2111. }
  2112. }
  2113. BOOL bRet = TRUE;
  2114. // check to see if the user actually changed something, if they didnt, then
  2115. // we dont have to apply anything
  2116. if (memcmp(&pfpsp->asInitial, &pfpsp->asCurrent, sizeof(pfpsp->asInitial)) != 0)
  2117. {
  2118. // NOTE: We dont check to see if all the dirs are empty, that would be too expensive.
  2119. // We only do that in the single file case.
  2120. if (pfpsp->fIsDirectory)
  2121. {
  2122. // check to see if the user wants to apply the attribs recursively or not
  2123. bRet = (int)DialogBoxParam(HINST_THISDLL,
  2124. MAKEINTRESOURCE(DLG_ATTRIBS_RECURSIVE),
  2125. hDlg,
  2126. RecursivePromptDlgProc,
  2127. (LPARAM)pfpsp);
  2128. }
  2129. if (bRet)
  2130. bRet = ApplyMultipleFileAttributes(pfpsp);
  2131. if (!bRet)
  2132. {
  2133. // the user hit cancel, so we return true to prevent the property sheet form closeing
  2134. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  2135. return TRUE;
  2136. }
  2137. else
  2138. {
  2139. // update the size / last accessed time
  2140. UpdateSizeField(pfpsp, NULL);
  2141. }
  2142. }
  2143. break;
  2144. }
  2145. // fall through
  2146. default:
  2147. return FALSE;
  2148. }
  2149. break;
  2150. default:
  2151. return FALSE;
  2152. }
  2153. return TRUE;
  2154. }
  2155. // in:
  2156. // hdlg
  2157. // id text control id
  2158. // pftUTC UTC time time to be set
  2159. //
  2160. STDAPI_(void) SetDateTimeText(HWND hdlg, int id, const FILETIME *pftUTC)
  2161. {
  2162. SetDateTimeTextEx(hdlg, id, pftUTC, FDTF_LONGDATE | FDTF_LONGTIME | FDTF_RELATIVE) ;
  2163. }
  2164. STDAPI_(void) SetDateTimeTextEx(HWND hdlg, int id, const FILETIME *pftUTC, DWORD dwFlags)
  2165. {
  2166. if (!IsNullTime(pftUTC))
  2167. {
  2168. LCID locale = GetUserDefaultLCID();
  2169. if ((PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_ARABIC) ||
  2170. (PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_HEBREW))
  2171. {
  2172. HWND hWnd = GetDlgItem(hdlg, id);
  2173. DWORD dwExStyle = GetWindowLong(hdlg, GWL_EXSTYLE);
  2174. if ((BOOLIFY(dwExStyle & WS_EX_RTLREADING)) != (BOOLIFY(dwExStyle & RTL_MIRRORED_WINDOW)))
  2175. dwFlags |= FDTF_RTLDATE;
  2176. else
  2177. dwFlags |= FDTF_LTRDATE;
  2178. }
  2179. TCHAR szBuf[64];
  2180. SHFormatDateTime(pftUTC, &dwFlags, szBuf, SIZECHARS(szBuf));
  2181. SetDlgItemText(hdlg, id, szBuf);
  2182. }
  2183. }
  2184. //
  2185. // Descriptions:
  2186. // Detects BiDi Calender
  2187. //
  2188. BOOL _IsBiDiCalendar(void)
  2189. {
  2190. TCHAR chCalendar[32];
  2191. BOOL fBiDiCalender = FALSE;
  2192. //
  2193. // Let's verify the calendar type whether it's gregorian or not.
  2194. if (GetLocaleInfo(LOCALE_USER_DEFAULT,
  2195. LOCALE_ICALENDARTYPE,
  2196. (TCHAR *) &chCalendar[0],
  2197. ARRAYSIZE(chCalendar)))
  2198. {
  2199. CALTYPE defCalendar = StrToInt((TCHAR *)&chCalendar[0]);
  2200. LCID locale = GetUserDefaultLCID();
  2201. if ((defCalendar == CAL_HIJRI) ||
  2202. (defCalendar == CAL_HEBREW) ||
  2203. ((defCalendar == CAL_GREGORIAN) &&
  2204. ((PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_ARABIC) ||
  2205. (PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_HEBREW))) ||
  2206. (defCalendar == CAL_GREGORIAN_ARABIC) ||
  2207. (defCalendar == CAL_GREGORIAN_XLIT_ENGLISH) ||
  2208. (defCalendar == CAL_GREGORIAN_XLIT_FRENCH))
  2209. {
  2210. fBiDiCalender = TRUE;
  2211. }
  2212. }
  2213. return fBiDiCalender;
  2214. }
  2215. // Set the friendly display name into control uId.
  2216. BOOL SetPidlToWindow(HWND hwnd, UINT uId, LPITEMIDLIST pidl)
  2217. {
  2218. BOOL fRes = FALSE;
  2219. LPCITEMIDLIST pidlItem;
  2220. IShellFolder* psf;
  2221. if (SUCCEEDED(SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlItem)))
  2222. {
  2223. TCHAR szPath[MAX_PATH];
  2224. // SHGDN_FORADDRESSBAR | SHGDN_FORPARSING because we want:
  2225. // c:\winnt\.... and http://weird, but not ::{GUID} or Folder.{GUID}
  2226. if (SUCCEEDED(DisplayNameOf(psf, pidlItem, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath))))
  2227. {
  2228. SetDlgItemText(hwnd, IDD_TARGET, szPath);
  2229. fRes = TRUE;
  2230. }
  2231. psf->Release();
  2232. }
  2233. return fRes;
  2234. }
  2235. //
  2236. // Descriptions:
  2237. // This function fills fields of the "general" dialog box (a page of
  2238. // a property sheet) with attributes of the associated file.
  2239. //
  2240. BOOL InitSingleFilePrsht(FILEPROPSHEETPAGE * pfpsp)
  2241. {
  2242. SHFILEINFO sfi;
  2243. // get info about the file.
  2244. SHGetFileInfo(pfpsp->szPath, pfpsp->fd.dwFileAttributes, &sfi, sizeof(sfi),
  2245. SHGFI_ICON|SHGFI_LARGEICON|
  2246. SHGFI_DISPLAYNAME|
  2247. SHGFI_TYPENAME | SHGFI_ADDOVERLAYS);
  2248. // .ani cursor hack!
  2249. if (lstrcmpi(PathFindExtension(pfpsp->szPath), TEXT(".ani")) == 0)
  2250. {
  2251. HICON hIcon = (HICON)LoadImage(NULL, pfpsp->szPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
  2252. if (hIcon)
  2253. {
  2254. if (sfi.hIcon)
  2255. DestroyIcon(sfi.hIcon);
  2256. sfi.hIcon = hIcon;
  2257. }
  2258. }
  2259. // icon
  2260. ReplaceDlgIcon(pfpsp->hDlg, IDD_ITEMICON, sfi.hIcon);
  2261. // set the initial rename state
  2262. pfpsp->fRename = FALSE;
  2263. // set the file type
  2264. if (pfpsp->fMountedDrive)
  2265. {
  2266. //Borrow szVolumeGUID
  2267. TCHAR szVolumeGUID[MAX_PATH];
  2268. LoadString(HINST_THISDLL, IDS_MOUNTEDVOLUME, szVolumeGUID, ARRAYSIZE(szVolumeGUID));
  2269. SetDlgItemText(pfpsp->hDlg, IDD_FILETYPE, szVolumeGUID);
  2270. //use szVolumeLabel temporarily
  2271. TCHAR szVolumeLabel[MAX_PATH];
  2272. lstrcpyn(szVolumeLabel, pfpsp->szPath, ARRAYSIZE(szVolumeLabel));
  2273. PathAddBackslash(szVolumeLabel);
  2274. GetVolumeNameForVolumeMountPoint(szVolumeLabel, szVolumeGUID, ARRAYSIZE(szVolumeGUID));
  2275. if (!GetVolumeInformation(szVolumeGUID, szVolumeLabel, ARRAYSIZE(szVolumeLabel),
  2276. NULL, NULL, NULL, pfpsp->szFileSys, ARRAYSIZE(pfpsp->szFileSys)))
  2277. {
  2278. EnableWindow(GetDlgItem(pfpsp->hDlg, IDC_DRV_PROPERTIES), FALSE);
  2279. *szVolumeLabel = 0;
  2280. }
  2281. if (!(*szVolumeLabel))
  2282. LoadString(HINST_THISDLL, IDS_UNLABELEDVOLUME, szVolumeLabel, ARRAYSIZE(szVolumeLabel));
  2283. SetDlgItemText(pfpsp->hDlg, IDC_DRV_TARGET, szVolumeLabel);
  2284. }
  2285. else
  2286. {
  2287. SetDlgItemText(pfpsp->hDlg, IDD_FILETYPE, sfi.szTypeName);
  2288. }
  2289. // save off the initial short filename, and set the "Name" edit box
  2290. lstrcpyn(pfpsp->szInitialName, sfi.szDisplayName, ARRAYSIZE(pfpsp->szInitialName));
  2291. SetDlgItemText(pfpsp->hDlg, IDD_NAMEEDIT, sfi.szDisplayName);
  2292. // use a strcmp to see if we are showing the extension
  2293. if (lstrcmpi(sfi.szDisplayName, PathFindFileName(pfpsp->szPath)) == 0)
  2294. {
  2295. // since the strings are the same, we must be showing the extension
  2296. pfpsp->fShowExtension = TRUE;
  2297. }
  2298. TCHAR szBuffer[MAX_PATH];
  2299. lstrcpyn(szBuffer, pfpsp->szPath, ARRAYSIZE(szBuffer));
  2300. PathRemoveFileSpec(szBuffer);
  2301. UINT cchMax;
  2302. GetCCHMaxFromPath(pfpsp->szPath, &cchMax, pfpsp->fShowExtension);
  2303. Edit_LimitText(GetDlgItem(pfpsp->hDlg, IDD_NAMEEDIT), cchMax);
  2304. // apply the limit input code for the item
  2305. pfpsp->pidl = ILCreateFromPath(pfpsp->szPath);
  2306. if (pfpsp->pidl)
  2307. {
  2308. IShellFolder *psf;
  2309. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pfpsp->pidl, &psf))))
  2310. {
  2311. SHLimitInputEdit(GetDlgItem(pfpsp->hDlg, IDD_NAMEEDIT), psf);
  2312. psf->Release();
  2313. }
  2314. }
  2315. // Are we a folder shortcut?
  2316. if (pfpsp->fFolderShortcut)
  2317. {
  2318. // Yes; Then we need to populate folder shortcut specific controls.
  2319. if (pfpsp->pidl)
  2320. {
  2321. IShellLink *psl;
  2322. if (SUCCEEDED(SHGetUIObjectFromFullPIDL(pfpsp->pidl, NULL, IID_PPV_ARG(IShellLink, &psl))))
  2323. {
  2324. // Populate the Target
  2325. if (SUCCEEDED(psl->GetIDList(&pfpsp->pidlTarget)))
  2326. {
  2327. if (SetPidlToWindow(pfpsp->hDlg, IDD_TARGET, pfpsp->pidlTarget))
  2328. {
  2329. pfpsp->fValidateEdit = FALSE; // Set this to false because we already have a pidl
  2330. // and don't need to validate.
  2331. }
  2332. }
  2333. // And description
  2334. TCHAR sz[INFOTIPSIZE];
  2335. if (SUCCEEDED(psl->GetDescription(sz, ARRAYSIZE(sz))))
  2336. {
  2337. SetDlgItemText(pfpsp->hDlg, IDD_COMMENT, sz);
  2338. }
  2339. psl->Release();
  2340. }
  2341. }
  2342. SetDateTimeText(pfpsp->hDlg, IDD_CREATED, &pfpsp->fd.ftCreationTime);
  2343. }
  2344. else
  2345. {
  2346. // set the initial attributes
  2347. SetInitialFileAttribs(pfpsp, pfpsp->fd.dwFileAttributes, pfpsp->fd.dwFileAttributes);
  2348. // special case for folders, we don't apply the read only bit to folders
  2349. // and to indicate that in the UI we make the inital state of the check
  2350. // box tri-state. this allows the read only bit to be applied to files in
  2351. // this folder, but not the folders themselves.
  2352. if (pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  2353. {
  2354. pfpsp->asInitial.fReadOnly = BST_INDETERMINATE;
  2355. SendDlgItemMessage(pfpsp->hDlg, IDD_READONLY, BM_SETSTYLE, BS_AUTO3STATE, 0);
  2356. }
  2357. // set the current attributes to the same as the initial
  2358. pfpsp->asCurrent = pfpsp->asInitial;
  2359. CheckDlgButton(pfpsp->hDlg, IDD_READONLY, pfpsp->asInitial.fReadOnly);
  2360. CheckDlgButton(pfpsp->hDlg, IDD_HIDDEN, pfpsp->asInitial.fHidden);
  2361. // Disable renaming the file if requested
  2362. if (pfpsp->fDisableRename)
  2363. {
  2364. EnableWindow(GetDlgItem(pfpsp->hDlg, IDD_NAMEEDIT), FALSE);
  2365. }
  2366. if (pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
  2367. {
  2368. // to avoid people making SYSTEM files HIDDEN (superhidden files are
  2369. // not show to the user by default) we don't let people make SYSTEM files HIDDEN
  2370. EnableWindow(GetDlgItem(pfpsp->hDlg, IDD_HIDDEN), FALSE);
  2371. }
  2372. // Archive is only on the general tab for FAT, otherwise it is under the "Advanced attributes"
  2373. // and FAT volumes dont have the "Advanced attributes" button.
  2374. if (pfpsp->pfci->fIsCompressionAvailable || pfpsp->fIsEncryptionAvailable)
  2375. {
  2376. // if compression/encryption is available, then we must be on NTFS
  2377. DestroyWindow(GetDlgItem(pfpsp->hDlg, IDD_ARCHIVE));
  2378. }
  2379. else
  2380. {
  2381. // we are on FAT/FAT32, so get rid of the "Advanced attributes" button, and set the inital Archive state
  2382. DestroyWindow(GetDlgItem(pfpsp->hDlg, IDC_ADVANCED));
  2383. CheckDlgButton(pfpsp->hDlg, IDD_ARCHIVE, pfpsp->asInitial.fArchive);
  2384. }
  2385. UpdateSizeField(pfpsp, &pfpsp->fd);
  2386. if (!(pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  2387. {
  2388. // Check to see if the target file is a lnk, because if it is a lnk then
  2389. // we need to display the type information for the target, not the lnk itself.
  2390. if (PathIsShortcut(pfpsp->szPath, pfpsp->fd.dwFileAttributes))
  2391. {
  2392. pfpsp->fIsLink = TRUE;
  2393. }
  2394. if (!(GetFileAttributes(pfpsp->szPath) & FILE_ATTRIBUTE_OFFLINE))
  2395. {
  2396. UpdateOpensWithInfo(pfpsp);
  2397. }
  2398. else
  2399. {
  2400. EnableWindow(GetDlgItem(pfpsp->hDlg, IDC_FT_PROP_CHANGEOPENSWITH), FALSE);
  2401. }
  2402. }
  2403. // get the full path to the folder that contains this file.
  2404. lstrcpyn(szBuffer, pfpsp->szPath, ARRAYSIZE(szBuffer));
  2405. PathRemoveFileSpec(szBuffer);
  2406. // Keep Functionality same as NT4 by avoiding PathCompactPath.
  2407. SetDlgItemTextWithToolTip(pfpsp->hDlg, IDD_LOCATION, szBuffer, &pfpsp->hwndTip);
  2408. }
  2409. return TRUE;
  2410. }
  2411. STDAPI_(BOOL) ShowMountedVolumeProperties(LPCTSTR pszMountedVolume, HWND hwndParent)
  2412. {
  2413. IMountedVolume* pMountedVolume;
  2414. HRESULT hr = SHCoCreateInstance(NULL, &CLSID_MountedVolume, NULL, IID_PPV_ARG(IMountedVolume, &pMountedVolume));
  2415. if (SUCCEEDED(hr))
  2416. {
  2417. TCHAR szPathSlash[MAX_PATH];
  2418. lstrcpyn(szPathSlash, pszMountedVolume, ARRAYSIZE(szPathSlash));
  2419. PathAddBackslash(szPathSlash);
  2420. hr = pMountedVolume->Initialize(szPathSlash);
  2421. if (SUCCEEDED(hr))
  2422. {
  2423. IDataObject* pDataObj;
  2424. hr = pMountedVolume->QueryInterface(IID_PPV_ARG(IDataObject, &pDataObj));
  2425. if (SUCCEEDED(hr))
  2426. {
  2427. PROPSTUFF *pps = (PROPSTUFF *)LocalAlloc(LPTR, sizeof(*pps));
  2428. if (pps)
  2429. {
  2430. pps->lpStartAddress = DrivesPropertiesThreadProc;
  2431. pps->pdtobj = pDataObj;
  2432. EnableWindow(hwndParent, FALSE);
  2433. DrivesPropertiesThreadProc(pps);
  2434. EnableWindow(hwndParent, TRUE);
  2435. LocalFree(pps);
  2436. }
  2437. pDataObj->Release();
  2438. }
  2439. pMountedVolume->Release();
  2440. }
  2441. }
  2442. return SUCCEEDED(hr);
  2443. }
  2444. #ifdef FOLDERSHORTCUT_EDITABLETARGET
  2445. BOOL SetFolderShortcutInfo(HWND hDlg, FILEPROPSHEETPAGE* pfpsp)
  2446. {
  2447. ASSERT(pfpsp->pidl);
  2448. BOOL fSuccess = FALSE;
  2449. IShellLink* psl;
  2450. if (SUCCEEDED(SHGetUIObjectFromFullPIDL(pfpsp->pidl, NULL, IID_PPV_ARG(IShellLink, &psl))))
  2451. {
  2452. TCHAR sz[INFOTIPSIZE];
  2453. Edit_GetText(GetDlgItem(pfpsp->hDlg, IDD_COMMENT), sz, ARRAYSIZE(sz));
  2454. psl->SetDescription(sz);
  2455. if (pfpsp->fValidateEdit)
  2456. {
  2457. IShellFolder* psf;
  2458. if (SUCCEEDED(SHGetDesktopFolder(&psf)))
  2459. {
  2460. TCHAR szPath[MAX_PATH];
  2461. Edit_GetText(GetDlgItem(pfpsp->hDlg, IDD_TARGET), sz, ARRAYSIZE(sz));
  2462. if (PathCanonicalize(szPath, sz))
  2463. {
  2464. LPITEMIDLIST pidlDest;
  2465. DWORD dwAttrib = SFGAO_FOLDER | SFGAO_VALIDATE;
  2466. ULONG chEat = 0;
  2467. if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, szPath, &chEat, &pidlDest, &dwAttrib)))
  2468. {
  2469. if ((dwAttrib & SFGAO_FOLDER) == SFGAO_FOLDER)
  2470. {
  2471. ILFree(pfpsp->pidlTarget);
  2472. pfpsp->pidlTarget = pidlDest;
  2473. fSuccess = TRUE;
  2474. }
  2475. else
  2476. {
  2477. ILFree(pidlDest);
  2478. }
  2479. }
  2480. }
  2481. psf->Release();
  2482. }
  2483. }
  2484. else
  2485. {
  2486. fSuccess = TRUE;
  2487. }
  2488. if (fSuccess)
  2489. {
  2490. psl->SetIDList(pfpsp->pidlTarget);
  2491. IPersistFile* ppf;
  2492. if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))))
  2493. {
  2494. fSuccess = (S_OK == ppf->Save(pfpsp->szPath, TRUE));
  2495. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pfpsp->szPath, NULL);
  2496. ppf->Release();
  2497. }
  2498. }
  2499. psl->Release();
  2500. }
  2501. return fSuccess;
  2502. }
  2503. #endif
  2504. //
  2505. // Descriptions:
  2506. // This is the dialog procedure for the "general" page of a property sheet.
  2507. //
  2508. BOOL_PTR CALLBACK SingleFilePrshtDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
  2509. {
  2510. FILEPROPSHEETPAGE* pfpsp = (FILEPROPSHEETPAGE *)GetWindowLongPtr(hDlg, DWLP_USER);
  2511. switch (uMessage)
  2512. {
  2513. case WM_INITDIALOG:
  2514. // REVIEW, we should store more state info here, for example
  2515. // the hIcon being displayed and the FILEINFO pointer, not just
  2516. // the file name ptr
  2517. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  2518. pfpsp = (FILEPROPSHEETPAGE *)lParam;
  2519. pfpsp->hDlg = hDlg;
  2520. pfpsp->pfci->hDlg = hDlg;
  2521. InitSingleFilePrsht(pfpsp);
  2522. // We set this to signal that we are done processing the WM_INITDIALOG.
  2523. // This is needed because we set the text of the "Name" edit box and unless
  2524. // he knows that this is being set for the first time, he thinks that someone is doing a rename.
  2525. pfpsp->fWMInitFinshed = TRUE;
  2526. break;
  2527. case WM_TIMER:
  2528. if (!pfpsp->fMountedDrive)
  2529. UpdateSizeCount(pfpsp);
  2530. break;
  2531. case WM_HELP:
  2532. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(void *)(pfpsp->fIsDirectory ? aFolderGeneralHelpIds : aFileGeneralHelpIds));
  2533. break;
  2534. case WM_CONTEXTMENU:
  2535. if ((int)SendMessage(hDlg, WM_NCHITTEST, 0, lParam) != HTCLIENT)
  2536. {
  2537. // not in our client area, so don't process it
  2538. return FALSE;
  2539. }
  2540. WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)(pfpsp->fIsDirectory ? aFolderGeneralHelpIds : aFileGeneralHelpIds));
  2541. break;
  2542. case WM_COMMAND:
  2543. switch (GET_WM_COMMAND_ID(wParam, lParam))
  2544. {
  2545. case IDD_READONLY:
  2546. case IDD_HIDDEN:
  2547. case IDD_ARCHIVE:
  2548. break;
  2549. #ifdef FOLDERSHORTCUT_EDITABLETARGET
  2550. case IDD_TARGET:
  2551. if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
  2552. {
  2553. // someone typed in the target, enable apply button
  2554. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
  2555. // Do a verification on apply.
  2556. pfpsp->fValidateEdit = TRUE;
  2557. }
  2558. break;
  2559. case IDD_COMMENT:
  2560. if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
  2561. {
  2562. // Set the apply.
  2563. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
  2564. }
  2565. break;
  2566. #endif
  2567. case IDD_NAMEEDIT:
  2568. // we need to check the pfpsp->fWMInitFinshed to make sure that we are done processing the WM_INITDIALOG,
  2569. // because during init we set the initial IDD_NAMEEDIT text which generates a EN_CHANGE message.
  2570. if ((GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) && !pfpsp->fRename && pfpsp->fWMInitFinshed)
  2571. {
  2572. pfpsp->fRename = TRUE;
  2573. //
  2574. // disable "Apply" even though the edit field has changed (reinerf)
  2575. //
  2576. // We only allow "ok" or "cancel" after the name has changed to be sure we
  2577. // don't rename the file out from under other property sheet extensions that
  2578. // cache the original name away
  2579. PropSheet_DisableApply(GetParent(pfpsp->hDlg));
  2580. }
  2581. break;
  2582. case IDC_CHANGEFILETYPE:
  2583. {
  2584. // Bring up the "Open With" dialog
  2585. OPENASINFO oai;
  2586. if (pfpsp->fIsLink && pfpsp->szLinkTarget[0])
  2587. {
  2588. // if we have a link we want to re-associate the link target, NOT .lnk files!
  2589. oai.pcszFile = pfpsp->szLinkTarget;
  2590. }
  2591. else
  2592. {
  2593. #ifdef DEBUG
  2594. LPTSTR pszExt = PathFindExtension(pfpsp->szPath);
  2595. // reality check...
  2596. ASSERT((lstrcmpi(pszExt, TEXT(".exe")) != 0) &&
  2597. (lstrcmpi(pszExt, TEXT(".lnk")) != 0));
  2598. #endif // DEBUG
  2599. oai.pcszFile = pfpsp->szPath;
  2600. }
  2601. oai.pcszClass = NULL;
  2602. oai.dwInFlags = (OAIF_REGISTER_EXT | OAIF_FORCE_REGISTRATION); // we want the association to be made
  2603. if (SUCCEEDED(OpenAsDialog(GetParent(hDlg), &oai)))
  2604. {
  2605. // we changed the association so update the "Opens with:" text. Clear out szLinkTarget to force
  2606. // the update to happen
  2607. pfpsp->szLinkTarget[0] = 0;
  2608. UpdateOpensWithInfo(pfpsp);
  2609. }
  2610. }
  2611. break;
  2612. case IDC_ADVANCED:
  2613. // the dialog box returns fase if the user hit cancel, and true if they hit ok,
  2614. // so if they cancelled, return immediately and don't send the PSM_CHANGED message
  2615. // because nothing actually changed
  2616. if (!DialogBoxParam(HINST_THISDLL,
  2617. MAKEINTRESOURCE(pfpsp->fIsDirectory ? DLG_FOLDERATTRIBS : DLG_FILEATTRIBS),
  2618. hDlg,
  2619. AdvancedFileAttribsDlgProc,
  2620. (LPARAM)pfpsp))
  2621. {
  2622. // the user has canceled
  2623. return TRUE;
  2624. }
  2625. break;
  2626. case IDC_DRV_PROPERTIES:
  2627. ASSERT(pfpsp->fMountedDrive);
  2628. ShowMountedVolumeProperties(pfpsp->szPath, hDlg);
  2629. break;
  2630. #ifdef FOLDERSHORTCUT_EDITABLETARGET
  2631. case IDD_BROWSE:
  2632. {
  2633. // Display the BrowseForFolder dialog.
  2634. // FEATURE(lamadio): Implement a filter to filter things we can create folder
  2635. // shortcuts to. Not enough time for this rev 6.5.99
  2636. TCHAR szTitle[MAX_PATH];
  2637. LoadString(HINST_THISDLL, IDS_BROWSEFORFS, szTitle, ARRAYSIZE(szTitle));
  2638. TCHAR szAltPath[MAX_PATH];
  2639. BROWSEINFO bi = {0};
  2640. bi.hwndOwner = hDlg;
  2641. bi.pidlRoot = NULL;
  2642. bi.pszDisplayName = szAltPath;
  2643. bi.lpszTitle = szTitle;
  2644. bi.ulFlags = BIF_USENEWUI | BIF_EDITBOX;
  2645. LPITEMIDLIST pidlFull = SHBrowseForFolder(&bi);
  2646. if (pidlFull)
  2647. {
  2648. ILFree(pfpsp->pidlTarget);
  2649. pfpsp->pidlTarget = pidlFull;
  2650. if (SetPidlToWindow(hDlg, IDD_TARGET, pfpsp->pidlTarget))
  2651. {
  2652. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
  2653. pfpsp->fValidateEdit = FALSE;
  2654. }
  2655. }
  2656. }
  2657. break;
  2658. #endif
  2659. default:
  2660. return TRUE;
  2661. }
  2662. // check to see if we need to enable the Apply button
  2663. if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
  2664. {
  2665. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
  2666. }
  2667. break;
  2668. case WM_DESTROY:
  2669. Free_DlgDependentFilePropSheetPage(pfpsp);
  2670. break;
  2671. case WM_NOTIFY:
  2672. switch (((NMHDR *)lParam)->code)
  2673. {
  2674. case PSN_APPLY:
  2675. // check to see if we could apply the name change. Note that this
  2676. // does not actually apply the change until PSN_LASTCHANCEAPPLY
  2677. pfpsp->fCanRename = TRUE;
  2678. if (pfpsp->fRename && !ApplyRename(pfpsp, FALSE))
  2679. {
  2680. // can't change the name so don't let the dialog close
  2681. pfpsp->fCanRename = FALSE;
  2682. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  2683. return TRUE;
  2684. }
  2685. if (pfpsp->fFolderShortcut)
  2686. {
  2687. #ifdef FOLDERSHORTCUT_EDITABLETARGET
  2688. if (!SetFolderShortcutInfo(hDlg, pfpsp))
  2689. {
  2690. // Display that we could not create because blah, blah, blah
  2691. ShellMessageBox(HINST_THISDLL,
  2692. hDlg,
  2693. MAKEINTRESOURCE(IDS_FOLDERSHORTCUT_ERR),
  2694. MAKEINTRESOURCE(IDS_FOLDERSHORTCUT_ERR_TITLE),
  2695. MB_OK | MB_ICONSTOP);
  2696. // Reset the Folder info.
  2697. SetPidlToWindow(hDlg, IDD_TARGET, pfpsp->pidlTarget);
  2698. // Don't close the dialog.
  2699. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  2700. return TRUE;
  2701. }
  2702. #endif
  2703. }
  2704. else
  2705. {
  2706. UINT uReadonlyState = IsDlgButtonChecked(hDlg, IDD_READONLY);
  2707. switch (uReadonlyState)
  2708. {
  2709. case BST_CHECKED:
  2710. pfpsp->asCurrent.fReadOnly = TRUE;
  2711. break;
  2712. case BST_UNCHECKED:
  2713. pfpsp->asCurrent.fReadOnly = FALSE;
  2714. break;
  2715. case BST_INDETERMINATE:
  2716. // read-only checkbox is initaially set to BST_INDETERMINATE for folders
  2717. ASSERT(pfpsp->fIsDirectory);
  2718. ASSERT(pfpsp->asInitial.fReadOnly == BST_INDETERMINATE);
  2719. pfpsp->asCurrent.fReadOnly = BST_INDETERMINATE;
  2720. break;
  2721. }
  2722. pfpsp->asCurrent.fHidden = (IsDlgButtonChecked(hDlg, IDD_HIDDEN) == BST_CHECKED);
  2723. // Archive is on the general page for FAT volumes
  2724. if (!pfpsp->pfci->fIsCompressionAvailable)
  2725. {
  2726. pfpsp->asCurrent.fArchive = (IsDlgButtonChecked(hDlg, IDD_ARCHIVE) == BST_CHECKED);
  2727. }
  2728. // check to see if the user actually changed something, if they didnt, then
  2729. // we dont have to apply anything
  2730. if (memcmp(&pfpsp->asInitial, &pfpsp->asCurrent, sizeof(pfpsp->asInitial)) != 0)
  2731. {
  2732. BOOL bRet = TRUE;
  2733. // Check to see if the user wants to apply the attribs recursively or not. If the
  2734. // directory is empty, dont bother to ask, since there is nothing to recurse into
  2735. if (pfpsp->fIsDirectory && !PathIsDirectoryEmpty(pfpsp->szPath))
  2736. {
  2737. bRet = (int)DialogBoxParam(HINST_THISDLL,
  2738. MAKEINTRESOURCE(DLG_ATTRIBS_RECURSIVE),
  2739. hDlg,
  2740. RecursivePromptDlgProc,
  2741. (LPARAM)pfpsp);
  2742. }
  2743. if (bRet)
  2744. {
  2745. bRet = ApplySingleFileAttributes(pfpsp);
  2746. }
  2747. if (!bRet)
  2748. {
  2749. // the user hit cancel, so we return true to prevent the property sheet from closing
  2750. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  2751. return TRUE;
  2752. }
  2753. else
  2754. {
  2755. // update the size / last accessed time
  2756. UpdateSizeField(pfpsp, NULL);
  2757. }
  2758. }
  2759. }
  2760. break;
  2761. case PSN_SETACTIVE:
  2762. if (pfpsp->fIsLink)
  2763. {
  2764. // If this is a link, each time we get set active we need to check to see
  2765. // if the user applied changes on the link tab that would affect the
  2766. // "Opens With:" info.
  2767. UpdateOpensWithInfo(pfpsp);
  2768. }
  2769. break;
  2770. case PSN_QUERYINITIALFOCUS:
  2771. // Special hack: We do not want initial focus on the "Rename" or "Change" controls, since
  2772. // if the user hit something by accident they would start renaming/modifying the assoc. So
  2773. // we set the focus to the "Read-only" control since it is present on all dialogs that use
  2774. // this wndproc (file, folder, and mounted drive)
  2775. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hDlg, IDD_READONLY));
  2776. return TRUE;
  2777. case PSN_LASTCHANCEAPPLY:
  2778. //
  2779. // HACKHACK (reinerf)
  2780. //
  2781. // I hacked PSN_LASTCHANCEAPPLY into the prsht code so we can get a notification after
  2782. // every other app has applied, then we can go and do the rename.
  2783. //
  2784. // strangely, PSN_LASTCHANCEAPPLY is called even if PSN_APPY returns TRUE.
  2785. //
  2786. // we can now safely rename the file, since all the other tabs have
  2787. // applied their stuff.
  2788. if (pfpsp->fRename && pfpsp->fCanRename)
  2789. {
  2790. // dont bother to check the return value since this is the last-chance,
  2791. // so the dialog is ending shortly after this
  2792. ApplyRename(pfpsp, TRUE);
  2793. }
  2794. break;
  2795. default:
  2796. return FALSE;
  2797. }
  2798. break;
  2799. default:
  2800. return FALSE;
  2801. }
  2802. return TRUE;
  2803. }
  2804. //
  2805. // This function consists of code that does some
  2806. // Initialization for the file property sheet dialog.
  2807. STDAPI InitCommonPrsht(FILEPROPSHEETPAGE * pfpsp)
  2808. {
  2809. pfpsp->psp.dwSize = sizeof(FILEPROPSHEETPAGE); // extra data
  2810. pfpsp->psp.dwFlags = PSP_USECALLBACK;
  2811. pfpsp->psp.hInstance = HINST_THISDLL;
  2812. pfpsp->psp.pfnCallback = NULL; //FilePrshtCallback;
  2813. pfpsp->pfci->bContinue = TRUE;
  2814. // Do basic init for file system props
  2815. if (HIDA_GetCount(pfpsp->pfci->hida) == 1) // single file?
  2816. {
  2817. // get most of the data we will need (the date/time stuff is not filled in)
  2818. if (HIDA_FillFindData(pfpsp->pfci->hida, 0, pfpsp->szPath, &(pfpsp->pfci->fd), FALSE))
  2819. {
  2820. pfpsp->fd = pfpsp->pfci->fd;
  2821. LPITEMIDLIST pidl = HIDA_ILClone(pfpsp->pfci->hida, 0);
  2822. if (pidl)
  2823. {
  2824. // disable renaming here.
  2825. DWORD dwAttrs = SFGAO_CANRENAME;
  2826. if (SUCCEEDED(SHGetNameAndFlags(pidl, 0, NULL, 0, &dwAttrs)) && !(dwAttrs & SFGAO_CANRENAME))
  2827. {
  2828. pfpsp->fDisableRename = TRUE;
  2829. }
  2830. ILFree(pidl);
  2831. }
  2832. if (pfpsp->pfci->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  2833. {
  2834. pfpsp->fIsDirectory = TRUE;
  2835. // check for HostFolder folder mounting a volume)
  2836. //GetVolumeNameFromMountPoint Succeeds then the give path is a mount point
  2837. //Make sure the path ends with a backslash. otherwise the following api wont work
  2838. TCHAR szPathSlash[MAX_PATH];
  2839. lstrcpyn(szPathSlash, pfpsp->szPath, ARRAYSIZE(szPathSlash));
  2840. PathAddBackslash(szPathSlash);
  2841. // Is this a mounted volume at this folder?
  2842. // this fct will return FALSE if not on NT5 and higher
  2843. TCHAR szVolumeName[MAX_PATH];
  2844. if (GetVolumeNameForVolumeMountPoint(szPathSlash, szVolumeName, ARRAYSIZE(szVolumeName)))
  2845. {
  2846. // Yes; show the Mounted Drive Propertysheet instead of normal
  2847. // folder property sheet
  2848. // fpsp.fMountedDrive also means NT5 or higher, because this fct will fail otherwise
  2849. pfpsp->fMountedDrive = TRUE;
  2850. }
  2851. // check to see if it's a folder shortcut
  2852. if (!(pfpsp->fMountedDrive))
  2853. {
  2854. // Folder and a shortcut? Must be a folder shortcut!
  2855. if (PathIsShortcut(pfpsp->szPath, pfpsp->pfci->fd.dwFileAttributes))
  2856. {
  2857. pfpsp->fFolderShortcut = TRUE;
  2858. }
  2859. }
  2860. }
  2861. {
  2862. DWORD dwVolumeFlags = GetVolumeFlags(pfpsp->szPath,
  2863. pfpsp->szFileSys,
  2864. ARRAYSIZE(pfpsp->szFileSys));
  2865. // test for file-based compression.
  2866. if (dwVolumeFlags & FS_FILE_COMPRESSION)
  2867. {
  2868. // filesystem supports compression
  2869. pfpsp->pfci->fIsCompressionAvailable = TRUE;
  2870. }
  2871. // test for file-based encryption.
  2872. if ((dwVolumeFlags & FS_FILE_ENCRYPTION) && !SHRestricted(REST_NOENCRYPTION))
  2873. {
  2874. // filesystem supports encryption
  2875. pfpsp->fIsEncryptionAvailable = TRUE;
  2876. }
  2877. //
  2878. // HACKHACK (reinerf) - we dont have a FS_SUPPORTS_INDEXING so we
  2879. // use the FILE_SUPPORTS_SPARSE_FILES flag, because native index support
  2880. // appeared first on NTFS5 volumes, at the same time sparse file support
  2881. // was implemented.
  2882. //
  2883. if (dwVolumeFlags & FILE_SUPPORTS_SPARSE_FILES)
  2884. {
  2885. // yup, we are on NTFS5 or greater
  2886. pfpsp->fIsIndexAvailable = TRUE;
  2887. }
  2888. // check to see if we have a .exe and we need to prompt for user logon
  2889. pfpsp->fIsExe = PathIsBinaryExe(pfpsp->szPath);
  2890. }
  2891. }
  2892. }
  2893. else
  2894. {
  2895. // we have multiple files
  2896. pfpsp->pfci->fMultipleFiles = TRUE;
  2897. }
  2898. return S_OK;
  2899. }
  2900. //
  2901. // Descriptions:
  2902. // This function creates a property sheet object for the "general" page
  2903. // which shows file system attributes.
  2904. //
  2905. // Arguments:
  2906. // hDrop -- specifies the file(s)
  2907. // pfnAddPage -- Specifies the callback function.
  2908. // lParam -- Specifies the lParam to be passed to the callback.
  2909. //
  2910. // Returns:
  2911. // TRUE if it added any pages
  2912. //
  2913. // History:
  2914. // 12-31-92 SatoNa Created
  2915. //
  2916. STDAPI FileSystem_AddPages(IDataObject *pdtobj, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
  2917. {
  2918. FILEPROPSHEETPAGE fpsp = {0};
  2919. HRESULT hr = S_OK;
  2920. fpsp.pfci = Create_FolderContentsInfo();
  2921. if (fpsp.pfci)
  2922. {
  2923. hr = DataObj_CopyHIDA(pdtobj, &fpsp.pfci->hida);
  2924. if (SUCCEEDED(hr))
  2925. {
  2926. hr = InitCommonPrsht(&fpsp);
  2927. if (SUCCEEDED(hr))
  2928. {
  2929. fpsp.psp.pfnCallback = FilePrshtCallback;
  2930. UINT uRes;
  2931. if (!fpsp.pfci->fMultipleFiles)
  2932. {
  2933. fpsp.psp.pfnDlgProc = SingleFilePrshtDlgProc;
  2934. if (fpsp.fIsDirectory)
  2935. {
  2936. if (fpsp.fMountedDrive)
  2937. {
  2938. uRes = DLG_MOUNTEDDRV_GENERAL;
  2939. }
  2940. else if (fpsp.fFolderShortcut)
  2941. {
  2942. uRes = DLG_FOLDERSHORTCUTPROP;
  2943. }
  2944. else
  2945. {
  2946. uRes = DLG_FOLDERPROP;
  2947. }
  2948. }
  2949. else
  2950. {
  2951. //Files
  2952. uRes = DLG_FILEPROP;
  2953. }
  2954. }
  2955. else
  2956. {
  2957. // Multiple Files / Folders.
  2958. fpsp.psp.pfnDlgProc = MultiplePrshtDlgProc;
  2959. uRes = DLG_FILEMULTPROP;
  2960. }
  2961. fpsp.psp.pszTemplate = MAKEINTRESOURCE(uRes);
  2962. }
  2963. }
  2964. if (SUCCEEDED(hr))
  2965. {
  2966. HPROPSHEETPAGE hpage = CreatePropertySheetPage(&fpsp.psp);
  2967. if (hpage)
  2968. {
  2969. if (pfnAddPage(hpage, lParam))
  2970. {
  2971. hr = S_OK;
  2972. if (!fpsp.pfci->fMultipleFiles)
  2973. {
  2974. if (AddLinkPage(fpsp.szPath, pfnAddPage, lParam))
  2975. {
  2976. // set second page default!
  2977. hr = ResultFromShort(2);
  2978. }
  2979. AddVersionPage(fpsp.szPath, pfnAddPage, lParam);
  2980. }
  2981. }
  2982. else
  2983. {
  2984. DestroyPropertySheetPage(hpage);
  2985. }
  2986. }
  2987. }
  2988. }
  2989. else
  2990. {
  2991. hr = E_OUTOFMEMORY;
  2992. }
  2993. if (FAILED(hr))
  2994. {
  2995. Free_DlgIndepFilePropSheetPage(&fpsp);
  2996. }
  2997. return hr;
  2998. }