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

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