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.

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