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.

511 lines
17 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "prshtcpp.h"
  4. #include "propsht.h"
  5. #include "treewkcb.h" // for CBaseTreeWalkerCB class
  6. #include "ftascstr.h" // for CFTAssocStore
  7. #include "ftcmmn.h" // for MAX_APPFRIENDLYNAME
  8. #include "ascstr.h" // for IAssocInfo class
  9. //
  10. // Folder attribute tree waker class
  11. //
  12. class CFolderAttribTreeWalker : public CBaseTreeWalkerCB
  13. {
  14. public:
  15. // constructor
  16. CFolderAttribTreeWalker(FILEPROPSHEETPAGE* pfpsp);
  17. // IShellTreeWalkerCallBack
  18. STDMETHODIMP FoundFile(LPCWSTR pwszPath, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd);
  19. STDMETHODIMP EnterFolder(LPCWSTR pwszPath, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd);
  20. protected:
  21. FILEPROPSHEETPAGE *_pfpsp;
  22. };
  23. CFolderAttribTreeWalker::CFolderAttribTreeWalker(FILEPROPSHEETPAGE* pfpsp): _pfpsp(pfpsp)
  24. {
  25. }
  26. HRESULT CFolderAttribTreeWalker::FoundFile(LPCWSTR pwszFile, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW* pwfd)
  27. {
  28. HWND hwndParent = NULL;
  29. TCHAR szFileName[MAX_PATH];
  30. ULARGE_INTEGER ulSizeOnDisk;
  31. BOOL bSomethingChanged;
  32. // check to see if the user hit cancel on the progress dlg
  33. if (HasUserCanceledAttributeProgressDlg(_pfpsp))
  34. {
  35. return E_FAIL;
  36. }
  37. if (_pfpsp->pProgressDlg)
  38. {
  39. // if we have a progress hwnd, try to use it
  40. // this will fail if the progress dialog hasent been displayed yet.
  41. IUnknown_GetWindow(_pfpsp->pProgressDlg, &hwndParent);
  42. }
  43. if (!hwndParent)
  44. {
  45. // if we dont have a progress hwnd, use the property page hwnd
  46. hwndParent = GetParent(_pfpsp->hDlg);
  47. }
  48. // thunk the pwszFile string
  49. SHUnicodeToTChar(pwszFile, szFileName, ARRAYSIZE(szFileName));
  50. if (pwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  51. {
  52. // (reinerf) - in the directory case, we set the size to zero since the
  53. // FolderSize() function dosen't add in the sizes of directories, and we need
  54. // the sizes to match when doing progress calcuations.
  55. _pfpsp->fd.nFileSizeLow = 0;
  56. _pfpsp->fd.nFileSizeHigh = 0;
  57. }
  58. else
  59. {
  60. // if compression is supported, we check to see if the file is sparse or compressed
  61. if (_pfpsp->pfci->fIsCompressionAvailable && (pwfd->dwFileAttributes & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE)))
  62. {
  63. ulSizeOnDisk.LowPart = SHGetCompressedFileSize(szFileName, &ulSizeOnDisk.HighPart);
  64. }
  65. else
  66. {
  67. // the file isint comrpessed so just round to the cluster size
  68. ulSizeOnDisk.LowPart = pwfd->nFileSizeLow;
  69. ulSizeOnDisk.HighPart = pwfd->nFileSizeHigh;
  70. ulSizeOnDisk.QuadPart = ROUND_TO_CLUSTER(ulSizeOnDisk.QuadPart, ptws->dwClusterSize);
  71. }
  72. // we set this so the progress dialog knows how much to update the progress slider
  73. _pfpsp->fd.nFileSizeLow = ulSizeOnDisk.LowPart;
  74. _pfpsp->fd.nFileSizeHigh = ulSizeOnDisk.HighPart;
  75. }
  76. if (!ApplyFileAttributes(szFileName, _pfpsp, hwndParent, &bSomethingChanged))
  77. {
  78. // the user hit cancel, so stop
  79. return E_FAIL;
  80. }
  81. return S_OK;
  82. }
  83. HRESULT CFolderAttribTreeWalker::EnterFolder(LPCWSTR pwszDir, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW* pwfd)
  84. {
  85. return FoundFile(pwszDir, ptws, pwfd);
  86. }
  87. STDAPI_(BOOL) ApplyRecursiveFolderAttribs(LPCTSTR pszDir, FILEPROPSHEETPAGE* pfpsp)
  88. {
  89. HRESULT hrInit = SHCoInitialize();
  90. HRESULT hr = E_FAIL;
  91. CFolderAttribTreeWalker* pfatw = new CFolderAttribTreeWalker(pfpsp);
  92. if (pfatw)
  93. {
  94. IShellTreeWalker *pstw;
  95. hr = CoCreateInstance(CLSID_CShellTreeWalker, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellTreeWalker, &pstw));
  96. if (SUCCEEDED(hr))
  97. {
  98. hr = pstw->WalkTree(WT_NOTIFYFOLDERENTER, pszDir, NULL, 0, SAFECAST(pfatw, IShellTreeWalkerCallBack*));
  99. pstw->Release();
  100. }
  101. pfatw->Release();
  102. }
  103. SHCoUninitialize(hrInit);
  104. return SUCCEEDED(hr) ? TRUE : FALSE;
  105. }
  106. //
  107. // Checks the progress dialog to see if the user has hit cancel
  108. //
  109. STDAPI_(BOOL) HasUserCanceledAttributeProgressDlg(FILEPROPSHEETPAGE* pfpsp)
  110. {
  111. BOOL fReturn = FALSE;
  112. if (pfpsp && pfpsp->pProgressDlg)
  113. {
  114. fReturn = pfpsp->pProgressDlg->HasUserCancelled();
  115. }
  116. return fReturn;
  117. }
  118. //
  119. // Creates the CProgressDialog object used by the attribs dlg
  120. //
  121. STDAPI_(BOOL) CreateAttributeProgressDlg(FILEPROPSHEETPAGE* pfpsp)
  122. {
  123. WCHAR wzBuffer[MAX_PATH];
  124. ASSERT(pfpsp->pfci->fMultipleFiles || pfpsp->fRecursive);
  125. // create the progress dialog as a modal window
  126. pfpsp->pProgressDlg = CProgressDialog_CreateInstance(IDS_APPLYINGATTRIBS, IDA_APPLYATTRIBS, HINST_THISDLL);
  127. if (!pfpsp->pProgressDlg)
  128. {
  129. // couldn't create a progress dialog, so bail
  130. return FALSE;
  131. }
  132. // set the static string "Applying Attrbiutes to:"
  133. LoadStringW(HINST_THISDLL, IDS_APPLYINGATTRIBSTO, wzBuffer, ARRAYSIZE(wzBuffer));
  134. pfpsp->pProgressDlg->SetLine(1, wzBuffer, FALSE, NULL);
  135. pfpsp->pProgressDlg->StartProgressDialog(GetParent(pfpsp->hDlg), NULL, (PROGDLG_MODAL | PROGDLG_AUTOTIME), NULL);
  136. return TRUE;
  137. }
  138. //
  139. // Delets the CProgressDialog object used by the attribs dlg
  140. //
  141. STDAPI_(BOOL) DestroyAttributeProgressDlg(FILEPROPSHEETPAGE* pfpsp)
  142. {
  143. if (!pfpsp->pProgressDlg)
  144. {
  145. ASSERT(FALSE);
  146. return FALSE;
  147. }
  148. pfpsp->pProgressDlg->StopProgressDialog();
  149. pfpsp->pProgressDlg->Release();
  150. pfpsp->pProgressDlg = NULL;
  151. // reset NumberOfBytesDone so we are back to zero if the user tries something else,
  152. // we will start over at zero
  153. pfpsp->ulNumberOfBytesDone.QuadPart = 0;
  154. pfpsp->cItemsDone = 0;
  155. return TRUE;
  156. }
  157. //
  158. // Sets the current file we are applying attribs for in the progress dlg
  159. //
  160. STDAPI SetProgressDlgPath(FILEPROPSHEETPAGE* pfpsp, LPCTSTR pszPath, BOOL fCompactPath)
  161. {
  162. HRESULT hr = E_INVALIDARG;
  163. ASSERT(pfpsp->pProgressDlg);
  164. if (pfpsp && pfpsp->pProgressDlg)
  165. {
  166. WCHAR wzPath[MAX_PATH];
  167. SHTCharToUnicode(pszPath, wzPath, ARRAYSIZE(wzPath));
  168. hr = pfpsp->pProgressDlg->SetLine(2, wzPath, fCompactPath, NULL);
  169. }
  170. return hr;
  171. }
  172. //
  173. // Updates the progress bar in the dlg
  174. //
  175. STDAPI UpdateProgressBar(FILEPROPSHEETPAGE* pfpsp)
  176. {
  177. HRESULT hr = E_INVALIDARG;
  178. ASSERT(pfpsp->pProgressDlg);
  179. if (pfpsp && pfpsp->pProgressDlg)
  180. {
  181. pfpsp->cItemsDone++;
  182. // if we are not changing compression or encryption, then
  183. // do progress based on the number of items we are applying to.
  184. if (pfpsp->asCurrent.fCompress == pfpsp->asInitial.fCompress &&
  185. pfpsp->asCurrent.fEncrypt == pfpsp->asInitial.fEncrypt)
  186. {
  187. if (pfpsp->fRecursive)
  188. {
  189. //progress is based on number of items done out of total items in all folders
  190. hr = pfpsp->pProgressDlg->SetProgress(pfpsp->cItemsDone, pfpsp->pfci->cFiles + pfpsp->pfci->cFolders);
  191. }
  192. else
  193. {
  194. //progress is based on number of items done out of total selected items
  195. hr = pfpsp->pProgressDlg->SetProgress(pfpsp->cItemsDone, HIDA_GetCount(pfpsp->pfci->hida));
  196. }
  197. }
  198. else
  199. {
  200. // since we are either encrypting or compressing, we do progress based on the sizes of the files
  201. hr = pfpsp->pProgressDlg->SetProgress64(pfpsp->ulNumberOfBytesDone.QuadPart, pfpsp->pfci->ulTotalNumberOfBytes.QuadPart);
  202. }
  203. }
  204. return hr;
  205. }
  206. // we dynamically size the text depeneding on whether or not the small icon is visible and
  207. // if the "Change..." button is visible
  208. void SizeOpensWithTextBox(FILEPROPSHEETPAGE* pfpsp, BOOL bIsSmallIconVisible, BOOL bIsOpensWithEnabled)
  209. {
  210. RECT rcArray[3]; // array of three rects: the IDD_TYPEICON rect, the IDC_CHANGEFILETYPE rect, and the IDD_OPENSWITH rect
  211. RECT* prcSmallIcon = &rcArray[0];
  212. RECT* prcChangeButton = &rcArray[1];
  213. RECT* prcText = &rcArray[2];
  214. BOOL bFailed = FALSE;
  215. GetWindowRect(GetDlgItem(pfpsp->hDlg, IDD_TYPEICON), &rcArray[0]);
  216. GetWindowRect(GetDlgItem(pfpsp->hDlg, IDC_CHANGEFILETYPE), &rcArray[1]);
  217. GetWindowRect(GetDlgItem(pfpsp->hDlg, IDD_OPENSWITH), &rcArray[2]);
  218. // map the rects into dlg coordiates
  219. // MapWindowPoints is mirroring aware only when you pass one rect. let's loop.
  220. for (int i =0; i < ARRAYSIZE(rcArray); i++ )
  221. {
  222. if(!(MapWindowPoints(NULL, pfpsp->hDlg, (LPPOINT)(&rcArray[i]), 2)))
  223. {
  224. bFailed = TRUE;
  225. break;
  226. }
  227. }
  228. if (!bFailed)
  229. {
  230. RECT rcTemp = {0,0,4,0}; // we need to find out how many pixels are in 4 DLU's worth of witdth
  231. MapDialogRect(pfpsp->hDlg, &rcTemp);
  232. if (bIsSmallIconVisible)
  233. {
  234. prcText->left = prcSmallIcon->right + rcTemp.right; // spacing between controls is 4 DLU's
  235. }
  236. else
  237. {
  238. prcText->left = prcSmallIcon->left;
  239. }
  240. if (bIsOpensWithEnabled)
  241. {
  242. prcText->right = prcChangeButton->left - rcTemp.right; // spacing between controls is 4 DLU's
  243. }
  244. else
  245. {
  246. prcText->right = prcChangeButton->right;
  247. }
  248. SetWindowPos(GetDlgItem(pfpsp->hDlg, IDD_OPENSWITH),
  249. HWND_BOTTOM,
  250. prcText->left,
  251. prcText->top,
  252. (prcText->right - prcText->left),
  253. (prcText->bottom - prcText->top),
  254. SWP_NOZORDER);
  255. }
  256. }
  257. // this function sets the "Opens With:" / "Description:" text based on whether or not
  258. // we are allowed to change the assocation, and enables / disables the "Opens With..." button
  259. void SetDescriptionAndOpensWithBtn(FILEPROPSHEETPAGE* pfpsp, BOOL fAllowModifyOpenWith)
  260. {
  261. TCHAR szOpensWithText[MAX_PATH];
  262. LoadString(HINST_THISDLL, fAllowModifyOpenWith ? IDS_OPENSWITH : IDS_DESCRIPTION, szOpensWithText, ARRAYSIZE(szOpensWithText));
  263. SetDlgItemText(pfpsp->hDlg, IDD_OPENSWITH_TXT, szOpensWithText);
  264. // enable/disable the "Change..." button accordingly
  265. EnableAndShowWindow(GetDlgItem(pfpsp->hDlg, IDC_CHANGEFILETYPE), fAllowModifyOpenWith);
  266. }
  267. // set the Friendly Name text (eg the text to the right of the "Opens With:" / "Description:" field
  268. void SetFriendlyNameText(LPTSTR pszPath, FILEPROPSHEETPAGE* pfpsp, IAssocInfo* pai, BOOL bIsSmallIconVisible)
  269. {
  270. TCHAR szAppFriendlyName[MAX_PATH];
  271. DWORD cchFriendlyName = ARRAYSIZE(szAppFriendlyName);
  272. szAppFriendlyName[0] = TEXT('\0');
  273. if (pai)
  274. {
  275. if (FAILED(pai->GetString(AISTR_APPFRIENDLY, szAppFriendlyName, &cchFriendlyName)))
  276. {
  277. // if we failed, it could mean that this app is not associated yet. in this
  278. // case we just use "Unknown Applicaion"
  279. LoadString(HINST_THISDLL, IDS_UNKNOWNAPPLICATION, szAppFriendlyName, ARRAYSIZE(szAppFriendlyName));
  280. }
  281. }
  282. else
  283. {
  284. UINT cchBuff = (UINT)cchFriendlyName;
  285. // get the friendly name from the file itself
  286. if (!pszPath || !pszPath[0] || !GetFileDescription(pszPath, szAppFriendlyName, &cchBuff))
  287. {
  288. // use the short name as it appears in the "rename" edit box if something above didnt work
  289. // (ok to truncate since this is for display purpose only)
  290. StringCchCopy(szAppFriendlyName, ARRAYSIZE(szAppFriendlyName), pfpsp->szInitialName);
  291. }
  292. }
  293. ASSERT(szAppFriendlyName[0]);
  294. SetDlgItemTextWithToolTip(pfpsp->hDlg, IDD_OPENSWITH, szAppFriendlyName, &pfpsp->hwndTip);
  295. // size and position the text properly depending on wether the small icon is visible and
  296. // the state of the "Change..." button
  297. SizeOpensWithTextBox(pfpsp, bIsSmallIconVisible, IsWindowEnabled(GetDlgItem(pfpsp->hDlg, IDC_CHANGEFILETYPE)));
  298. }
  299. // sets the small icon in the description field
  300. //
  301. // return value: TRUE - a small icon is visible
  302. // FALSE - small icon was not set
  303. //
  304. BOOL SetSmallIcon(FILEPROPSHEETPAGE* pfpsp, IAssocInfo* pai, BOOL fAllowModifyOpenWith)
  305. {
  306. HICON hIcon = NULL;
  307. HICON hIconOld = NULL;
  308. int iIcon;
  309. BOOL bShowSmallIcon;
  310. // only setup the small icon of the associated app if we have the "Change..." button
  311. // and we were able to get the friendly name.
  312. if (fAllowModifyOpenWith && pai && SUCCEEDED(pai->GetDWORD(AIDWORD_APPSMALLICON, (DWORD*)&iIcon)))
  313. {
  314. HIMAGELIST hIL = NULL;
  315. Shell_GetImageLists(NULL, &hIL);
  316. if (hIL)
  317. {
  318. hIcon = ImageList_ExtractIcon(g_hinst, hIL, iIcon);
  319. }
  320. }
  321. // we will show the small icon if we got one and if we are allowed to modify the opens with
  322. bShowSmallIcon = (hIcon != NULL);
  323. hIconOld = (HICON)SendDlgItemMessage(pfpsp->hDlg, IDD_TYPEICON, STM_SETICON, (WPARAM)hIcon, 0);
  324. if (hIconOld)
  325. DestroyIcon(hIconOld);
  326. // enable/disable the IDD_TYPEICON icon accordingly
  327. EnableAndShowWindow(GetDlgItem(pfpsp->hDlg, IDD_TYPEICON), bShowSmallIcon);
  328. return bShowSmallIcon;
  329. }
  330. //
  331. // We use this to set the text for the associated application and other goodies
  332. //
  333. STDAPI UpdateOpensWithInfo(FILEPROPSHEETPAGE* pfpsp)
  334. {
  335. HRESULT hr;
  336. TCHAR szPath[MAX_PATH];
  337. IAssocStore* pas = NULL;
  338. IAssocInfo* pai = NULL;
  339. BOOL fAllowChangeAssoc = TRUE;
  340. BOOL fAllowModifyOpenWith = TRUE;
  341. BOOL fIsLink = FALSE;
  342. BOOL bShowSmallIcon;
  343. szPath[0] = TEXT('\0');
  344. // We need to check to see if this is a link. If so, then we need to get the information for
  345. // the link target
  346. if (pfpsp->fIsLink)
  347. {
  348. if (S_OK != GetPathFromLinkFile(pfpsp->szPath, szPath, ARRAYSIZE(szPath)))
  349. {
  350. // we failed for some strange reason, perhaps its a darwin link,
  351. // we just treat the file as if it were not a link. And we do not let
  352. // the user change the association
  353. fAllowModifyOpenWith = FALSE;
  354. pfpsp->fIsLink = FALSE;
  355. }
  356. else
  357. {
  358. // if the link target didn't change, we dont need to update anything
  359. if (pfpsp->szLinkTarget[0] && lstrcmpi(pfpsp->szLinkTarget, szPath) == 0)
  360. {
  361. return S_FALSE;
  362. }
  363. }
  364. }
  365. else
  366. {
  367. // just use the path of the file since it is not a link. pass null-on-failure so we don't display
  368. // information for the wrong file type if the path is > MAX_PATH.
  369. StringCchCopyEx(szPath, ARRAYSIZE(szPath), pfpsp->szPath, NULL, NULL, STRSAFE_NULL_ON_FAILURE);
  370. }
  371. // if we haven't initialized the AssocStore, do so now
  372. pas = (IAssocStore*)pfpsp->pAssocStore;
  373. if (!pas)
  374. {
  375. pas = new CFTAssocStore();
  376. pfpsp->pAssocStore = (void *)pas;
  377. }
  378. if (!pfpsp->pAssocStore)
  379. {
  380. // if we couldn't make an AssocStore, so bail
  381. return E_OUTOFMEMORY;
  382. }
  383. LPTSTR pszExt = PathFindExtension(szPath);
  384. if (PathIsExe(szPath) || !szPath[0] || *pszExt == TEXT('\0'))
  385. {
  386. // this file is an .exe (or .com, .bat, etc) or GetPathFromLinkFile returned a
  387. // null path (eg link to a special folder) or the file has no extension (eg 'c:\foo.',
  388. // or 'c:\'), then we dont want the user to be able to change the association since
  389. // there isint one.
  390. fAllowModifyOpenWith = FALSE;
  391. }
  392. if (fAllowModifyOpenWith)
  393. {
  394. // get the AssocInfo for this file, based on its extension
  395. hr = pas->GetAssocInfo(pszExt, AIINIT_EXT, &pai);
  396. #ifdef DEBUG
  397. if (FAILED(hr))
  398. {
  399. ASSERT(pai == NULL);
  400. }
  401. #endif
  402. }
  403. if (SHRestricted(REST_NOFILEASSOCIATE))
  404. {
  405. // we are not allowed to change file assocoations, so remove the opens with button
  406. fAllowModifyOpenWith = FALSE;
  407. }
  408. SetDescriptionAndOpensWithBtn(pfpsp, fAllowModifyOpenWith);
  409. bShowSmallIcon = SetSmallIcon(pfpsp, pai, fAllowModifyOpenWith);
  410. SetFriendlyNameText(szPath, pfpsp, pai, bShowSmallIcon);
  411. if (pai)
  412. {
  413. pai->Release();
  414. }
  415. // save off the link target so we only update the stuff above when the target changes.
  416. if (pfpsp->fIsLink)
  417. {
  418. // use null-on-failure so if it is too big we will fail the lstrcmpi call above and always update
  419. StringCchCopyEx(pfpsp->szLinkTarget, ARRAYSIZE(pfpsp->szLinkTarget), szPath, NULL, NULL, STRSAFE_NULL_ON_FAILURE);
  420. }
  421. else
  422. {
  423. // its not a link so reset the link target to the empty string
  424. pfpsp->szLinkTarget[0] = TEXT('\0');
  425. }
  426. return S_OK;
  427. }