Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

702 lines
22 KiB

  1. #include "shellprv.h"
  2. #include "propsht.h"
  3. #include "sencrypt.h"
  4. #include "datautil.h"
  5. #define IDM_ENCRYPT 0
  6. #define IDM_DECRYPT 1
  7. #define BOOL_UNINIT 5
  8. // Local fns to this .cpp file
  9. STDAPI CEncryptionContextMenuHandler_CreateInstance(IUnknown *punk, REFIID riid, void **ppv);
  10. BOOL InitSinglePrshtNoDlg(FILEPROPSHEETPAGE * pfpsp);
  11. BOOL InitMultiplePrshtNoDlg(FILEPROPSHEETPAGE* pfpsp);
  12. // Class definition
  13. class CEncryptionContextMenu : public IShellExtInit, public IContextMenu
  14. {
  15. public:
  16. CEncryptionContextMenu();
  17. HRESULT Init_FolderContentsInfo();
  18. // IUnknown
  19. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  20. STDMETHODIMP_(ULONG) AddRef(void);
  21. STDMETHODIMP_(ULONG) Release(void);
  22. // IContextMenu
  23. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  24. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici);
  25. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  26. // IShellExtInit
  27. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  28. private:
  29. virtual ~CEncryptionContextMenu();
  30. static DWORD CALLBACK EncryptThreadProc(void *pv) { return ((CEncryptionContextMenu *) pv)->_Encrypt(); };
  31. DWORD _Encrypt();
  32. BOOL _InitPrsht(FILEPROPSHEETPAGE * pfpsp);
  33. BOOL _AreFilesEncryptable(IDataObject *pdtobj);
  34. LONG _cRef; // Reference count
  35. UINT _uFileCount; // number of files selected
  36. HWND _hwnd; // Window that we're working over
  37. BOOL _fEncrypt; // If true, do encrypt; if false, do decrypt
  38. FILEPROPSHEETPAGE _fpsp; // Prop sheet page to be filled in and run through properties funcs
  39. BOOL _fEncryptAllowed; // True iff we are allowed to encrypt
  40. IDataObject *_pdtobj; // Our data object. Keep in orig. thread
  41. TCHAR _szPath[MAX_PATH]; // Path of first thing clicked on
  42. };
  43. // Constructor & Destructor
  44. CEncryptionContextMenu::CEncryptionContextMenu() : _cRef(1)
  45. {
  46. DllAddRef();
  47. _fEncryptAllowed = FALSE; // compute this at ::Initialize() time
  48. _uFileCount = 0;
  49. _hwnd = 0;
  50. _fEncrypt = FALSE;
  51. _pdtobj = NULL;
  52. ZeroMemory(&_fpsp, sizeof(_fpsp));
  53. }
  54. CEncryptionContextMenu::~CEncryptionContextMenu()
  55. {
  56. ATOMICRELEASE(_pdtobj);
  57. if (_fpsp.pfci)
  58. {
  59. Release_FolderContentsInfo(_fpsp.pfci);
  60. }
  61. DllRelease();
  62. }
  63. HRESULT CEncryptionContextMenu::Init_FolderContentsInfo()
  64. {
  65. HRESULT hr = E_OUTOFMEMORY;
  66. _fpsp.pfci = Create_FolderContentsInfo();
  67. if (_fpsp.pfci)
  68. {
  69. hr = S_OK;
  70. }
  71. return hr;
  72. }
  73. // IUnknown implementation. Standard stuff, nothing fancy.
  74. HRESULT CEncryptionContextMenu::QueryInterface(REFIID riid, void **ppvObj)
  75. {
  76. static const QITAB qit[] = {
  77. QITABENT(CEncryptionContextMenu, IShellExtInit),
  78. QITABENT(CEncryptionContextMenu, IContextMenu),
  79. { 0 },
  80. };
  81. return QISearch(this, qit, riid, ppvObj);
  82. }
  83. ULONG CEncryptionContextMenu::AddRef()
  84. {
  85. return InterlockedIncrement(&_cRef);
  86. }
  87. ULONG CEncryptionContextMenu::Release()
  88. {
  89. if (InterlockedDecrement(&_cRef))
  90. return _cRef;
  91. delete this;
  92. return 0;
  93. }
  94. // IShellExtInit implementation
  95. STDMETHODIMP CEncryptionContextMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  96. {
  97. HRESULT hr = S_FALSE;
  98. // registry key that enables/disables this menu
  99. BOOL fEnableEncryptMenu = SHRegGetBoolUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
  100. TEXT("EncryptionContextMenu"), 0, 0);
  101. if (fEnableEncryptMenu && !SHRestricted(REST_NOENCRYPTION) && !_fEncryptAllowed)
  102. {
  103. _fEncryptAllowed = _AreFilesEncryptable(pdtobj);
  104. if (_fEncryptAllowed)
  105. {
  106. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  107. hr = S_OK;
  108. }
  109. }
  110. return hr;
  111. }
  112. // Checks the data object to see if we can encrypt here.
  113. BOOL CEncryptionContextMenu::_AreFilesEncryptable(IDataObject *pdtobj)
  114. {
  115. BOOL fSuccess = FALSE;
  116. STGMEDIUM medium;
  117. FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  118. if (SUCCEEDED(pdtobj->GetData(&fe, &medium)))
  119. {
  120. // Get the file name from the CF_HDROP.
  121. _uFileCount = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0);
  122. if (_uFileCount)
  123. {
  124. if (DragQueryFile((HDROP)medium.hGlobal, 0, _szPath, ARRAYSIZE(_szPath)))
  125. {
  126. TCHAR szFileSys[MAX_PATH];
  127. fSuccess = (FS_FILE_ENCRYPTION & GetVolumeFlags(_szPath, szFileSys, ARRAYSIZE(szFileSys)));
  128. }
  129. }
  130. ReleaseStgMedium(&medium);
  131. }
  132. return fSuccess;
  133. }
  134. // IContextMenuHandler impelementation
  135. STDMETHODIMP CEncryptionContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu,
  136. UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  137. {
  138. HRESULT hr = E_FAIL;
  139. if ((uFlags & CMF_DEFAULTONLY) || !_fEncryptAllowed)
  140. {
  141. hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(1)); //this menu only allows the defaults or we can't encrypt
  142. }
  143. else
  144. {
  145. TCHAR szEncryptMsg[128], szDecryptMsg[128];
  146. // If only one item is selected, display enc or dec as appropriate
  147. if (_uFileCount == 1)
  148. {
  149. DWORD dwAttribs = GetFileAttributes(_szPath);
  150. if (dwAttribs != (DWORD)-1)
  151. {
  152. LoadString(HINST_THISDLL, IDS_ECM_ENCRYPT, szEncryptMsg, ARRAYSIZE(szEncryptMsg));
  153. if (!(dwAttribs & FILE_ATTRIBUTE_ENCRYPTED))
  154. {
  155. if (InsertMenu(hmenu,
  156. indexMenu,
  157. MF_STRING | MF_BYPOSITION,
  158. idCmdFirst + IDM_ENCRYPT,
  159. szEncryptMsg))
  160. {
  161. hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_ENCRYPT + 1));
  162. }
  163. }
  164. else
  165. {
  166. LoadString(HINST_THISDLL, IDS_ECM_DECRYPT, szDecryptMsg, ARRAYSIZE(szDecryptMsg));
  167. if (InsertMenu(hmenu,
  168. indexMenu,
  169. MF_STRING | MF_BYPOSITION,
  170. idCmdFirst + IDM_ENCRYPT + 1,
  171. szDecryptMsg))
  172. {
  173. hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DECRYPT + 1));
  174. }
  175. }
  176. }
  177. }
  178. else if (idCmdLast - idCmdFirst >= 2)
  179. {
  180. LoadString(HINST_THISDLL, IDS_ECM_ENCRYPT, szEncryptMsg, ARRAYSIZE(szDecryptMsg));
  181. LoadString(HINST_THISDLL, IDS_ECM_DECRYPT, szDecryptMsg, ARRAYSIZE(szDecryptMsg));
  182. // If more than one item is selected, display both enc and dec
  183. if (InsertMenu(hmenu,
  184. indexMenu,
  185. MF_STRING | MF_BYPOSITION,
  186. idCmdFirst + IDM_ENCRYPT,
  187. szEncryptMsg))
  188. {
  189. if (InsertMenu(hmenu,
  190. indexMenu + 1,
  191. MF_STRING | MF_BYPOSITION,
  192. idCmdFirst + IDM_DECRYPT,
  193. szDecryptMsg))
  194. {
  195. hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DECRYPT + 1));
  196. }
  197. else
  198. {
  199. // If you can't add both, add neither
  200. RemoveMenu(hmenu, indexMenu, MF_BYPOSITION);
  201. }
  202. }
  203. }
  204. }
  205. return hr;
  206. }
  207. const ICIVERBTOIDMAP c_IDMap[] =
  208. {
  209. { L"encrypt", "encrypt", IDM_ENCRYPT, IDM_ENCRYPT, },
  210. { L"decrypt", "decrypt", IDM_DECRYPT, IDM_DECRYPT, },
  211. };
  212. STDMETHODIMP CEncryptionContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  213. {
  214. UINT uID;
  215. HRESULT hr = E_FAIL;
  216. if (_fEncryptAllowed)
  217. {
  218. hr = SHMapICIVerbToCmdID(pici, c_IDMap, ARRAYSIZE(c_IDMap), &uID);
  219. if (SUCCEEDED(hr))
  220. {
  221. switch (uID)
  222. {
  223. case IDM_ENCRYPT:
  224. case IDM_DECRYPT:
  225. _fEncrypt = (IDM_ENCRYPT == uID);
  226. break;
  227. default:
  228. ASSERTMSG(0, "Should never get commands we didn't put on the menu...");
  229. break;
  230. }
  231. _hwnd = pici->hwnd; // The handle to the explorer window that called us.
  232. ASSERT(NULL == _fpsp.pfci->hida);
  233. hr = DataObj_CopyHIDA(_pdtobj, &_fpsp.pfci->hida);
  234. if (SUCCEEDED(hr))
  235. {
  236. AddRef(); // Give our background thread a ref
  237. // Start the new thread here
  238. if (SHCreateThread(EncryptThreadProc, this, CTF_COINIT | CTF_FREELIBANDEXIT, NULL))
  239. {
  240. hr = S_OK;
  241. }
  242. else
  243. {
  244. Release(); // thread create failed
  245. }
  246. }
  247. }
  248. }
  249. // If we succeeded or not, we give up our data here
  250. ATOMICRELEASE(_pdtobj);
  251. return hr;
  252. }
  253. STDMETHODIMP CEncryptionContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags,
  254. UINT *pRes, LPSTR pszName, UINT uMaxNameLen)
  255. {
  256. HRESULT hr = E_INVALIDARG;
  257. // Note that because we can be specifically asked for
  258. // UNICODE or ansi strings, we have to be ready to load all strings in
  259. // either version.
  260. if (idCommand == IDM_ENCRYPT ||
  261. idCommand == IDM_DECRYPT)
  262. {
  263. switch(uFlags)
  264. {
  265. case GCS_HELPTEXTA:
  266. if (idCommand == IDM_ENCRYPT)
  267. {
  268. LoadStringA(HINST_THISDLL, IDS_ECM_ENCRYPT_HELP, pszName, uMaxNameLen);
  269. }
  270. else
  271. {
  272. LoadStringA(HINST_THISDLL, IDS_ECM_DECRYPT_HELP, pszName, uMaxNameLen);
  273. }
  274. hr = S_OK;
  275. break;
  276. case GCS_HELPTEXTW:
  277. if (idCommand == IDM_ENCRYPT)
  278. {
  279. LoadStringW(HINST_THISDLL, IDS_ECM_ENCRYPT_HELP, (LPWSTR)pszName, uMaxNameLen);
  280. }
  281. else
  282. {
  283. LoadStringW(HINST_THISDLL, IDS_ECM_DECRYPT_HELP, (LPWSTR)pszName, uMaxNameLen);
  284. }
  285. hr = S_OK;
  286. break;
  287. case GCS_VERBA:
  288. case GCS_VERBW:
  289. hr = SHMapCmdIDToVerb(idCommand, c_IDMap, ARRAYSIZE(c_IDMap), pszName, uMaxNameLen, GCS_VERBW == uFlags);
  290. break;
  291. default:
  292. hr = S_OK;
  293. break;
  294. }
  295. }
  296. return hr;
  297. }
  298. STDAPI CEncryptionContextMenuHandler_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
  299. {
  300. HRESULT hr;
  301. *ppv = NULL;
  302. CEncryptionContextMenu *pdocp = new CEncryptionContextMenu();
  303. if (pdocp)
  304. {
  305. hr = pdocp->Init_FolderContentsInfo();
  306. if (SUCCEEDED(hr))
  307. {
  308. hr = pdocp->QueryInterface(riid, ppv);
  309. }
  310. pdocp->Release();
  311. }
  312. else
  313. {
  314. hr = E_OUTOFMEMORY;
  315. }
  316. return hr;
  317. }
  318. // Multithread code
  319. // Proc for thread that does the encryption and manages the progress dialong.
  320. // The passed parameter is a ptr hwnd to be made modal for the context menu
  321. DWORD CEncryptionContextMenu::_Encrypt(void)
  322. {
  323. // Transfer ownership in case someone reenters our thread creator
  324. // Init the property sheet
  325. BOOL fSuccess = _InitPrsht(&_fpsp);
  326. if (fSuccess)
  327. {
  328. // Set encryption opts, turn off compression
  329. if (_fEncrypt)
  330. {
  331. _fpsp.asCurrent.fCompress = FALSE;
  332. _fpsp.asCurrent.fEncrypt = TRUE;
  333. }
  334. else
  335. {
  336. _fpsp.asCurrent.fEncrypt = FALSE;
  337. }
  338. // See if the user wants to do this recursive-style
  339. if (_fpsp.fIsDirectory)
  340. {
  341. // check to see if the user wants to apply the attribs recursively or not
  342. fSuccess = (int)DialogBoxParam(HINST_THISDLL,
  343. MAKEINTRESOURCE(DLG_ATTRIBS_RECURSIVE),
  344. _hwnd, RecursivePromptDlgProc, (LPARAM)&_fpsp);
  345. }
  346. // Apply encryption, remember to turn off compression
  347. if (fSuccess)
  348. {
  349. if (_fpsp.pfci->fMultipleFiles || _fpsp.fRecursive)
  350. {
  351. ApplyMultipleFileAttributes(&_fpsp);
  352. }
  353. else
  354. {
  355. ApplySingleFileAttributesNoDlg(&_fpsp, _hwnd);
  356. }
  357. }
  358. }
  359. // As far as I can tell, nothing in fpsp must be freed
  360. // But, we give up the storage medium here
  361. Release(); // Release our ref
  362. return fSuccess; // Must give a ret value
  363. }
  364. // Helper to init the passed prsht
  365. BOOL CEncryptionContextMenu::_InitPrsht(FILEPROPSHEETPAGE * pfpsp)
  366. {
  367. // Init the propsht properly
  368. BOOL fSuccess = S_OK == InitCommonPrsht(pfpsp);
  369. if (fSuccess)
  370. {
  371. if (_uFileCount == 1)
  372. {
  373. fSuccess = InitSinglePrshtNoDlg(pfpsp);
  374. }
  375. else if (_uFileCount > 1)
  376. {
  377. fSuccess = InitMultiplePrshtNoDlg(pfpsp);
  378. }
  379. }
  380. return fSuccess;
  381. }
  382. //
  383. // Descriptions:
  384. // This function fills fields of the multiple object property sheet,
  385. // without getting the current state from the dialog.
  386. //
  387. BOOL InitMultiplePrshtNoDlg(FILEPROPSHEETPAGE* pfpsp)
  388. {
  389. SHFILEINFO sfi;
  390. TCHAR szBuffer[MAX_PATH+1];
  391. TCHAR szType[MAX_PATH] = {0};
  392. TCHAR szDirPath[MAX_PATH] = {0};
  393. int iItem;
  394. BOOL fMultipleType = FALSE;
  395. BOOL fSameLocation = TRUE;
  396. DWORD dwFlagsOR = 0; // start all clear
  397. DWORD dwFlagsAND = (DWORD)-1; // start all set
  398. DWORD dwVolumeFlagsAND = (DWORD)-1; // start all set
  399. // For all the selected files compare their types and get their attribs
  400. for (iItem = 0; HIDA_FillFindData(pfpsp->pfci->hida, iItem, szBuffer, NULL, FALSE); iItem++)
  401. {
  402. DWORD dwFileAttributes = GetFileAttributes(szBuffer);
  403. dwFlagsAND &= dwFileAttributes;
  404. dwFlagsOR |= dwFileAttributes;
  405. // process types only if we haven't already found that there are several types
  406. if (!fMultipleType)
  407. {
  408. SHGetFileInfo((LPTSTR)IDA_GetIDListPtr((LPIDA)GlobalLock(pfpsp->pfci->hida), iItem), 0,
  409. &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_TYPENAME);
  410. if (szType[0] == TEXT('\0'))
  411. lstrcpy(szType, sfi.szTypeName);
  412. else
  413. fMultipleType = lstrcmp(szType, sfi.szTypeName) != 0;
  414. }
  415. dwVolumeFlagsAND &= GetVolumeFlags(szBuffer, pfpsp->szFileSys, ARRAYSIZE(pfpsp->szFileSys));
  416. // check to see if the files are in the same location
  417. if (fSameLocation)
  418. {
  419. PathRemoveFileSpec(szBuffer);
  420. if (szDirPath[0] == TEXT('\0'))
  421. StrCpyN(szDirPath, szBuffer, ARRAYSIZE(szDirPath));
  422. else
  423. fSameLocation = (lstrcmpi(szDirPath, szBuffer) == 0);
  424. }
  425. }
  426. if ((dwVolumeFlagsAND & FS_FILE_ENCRYPTION) && !SHRestricted(REST_NOENCRYPTION))
  427. {
  428. // all the files are on volumes that support encryption (eg NTFS)
  429. pfpsp->fIsEncryptionAvailable = TRUE;
  430. }
  431. if (dwVolumeFlagsAND & FS_FILE_COMPRESSION)
  432. {
  433. pfpsp->pfci->fIsCompressionAvailable = TRUE;
  434. }
  435. //
  436. // HACKHACK (reinerf) - we dont have a FS_SUPPORTS_INDEXING so we
  437. // use the FILE_SUPPORTS_SPARSE_FILES flag, because native index support
  438. // appeared first on NTFS5 volumes, at the same time sparse file support
  439. // was implemented.
  440. //
  441. if (dwVolumeFlagsAND & FILE_SUPPORTS_SPARSE_FILES)
  442. {
  443. // yup, we are on NTFS5 or greater
  444. pfpsp->fIsIndexAvailable = TRUE;
  445. }
  446. // if any of the files was a directory, then we set this flag
  447. if (dwFlagsOR & FILE_ATTRIBUTE_DIRECTORY)
  448. {
  449. pfpsp->fIsDirectory = TRUE;
  450. }
  451. // setup all the flags based on what we found out
  452. SetInitialFileAttribs(pfpsp, dwFlagsAND, dwFlagsOR);
  453. // set the current attributes to the same as the initial
  454. pfpsp->asCurrent = pfpsp->asInitial;
  455. if (fSameLocation)
  456. {
  457. LoadString(HINST_THISDLL, IDS_ALLIN, szBuffer, ARRAYSIZE(szBuffer));
  458. StrCatBuff(szBuffer, szDirPath, ARRAYSIZE(szBuffer));
  459. StrCpyN(pfpsp->szPath, szDirPath, ARRAYSIZE(pfpsp->szPath));
  460. }
  461. UpdateSizeField(pfpsp, NULL);
  462. return TRUE;
  463. }
  464. //
  465. // Descriptions:
  466. // This function fills fields of the "general" dialog box (a page of
  467. // a property sheet) with attributes of the associated file. Doesn't
  468. // make calss to hDlg
  469. //
  470. BOOL InitSinglePrshtNoDlg(FILEPROPSHEETPAGE * pfpsp)
  471. {
  472. TCHAR szBuffer[MAX_PATH];
  473. SHFILEINFO sfi;
  474. // fd is filled in with info from the pidl, but this
  475. // does not contain all the date/time information so hit the disk here.
  476. HANDLE hfind = FindFirstFile(pfpsp->szPath, &pfpsp->fd);
  477. ASSERT(hfind != INVALID_HANDLE_VALUE);
  478. if (hfind == INVALID_HANDLE_VALUE)
  479. {
  480. // if this failed we should clear out some values as to not show garbage on the screen.
  481. ZeroMemory(&pfpsp->fd, sizeof(pfpsp->fd));
  482. }
  483. else
  484. {
  485. FindClose(hfind);
  486. }
  487. // get info about the file.
  488. SHGetFileInfo(pfpsp->szPath, pfpsp->fd.dwFileAttributes, &sfi, sizeof(sfi),
  489. SHGFI_ICON|SHGFI_LARGEICON|
  490. SHGFI_DISPLAYNAME|
  491. SHGFI_TYPENAME | SHGFI_ADDOVERLAYS);
  492. // .ani cursor hack!
  493. if (StrCmpI(PathFindExtension(pfpsp->szPath), TEXT(".ani")) == 0)
  494. {
  495. HICON hIcon = (HICON)LoadImage(NULL, pfpsp->szPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
  496. if (hIcon)
  497. {
  498. if (sfi.hIcon)
  499. DestroyIcon(sfi.hIcon);
  500. sfi.hIcon = hIcon;
  501. }
  502. }
  503. // set the initial rename state
  504. pfpsp->fRename = FALSE;
  505. // set the file type
  506. if (pfpsp->fMountedDrive)
  507. {
  508. TCHAR szVolumeGUID[MAX_PATH];
  509. TCHAR szVolumeLabel[MAX_PATH];
  510. //Borrow szVolumeGUID
  511. LoadString(HINST_THISDLL, IDS_MOUNTEDVOLUME, szVolumeGUID, ARRAYSIZE(szVolumeGUID));
  512. //use szVolumeLabel temporarily
  513. lstrcpy(szVolumeLabel, pfpsp->szPath);
  514. PathAddBackslash(szVolumeLabel);
  515. GetVolumeNameForVolumeMountPoint(szVolumeLabel, szVolumeGUID, ARRAYSIZE(szVolumeGUID));
  516. if (!GetVolumeInformation(szVolumeGUID, szVolumeLabel, ARRAYSIZE(szVolumeLabel),
  517. NULL, NULL, NULL, pfpsp->szFileSys, ARRAYSIZE(pfpsp->szFileSys)))
  518. {
  519. *szVolumeLabel = 0;
  520. }
  521. if (!(*szVolumeLabel))
  522. LoadString(HINST_THISDLL, IDS_UNLABELEDVOLUME, szVolumeLabel, ARRAYSIZE(szVolumeLabel));
  523. }
  524. // save off the initial short filename, and set the "Name" edit box
  525. lstrcpy(pfpsp->szInitialName, sfi.szDisplayName);
  526. // use a strcmp to see if we are showing the extension
  527. if (lstrcmpi(sfi.szDisplayName, PathFindFileName(pfpsp->szPath)) == 0)
  528. {
  529. // since the strings are the same, we must be showing the extension
  530. pfpsp->fShowExtension = TRUE;
  531. }
  532. lstrcpy(szBuffer, pfpsp->szPath);
  533. PathRemoveFileSpec(szBuffer);
  534. // Are we a folder shortcut?
  535. if (!pfpsp->fFolderShortcut)
  536. {
  537. // set the initial attributes
  538. SetInitialFileAttribs(pfpsp, pfpsp->fd.dwFileAttributes, pfpsp->fd.dwFileAttributes);
  539. // set the current attributes to the same as the initial
  540. pfpsp->asCurrent = pfpsp->asInitial;
  541. UpdateSizeField(pfpsp, &pfpsp->fd);
  542. if (!(pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  543. {
  544. // Check to see if the target file is a lnk, because if it is a lnk then
  545. // we need to display the type information for the target, not the lnk itself.
  546. if (PathIsShortcut(pfpsp->szPath, pfpsp->fd.dwFileAttributes))
  547. {
  548. pfpsp->fIsLink = TRUE;
  549. }
  550. if (!(GetFileAttributes(pfpsp->szPath) & FILE_ATTRIBUTE_OFFLINE))
  551. {
  552. UpdateOpensWithInfo(pfpsp);
  553. }
  554. }
  555. else
  556. {
  557. pfpsp->fIsDirectory = TRUE;
  558. }
  559. // get the full path to the folder that contains this file.
  560. StrCpyN(szBuffer, pfpsp->szPath, ARRAYSIZE(szBuffer));
  561. PathRemoveFileSpec(szBuffer);
  562. }
  563. return TRUE;
  564. }
  565. STDAPI_(BOOL) ApplySingleFileAttributesNoDlg(FILEPROPSHEETPAGE* pfpsp, HWND hwnd)
  566. {
  567. BOOL bRet = TRUE;
  568. BOOL bSomethingChanged = FALSE;
  569. if (!pfpsp->fRecursive)
  570. {
  571. bRet = ApplyFileAttributes(pfpsp->szPath, pfpsp, hwnd, &bSomethingChanged);
  572. if (bSomethingChanged)
  573. {
  574. // something changed, so generate a notification for the item
  575. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pfpsp->szPath, NULL);
  576. }
  577. }
  578. else
  579. {
  580. // We only should be doing a recursive operation if we have a directory!
  581. ASSERT(pfpsp->fIsDirectory);
  582. CreateAttributeProgressDlg(pfpsp);
  583. // apply attribs to this folder & sub files/folders
  584. bRet = ApplyRecursiveFolderAttribs(pfpsp->szPath, pfpsp);
  585. // send out a notification for the whole dir, regardless of the return value since
  586. // something could have changed even if the user hit cancel
  587. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, pfpsp->szPath, NULL);
  588. DestroyAttributeProgressDlg(pfpsp);
  589. }
  590. if (bRet)
  591. {
  592. // since we just sucessfully applied attribs, reset any tri-state checkboxes as necessary
  593. //UpdateTriStateCheckboxes(pfpsp);
  594. // the user did NOT hit cancel, so update the prop sheet to reflect the new attribs
  595. pfpsp->asInitial = pfpsp->asCurrent;
  596. }
  597. // handle any events we may have generated
  598. SHChangeNotifyHandleEvents();
  599. return bRet;
  600. }