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.

4255 lines
112 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: folder.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include <shguidp.h>
  13. #include <shdguid.h>
  14. #include <shlapip.h>
  15. #include <shlobjp.h>
  16. #include <shsemip.h>
  17. #include "folder.h"
  18. #include "resource.h"
  19. #include "idldata.h"
  20. #include "idlhelp.h"
  21. #include "items.h"
  22. #include "strings.h"
  23. #include "msgbox.h"
  24. #include "sharecnx.h"
  25. #include "msg.h"
  26. #include "security.h"
  27. //
  28. // This module contains several classes. Here's a summary list.
  29. //
  30. // COfflineFilesFolder - Implementation for IShellFolder
  31. //
  32. // COfflineDetails - Implementation for IShellDetails
  33. //
  34. // COfflineFilesViewCallback - Implementation for IShellFolderViewCB
  35. //
  36. // COfflineFilesDropTarget - Implementation for IDropTarget
  37. //
  38. // COfflineFilesViewEnum - Implementation for IEnumSFVViews
  39. //
  40. // CShellObjProxy<T> - Template class that encapsulates the attainment of a
  41. // shell object and item ID list for a given OLID and interface
  42. // type. Also ensures proper cleanup of the interface pointer
  43. // and ID list.
  44. //
  45. // CFolderCache - A simple cache of a bound shell object pointer
  46. // and item ID lists for associated OLIDs. Reduces the number
  47. // of binds required in the shell namespace. A singleton instance
  48. // is used for all cache accesses.
  49. //
  50. // CFolderDeleteHandler - Centralizes folder item deletion code.
  51. //
  52. // CFileTypeCache - Cache of file type descriptions. This reduces the number
  53. // of calls to SHGetFileInfo.
  54. //
  55. //
  56. // Columns
  57. //
  58. enum {
  59. ICOL_NAME = 0,
  60. ICOL_TYPE,
  61. ICOL_SYNCSTATUS,
  62. ICOL_PINSTATUS,
  63. ICOL_ACCESS,
  64. ICOL_SERVERSTATUS,
  65. ICOL_LOCATION,
  66. ICOL_SIZE,
  67. ICOL_DATE,
  68. ICOL_MAX
  69. };
  70. typedef struct
  71. {
  72. short int icol; // column index
  73. short int ids; // Id of string for title
  74. short int cchCol; // Number of characters wide to make column
  75. short int iFmt; // The format of the column;
  76. } COL_DATA;
  77. const COL_DATA c_cols[] = {
  78. {ICOL_NAME, IDS_COL_NAME, 20, LVCFMT_LEFT},
  79. {ICOL_TYPE, IDS_COL_TYPE, 20, LVCFMT_LEFT},
  80. {ICOL_SYNCSTATUS, IDS_COL_SYNCSTATUS, 18, LVCFMT_LEFT},
  81. {ICOL_PINSTATUS, IDS_COL_PINSTATUS, 18, LVCFMT_LEFT},
  82. {ICOL_ACCESS, IDS_COL_ACCESS, 18, LVCFMT_LEFT},
  83. {ICOL_SERVERSTATUS,IDS_COL_SERVERSTATUS,18, LVCFMT_LEFT},
  84. {ICOL_LOCATION, IDS_COL_LOCATION, 18, LVCFMT_LEFT},
  85. {ICOL_SIZE, IDS_COL_SIZE, 16, LVCFMT_RIGHT},
  86. {ICOL_DATE, IDS_COL_DATE, 20, LVCFMT_LEFT}
  87. };
  88. //
  89. // This is a special GUID used by the folder's delete handler to obtain
  90. // the IShellFolderViewCB pointer from the COfflineFilesFolder.
  91. // The delete handler QI's for this "interface". If the folder knows
  92. // about it (only the COfflineFilesFolder will) then it returns it's
  93. // IShellFolderViewCB pointer. See COfflineFilesFolder::QueryInterface()
  94. // and CFolderDeleteHandler::InvokeCommand for usage.
  95. //
  96. // {47862305-0417-11d3-8BED-00C04FA31A66}
  97. static const GUID IID_OfflineFilesFolderViewCB =
  98. { 0x47862305, 0x417, 0x11d3, { 0x8b, 0xed, 0x0, 0xc0, 0x4f, 0xa3, 0x1a, 0x66 } };
  99. //
  100. // Private message used to enable/disable redraw of the listview
  101. // through the MessageSFVCB method of the folder view callback.
  102. // See CFolderDeleteHandler::DeleteFiles and
  103. // COfflineFilesViewCallback::OnSFVMP_SetViewRedraw for usage.
  104. //
  105. const UINT SFVMP_SETVIEWREDRAW = 1234;
  106. const UINT SFVMP_DELVIEWITEM = 1235;
  107. #if defined(ALIGNMENT_MACHINE)
  108. LONG
  109. __inline
  110. static
  111. uaCompareFileTime(
  112. IN FILETIME CONST UNALIGNED *UaFileTime1,
  113. IN FILETIME CONST UNALIGNED *UaFileTime2
  114. )
  115. {
  116. FILETIME fileTime1;
  117. FILETIME fileTime2;
  118. fileTime1 = *UaFileTime1;
  119. fileTime2 = *UaFileTime2;
  120. return CompareFileTime( &fileTime1, &fileTime2 );
  121. }
  122. #else
  123. #define uaCompareFileTime CompareFileTime
  124. #endif
  125. HRESULT StringToStrRet(LPCTSTR pString, STRRET *pstrret)
  126. {
  127. #ifdef UNICODE
  128. HRESULT hr = SHStrDup(pString, &pstrret->pOleStr);
  129. if (SUCCEEDED(hr))
  130. {
  131. pstrret->uType = STRRET_WSTR;
  132. }
  133. return hr;
  134. #else
  135. pstrret->uType = STRRET_CSTR;
  136. lstrcpyn(pstrret->cStr, pString, ARRAYSIZE(pstrret->cStr));
  137. return S_OK;
  138. #endif
  139. }
  140. //---------------------------------------------------------------------------
  141. // Shell view details
  142. //---------------------------------------------------------------------------
  143. class COfflineDetails : public IShellDetails
  144. {
  145. public:
  146. COfflineDetails(COfflineFilesFolder *pFav);
  147. // *** IUnknown methods ***
  148. STDMETHOD(QueryInterface) (THIS_ REFIID riid, void ** ppv);
  149. STDMETHOD_(ULONG,AddRef) (THIS);
  150. STDMETHOD_(ULONG,Release) (THIS);
  151. // IshellDetails
  152. STDMETHOD(GetDetailsOf)(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetails);
  153. STDMETHOD(ColumnClick)(UINT iColumn);
  154. protected:
  155. ~COfflineDetails();
  156. COfflineFilesFolder *_pfolder;
  157. LONG _cRef;
  158. };
  159. //---------------------------------------------------------------------------
  160. // Folder view callback
  161. //---------------------------------------------------------------------------
  162. class COfflineFilesViewCallback : public IShellFolderViewCB, IObjectWithSite
  163. {
  164. public:
  165. COfflineFilesViewCallback(COfflineFilesFolder *pfolder);
  166. // IUnknown
  167. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  168. STDMETHOD_(ULONG, AddRef)();
  169. STDMETHOD_(ULONG, Release)();
  170. // IShellFolderViewCB
  171. STDMETHOD(MessageSFVCB)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  172. // IObjectWithSite
  173. STDMETHOD(SetSite)(IUnknown *punkSite);
  174. STDMETHOD(GetSite)(REFIID riid, void **ppv);
  175. private:
  176. LONG _cRef;
  177. COfflineFilesFolder *_pfolder;
  178. IShellFolderView *_psfv;
  179. HWND m_hwnd;
  180. CRITICAL_SECTION m_cs; // Serialize change notify handling.
  181. ~COfflineFilesViewCallback();
  182. DWORD GetChangeNotifyEvents(void) const
  183. { return (SHCNE_UPDATEITEM | SHCNE_UPDATEDIR | SHCNE_RENAMEITEM | SHCNE_DELETE); }
  184. HRESULT OnSFVM_WindowCreated(HWND hwnd);
  185. HRESULT OnSFVM_AddPropertyPages(DWORD pv, SFVM_PROPPAGE_DATA *ppagedata);
  186. HRESULT OnSFVM_QueryFSNotify(SHChangeNotifyEntry *pfsne);
  187. HRESULT OnSFVM_FSNotify(LPCITEMIDLIST *ppidl, LONG lEvent);
  188. HRESULT OnSFVM_GetNotify(LPITEMIDLIST *ppidl, LONG *plEvents);
  189. HRESULT OnSFVM_GetViews(SHELLVIEWID *pvid, IEnumSFVViews **ppev);
  190. HRESULT OnSFVM_AlterDropEffect(DWORD *pdwEffect, IDataObject *pdtobj);
  191. HRESULT OnSFVMP_SetViewRedraw(BOOL bRedraw);
  192. HRESULT OnSFVMP_DelViewItem(LPCTSTR pszPath);
  193. HRESULT UpdateDir(LPCTSTR pszDir);
  194. HRESULT UpdateItem(LPCTSTR pszItem);
  195. HRESULT UpdateItem(LPCTSTR pszPath, const WIN32_FIND_DATA& fd, DWORD dwStatus, DWORD dwPinCount, DWORD dwHintFlags);
  196. HRESULT RemoveItem(LPCTSTR pszPath);
  197. HRESULT RemoveItem(LPCOLID polid);
  198. HRESULT RemoveItems(LPCTSTR pszDir);
  199. HRESULT RenameItem(LPCITEMIDLIST pidlOld, LPCITEMIDLIST pidl);
  200. UINT ItemIndexFromOLID(LPCOLID polid);
  201. HRESULT FindOLID(LPCTSTR pszPath, LPCOLID *ppolid);
  202. void Lock(void)
  203. { EnterCriticalSection(&m_cs); }
  204. void Unlock(void)
  205. { LeaveCriticalSection(&m_cs); }
  206. };
  207. //---------------------------------------------------------------------------
  208. // Drop target
  209. //---------------------------------------------------------------------------
  210. class COfflineFilesDropTarget : public IDropTarget
  211. {
  212. public:
  213. // IUnknown
  214. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  215. STDMETHOD_(ULONG, AddRef)(void);
  216. STDMETHOD_(ULONG, Release)(void);
  217. // IDropTarget
  218. STDMETHODIMP DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
  219. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
  220. STDMETHODIMP DragLeave(void);
  221. STDMETHODIMP Drop(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
  222. static HRESULT CreateInstance(HWND hwnd, REFIID riid, void **ppv);
  223. private:
  224. COfflineFilesDropTarget(HWND hwnd);
  225. ~COfflineFilesDropTarget();
  226. bool IsOurDataObject(IDataObject *pdtobj);
  227. LONG m_cRef;
  228. HWND m_hwnd;
  229. LPCONTEXTMENU m_pcm;
  230. bool m_bIsOurData;
  231. };
  232. //---------------------------------------------------------------------------
  233. // View type enumerator
  234. //---------------------------------------------------------------------------
  235. class COfflineFilesViewEnum : public IEnumSFVViews
  236. {
  237. public:
  238. // *** IUnknown methods ***
  239. STDMETHOD(QueryInterface) (REFIID riid, void **ppv);
  240. STDMETHOD_(ULONG,AddRef) (void);
  241. STDMETHOD_(ULONG,Release) (void);
  242. // *** IEnumSFVViews methods ***
  243. STDMETHOD(Next)(ULONG celt, SFVVIEWSDATA **ppData, ULONG *pceltFetched);
  244. STDMETHOD(Skip)(ULONG celt);
  245. STDMETHOD(Reset)(void);
  246. STDMETHOD(Clone)(IEnumSFVViews **ppenum);
  247. static HRESULT CreateInstance(IEnumSFVViews **ppEnum);
  248. protected:
  249. COfflineFilesViewEnum(void);
  250. ~COfflineFilesViewEnum(void);
  251. LONG m_cRef;
  252. int m_iAddView;
  253. };
  254. //-------------------------------------------------------------------------
  255. // Shell object proxy
  256. //
  257. // A simple template class to package up the attainment of a
  258. // shell object pointer and item PIDL from our folder cache for a given OLID.
  259. // The caller can then easily call the appropriate shell object function
  260. // through operator ->(). The object automates the release of the shell
  261. // object interface and freeing of the IDList. Caller must call Result()
  262. // to verify validity of contents prior to invoking operator ->().
  263. //
  264. // Usage:
  265. //
  266. // CShellObjProxy<IShellFolder> pxy(IID_IShellFolder, polid);
  267. // if (SUCCEEDED(hr = pxy.Result()))
  268. // {
  269. // hr = pxy->GetIconOf(pxy.ItemIDList(), gil, pnIcon);
  270. // }
  271. //
  272. //-------------------------------------------------------------------------
  273. template <class T>
  274. class CShellObjProxy
  275. {
  276. public:
  277. CShellObjProxy(REFIID riid, LPCOLID polid)
  278. : m_hr(E_INVALIDARG),
  279. m_pObj(NULL),
  280. m_pidlFull(NULL),
  281. m_pidlItem(NULL)
  282. {
  283. if (NULL != polid)
  284. {
  285. m_hr = CFolderCache::Singleton().GetItem(polid,
  286. riid,
  287. (void **)&m_pObj,
  288. &m_pidlFull,
  289. &m_pidlItem);
  290. }
  291. }
  292. ~CShellObjProxy(void)
  293. {
  294. if (NULL != m_pidlFull)
  295. ILFree(m_pidlFull);
  296. if (NULL != m_pObj)
  297. m_pObj->Release();
  298. }
  299. HRESULT Result(void) const
  300. { return m_hr; }
  301. T* operator -> () const
  302. { return m_pObj; }
  303. LPCITEMIDLIST ItemIDList(void) const
  304. { return m_pidlItem; }
  305. private:
  306. HRESULT m_hr;
  307. T *m_pObj;
  308. LPITEMIDLIST m_pidlFull;
  309. LPCITEMIDLIST m_pidlItem;
  310. };
  311. //-----------------------------------------------------------------------------------
  312. // Folder cache
  313. //
  314. // The OfflineFiles folder IDList format (OLID) contains fully-qualified UNC paths.
  315. // The folder may (most likely) contain OLIDs from multiple network shares.
  316. // Therefore, when creating IDLists to hand off to the shell's filesystem
  317. // implementations we create fully-qualified IDLists (an expensive operation).
  318. // The folder cache is used to cache these IDLists to reduce the number of calls
  319. // to SHBindToParent. This also speeds up filling of the listview as
  320. // GetAttributesOf(), GetIconIndex() etc. are called many times as the view
  321. // is opened.
  322. //
  323. // The implementation is a simple circular queue to handle aging of items.
  324. // Only three public methods are exposed. GetItem() is used to
  325. // retrieve the IShellFolder ptr and IDList associated with a particular OLID.
  326. // If the item is not in the cache, the implementation obtains the shell folder
  327. // ptr and IDList then caches them for later use. Clear() is used to clear the
  328. // contents of the cache to reduce memory footprint when the Offline Files
  329. // folder is no longer open.
  330. //
  331. // The entries in the queue utilize a handle-envelope idiom to hide memory
  332. // management of the entries from the caching code. This way we can assign
  333. // handle values without copying the actual use-counted entry. Once a use-count
  334. // drops to 0 the actual entry is deleted.
  335. //
  336. // A singleton instance is enforced through a private ctor. Use the
  337. // Singleton() method to obtain a reference to the singleton.
  338. //
  339. // Note that because of the shell's icon thread this cache must be thread-safe.
  340. // A critical section is used for this.
  341. //-----------------------------------------------------------------------------------
  342. class CFolderCache
  343. {
  344. public:
  345. ~CFolderCache(void);
  346. //
  347. // Retrieve one item from the cache. Item is added if not in cache.
  348. //
  349. HRESULT GetItem(
  350. LPCOLID polid,
  351. REFIID riid,
  352. void **ppv,
  353. LPITEMIDLIST *ppidl,
  354. LPCITEMIDLIST *ppidlChild);
  355. //
  356. // Clear the cache entry data.
  357. //
  358. void Clear(void);
  359. //
  360. // Return reference to the singleton instance.
  361. //
  362. static CFolderCache& Singleton(void);
  363. private:
  364. //
  365. // Enforce singleton existence.
  366. //
  367. CFolderCache(void);
  368. //
  369. // Prevent copy.
  370. //
  371. CFolderCache(const CFolderCache& rhs);
  372. CFolderCache& operator = (const CFolderCache& rhs);
  373. LPOLID m_polid; // Key olid.
  374. IShellFolder *m_psf; // Cached IShellFolder ptr.
  375. LPITEMIDLIST m_pidl; // Cached shell pidl.
  376. CRITICAL_SECTION m_cs; // For synchronizing cache access.
  377. void Lock(void)
  378. { EnterCriticalSection(&m_cs); }
  379. void Unlock(void)
  380. { LeaveCriticalSection(&m_cs); }
  381. };
  382. //----------------------------------------------------------------------------
  383. // COfflineDetails
  384. //----------------------------------------------------------------------------
  385. STDMETHODIMP
  386. COfflineDetails::GetDetailsOf(
  387. LPCITEMIDLIST pidl,
  388. UINT iColumn,
  389. LPSHELLDETAILS pDetails
  390. )
  391. {
  392. TCHAR szTemp[MAX_PATH];
  393. HRESULT hres;
  394. if (!pidl)
  395. {
  396. if (iColumn < ICOL_MAX)
  397. {
  398. pDetails->fmt = c_cols[iColumn].iFmt;
  399. pDetails->cxChar = c_cols[iColumn].cchCol;
  400. LoadString(g_hInstance, c_cols[iColumn].ids, szTemp, ARRAYSIZE(szTemp));
  401. hres = StringToStrRet(szTemp, &pDetails->str);
  402. }
  403. else
  404. {
  405. pDetails->str.uType = STRRET_CSTR;
  406. pDetails->str.cStr[0] = 0;
  407. hres = E_NOTIMPL;
  408. }
  409. }
  410. else
  411. {
  412. LPCOLID polid = _pfolder->_Validate(pidl);
  413. if (polid)
  414. {
  415. hres = S_OK;
  416. // Need to fill in the details
  417. switch (iColumn)
  418. {
  419. case ICOL_TYPE:
  420. _pfolder->_GetTypeString(polid, szTemp, ARRAYSIZE(szTemp));
  421. break;
  422. case ICOL_SYNCSTATUS:
  423. _pfolder->_GetSyncStatusString(polid, szTemp, ARRAYSIZE(szTemp));
  424. break;
  425. case ICOL_PINSTATUS:
  426. _pfolder->_GetPinStatusString(polid, szTemp, ARRAYSIZE(szTemp));
  427. break;
  428. case ICOL_ACCESS:
  429. _pfolder->_GetAccessString(polid, szTemp, ARRAYSIZE(szTemp));
  430. break;
  431. case ICOL_SERVERSTATUS:
  432. _pfolder->_GetServerStatusString(polid, szTemp, ARRAYSIZE(szTemp));
  433. break;
  434. case ICOL_LOCATION:
  435. ualstrcpyn(szTemp, polid->szPath, ARRAYSIZE(szTemp));
  436. break;
  437. case ICOL_SIZE:
  438. {
  439. ULARGE_INTEGER ullSize = {polid->dwFileSizeLow, polid->dwFileSizeHigh};
  440. StrFormatKBSize(ullSize.QuadPart, szTemp, ARRAYSIZE(szTemp));
  441. break;
  442. }
  443. case ICOL_DATE:
  444. SHFormatDateTime(&polid->ft, NULL, szTemp, ARRAYSIZE(szTemp));
  445. break;
  446. default:
  447. hres = E_FAIL;
  448. }
  449. if (SUCCEEDED(hres))
  450. hres = StringToStrRet(szTemp, &pDetails->str);
  451. }
  452. else
  453. hres = E_INVALIDARG;
  454. }
  455. return hres;
  456. }
  457. STDMETHODIMP
  458. COfflineDetails::ColumnClick(
  459. UINT iColumn
  460. )
  461. {
  462. return S_FALSE; // bounce this to the IShellFolderViewCB handler
  463. }
  464. //----------------------------------------------------------------------------
  465. // CFolderCache
  466. //----------------------------------------------------------------------------
  467. //
  468. // This is a very simple cache of one entry.
  469. // Originally I implemented a multi-item cache. This of course had more
  470. // overhead than a single-item cache. The problem is that the access patterns
  471. // in the folder are such that cache hits occur consecutively for the
  472. // same item as the view is filling. Rarely (or never) was there a hit
  473. // for an item other than the most-recently-added item. Therefore, items
  474. // 1 through n-1 were just taking up space. This is why I went back to using
  475. // a single-item cache. [brianau - 5/27/99]
  476. //
  477. //
  478. // Returns a reference to the global shell folder cache.
  479. // Since the folder cache object is a function static it will not be created
  480. // until this function is first called. That also means it will not be
  481. // destroyed until the module is unloaded. That's why we have the Clear() method.
  482. // The FolderViewCallback dtor clears the cache so that we don't have cached
  483. // info laying around in memory while the Offline Files folder isn't open.
  484. // The cache skeleton is very cheap so that's not a problem to leave in memory.
  485. //
  486. CFolderCache&
  487. CFolderCache::Singleton(
  488. void
  489. )
  490. {
  491. static CFolderCache TheFolderCache;
  492. return TheFolderCache;
  493. }
  494. CFolderCache::CFolderCache(
  495. void
  496. ) : m_polid(NULL),
  497. m_pidl(NULL),
  498. m_psf(NULL)
  499. {
  500. InitializeCriticalSection(&m_cs);
  501. }
  502. CFolderCache::~CFolderCache(
  503. void
  504. )
  505. {
  506. Clear();
  507. DeleteCriticalSection(&m_cs);
  508. }
  509. //
  510. // Clear the cache by deleting the queue array and
  511. // resetting the head/tail indexes. A subsequent call to
  512. // GetItem() will re-initialize the queue.
  513. //
  514. void
  515. CFolderCache::Clear(
  516. void
  517. )
  518. {
  519. Lock();
  520. if (m_polid)
  521. {
  522. ILFree((LPITEMIDLIST)m_polid);
  523. m_polid = NULL;
  524. }
  525. if (m_pidl)
  526. {
  527. ILFree(m_pidl);
  528. m_pidl = NULL;
  529. }
  530. if (m_psf)
  531. {
  532. m_psf->Release();
  533. m_psf = NULL;
  534. }
  535. Unlock();
  536. }
  537. //
  538. // Retrieve an item from the cache. If not found, bind to
  539. // and cache a new one.
  540. //
  541. HRESULT
  542. CFolderCache::GetItem(
  543. LPCOLID polid,
  544. REFIID riid,
  545. void **ppv,
  546. LPITEMIDLIST *ppidlParent,
  547. LPCITEMIDLIST *ppidlChild
  548. )
  549. {
  550. TraceAssert(NULL != polid);
  551. TraceAssert(NULL != ppv);
  552. TraceAssert(NULL != ppidlParent);
  553. TraceAssert(NULL != ppidlChild);
  554. HRESULT hr = NOERROR;
  555. *ppidlParent = NULL;
  556. *ppidlChild = NULL;
  557. *ppv = NULL;
  558. Lock();
  559. IShellFolder *psf;
  560. LPCITEMIDLIST pidlChild;
  561. LPITEMIDLIST pidl;
  562. if (NULL == m_polid || !ILIsEqual((LPCITEMIDLIST)m_polid, (LPCITEMIDLIST)polid))
  563. {
  564. //
  565. // Cache miss.
  566. //
  567. Clear();
  568. hr = COfflineFilesFolder::OLID_Bind(polid,
  569. IID_IShellFolder,
  570. (void **)&psf,
  571. (LPITEMIDLIST *)&pidl,
  572. &pidlChild);
  573. if (SUCCEEDED(hr))
  574. {
  575. //
  576. // Cache the new item.
  577. //
  578. m_polid = (LPOLID)ILClone((LPCITEMIDLIST)polid);
  579. if (NULL != m_polid)
  580. {
  581. m_pidl = pidl; // Take ownership of pidl from Bind
  582. m_psf = psf; // Use ref count from Bind.
  583. }
  584. else
  585. {
  586. ILFree(pidl);
  587. m_psf->Release();
  588. hr = E_OUTOFMEMORY;
  589. }
  590. }
  591. }
  592. if (SUCCEEDED(hr))
  593. {
  594. //
  595. // Cache hit or we just bound and cached a new item.
  596. //
  597. *ppidlParent = ILClone(m_pidl);
  598. if (NULL != *ppidlParent)
  599. {
  600. *ppidlChild = ILFindLastID(*ppidlParent);
  601. hr = m_psf->QueryInterface(riid, ppv);
  602. }
  603. }
  604. Unlock();
  605. return hr;
  606. }
  607. //----------------------------------------------------------------------------
  608. // CFolderDeleteHandler
  609. //----------------------------------------------------------------------------
  610. CFolderDeleteHandler::CFolderDeleteHandler(
  611. HWND hwndParent,
  612. IDataObject *pdtobj,
  613. IShellFolderViewCB *psfvcb
  614. ) : m_hwndParent(hwndParent),
  615. m_pdtobj(pdtobj),
  616. m_psfvcb(psfvcb)
  617. {
  618. TraceAssert(NULL != pdtobj);
  619. if (NULL != m_pdtobj)
  620. m_pdtobj->AddRef();
  621. if (NULL != m_psfvcb)
  622. m_psfvcb->AddRef();
  623. }
  624. CFolderDeleteHandler::~CFolderDeleteHandler(
  625. void
  626. )
  627. {
  628. if (NULL != m_pdtobj)
  629. m_pdtobj->Release();
  630. if (NULL != m_psfvcb)
  631. m_psfvcb->Release();
  632. }
  633. //
  634. // This function deletes files from the cache while also displaying
  635. // standard shell progress UI.
  636. //
  637. HRESULT
  638. CFolderDeleteHandler::DeleteFiles(
  639. void
  640. )
  641. {
  642. HRESULT hr = E_FAIL;
  643. if (!ConfirmDeleteFiles(m_hwndParent))
  644. return S_FALSE;
  645. if (NULL != m_pdtobj)
  646. {
  647. //
  648. // Retrieve the selection as an HDROP.
  649. //
  650. FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  651. STGMEDIUM medium;
  652. hr = m_pdtobj->GetData(&fe, &medium);
  653. if (SUCCEEDED(hr))
  654. {
  655. LPDROPFILES pDropFiles = (LPDROPFILES)GlobalLock(medium.hGlobal);
  656. if (NULL != pDropFiles)
  657. {
  658. //
  659. // Create the progress dialog.
  660. //
  661. IProgressDialog *ppd;
  662. if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog,
  663. NULL,
  664. CLSCTX_INPROC_SERVER,
  665. IID_IProgressDialog,
  666. (void **)&ppd)))
  667. {
  668. //
  669. // Init and start the progress dialog.
  670. //
  671. TCHAR szCaption[80];
  672. TCHAR szLine1[80];
  673. LPTSTR pszFileList = (LPTSTR)((LPBYTE)pDropFiles + pDropFiles->pFiles);
  674. LPTSTR pszFile = pszFileList;
  675. int cFiles = 0;
  676. int iFile = 0;
  677. bool bCancelled = false;
  678. bool bNoToAll = false;
  679. //
  680. // Count the number of files in the list.
  681. //
  682. while(TEXT('\0') != *pszFile && !bCancelled)
  683. {
  684. //
  685. // Need to guard against deleting files that have offline
  686. // changes but haven't been synchronized. User may want
  687. // to delete these but we give them lot's of warning.
  688. //
  689. if (FileModifiedOffline(pszFile) &&
  690. (bNoToAll || !ConfirmDeleteModifiedFile(m_hwndParent,
  691. pszFile,
  692. &bNoToAll,
  693. &bCancelled)))
  694. {
  695. //
  696. // "Remove" this file from the list by replacing the
  697. // first char with a '*'. We'll use this as an indicator
  698. // when scanning through the file list during the deletion
  699. // phase below.
  700. //
  701. *pszFile = TEXT('*');
  702. cFiles--;
  703. }
  704. while(*pszFile)
  705. pszFile++;
  706. pszFile++;
  707. cFiles++;
  708. }
  709. if (!bCancelled)
  710. {
  711. LoadString(g_hInstance, IDS_APPLICATION, szCaption, ARRAYSIZE(szCaption));
  712. LoadString(g_hInstance, IDS_DELFILEPROG_LINE1, szLine1, ARRAYSIZE(szLine1));
  713. ppd->SetTitle(szCaption);
  714. ppd->SetLine(1, szLine1, FALSE, NULL);
  715. ppd->SetAnimation(g_hInstance, IDA_FILEDEL);
  716. ppd->StartProgressDialog(m_hwndParent,
  717. NULL,
  718. PROGDLG_AUTOTIME | PROGDLG_MODAL,
  719. NULL);
  720. }
  721. //
  722. // Process the files in the list.
  723. //
  724. CShareCnxStatusCache CnxStatus;
  725. BOOL bUserIsAdmin = IsCurrentUserAnAdminMember();
  726. //
  727. // Disable redraw on the view to avoid flicker.
  728. //
  729. m_psfvcb->MessageSFVCB(SFVMP_SETVIEWREDRAW, 0, 0);
  730. pszFile = pszFileList;
  731. while(TEXT('\0') != *pszFile && !bCancelled)
  732. {
  733. //
  734. // If the file wasn't excluded from deletion above
  735. // by replacing the first character with '*', delete it.
  736. //
  737. if (TEXT('*') != *pszFile)
  738. {
  739. DWORD dwErr = ERROR_ACCESS_DENIED;
  740. ppd->SetLine(2, pszFile, FALSE, NULL);
  741. if (bUserIsAdmin || !OthersHaveAccess(pszFile))
  742. {
  743. dwErr = CscDelete(pszFile);
  744. if (ERROR_ACCESS_DENIED == dwErr)
  745. {
  746. //
  747. // This is a little weird. CscDelete
  748. // returns ERROR_ACCESS_DENIED if there's
  749. // a handle open on the file. Set the
  750. // code to ERROR_BUSY so we know to handle
  751. // this as a special case below.
  752. //
  753. dwErr = ERROR_BUSY;
  754. }
  755. }
  756. if (ERROR_SUCCESS == dwErr)
  757. {
  758. //
  759. // File was deleted.
  760. //
  761. if (S_OK == CnxStatus.IsOpenConnectionPathUNC(pszFile))
  762. {
  763. //
  764. // Post a shell chg "update" notify if there's
  765. // an open connection to the path. Deleting
  766. // something from the cache will remove the
  767. // "pinned" icon overlay in shell filesystem folders.
  768. //
  769. ShellChangeNotify(pszFile, NULL, iFile == cFiles, SHCNE_UPDATEITEM);
  770. }
  771. m_psfvcb->MessageSFVCB(SFVMP_DELVIEWITEM, 0, (LPARAM)pszFile);
  772. }
  773. else
  774. {
  775. //
  776. // Error deleting file.
  777. //
  778. HWND hwndProgress = GetProgressDialogWindow(ppd);
  779. INT iUserResponse = IDOK;
  780. if (ERROR_BUSY == dwErr)
  781. {
  782. //
  783. // Special handling for ERROR_BUSY.
  784. //
  785. iUserResponse = CscMessageBox(hwndProgress ? hwndProgress : m_hwndParent,
  786. MB_OKCANCEL | MB_ICONERROR,
  787. g_hInstance,
  788. IDS_FMT_ERR_DELFROMCACHE_BUSY,
  789. pszFile);
  790. }
  791. else
  792. {
  793. //
  794. // Hit an error deleting file. Display message and
  795. // give user a chance to cancel operation.
  796. //
  797. iUserResponse = CscMessageBox(hwndProgress ? hwndProgress : m_hwndParent,
  798. MB_OKCANCEL | MB_ICONERROR,
  799. Win32Error(dwErr),
  800. g_hInstance,
  801. IDS_FMT_DELFILES_ERROR,
  802. pszFile);
  803. }
  804. bCancelled = bCancelled || IDCANCEL == iUserResponse;
  805. }
  806. ppd->SetProgress(iFile++, cFiles);
  807. bCancelled = bCancelled || ppd->HasUserCancelled();
  808. }
  809. while(*pszFile)
  810. pszFile++;
  811. pszFile++;
  812. }
  813. //
  814. // Clean up the progress dialog.
  815. //
  816. ppd->StopProgressDialog();
  817. ppd->Release();
  818. m_psfvcb->MessageSFVCB(SFVMP_SETVIEWREDRAW, 0, 1);
  819. }
  820. GlobalUnlock(medium.hGlobal);
  821. }
  822. ReleaseStgMedium(&medium);
  823. }
  824. }
  825. return hr;
  826. }
  827. //
  828. // Inform user they're deleting only the offline copy of the
  829. // selected file(s) and that the file(s) will no longer be
  830. // available offline once they're deleted. The dialog also
  831. // provides a "don't bug me again" checkbox. This setting
  832. // is saved per-user in the registry.
  833. //
  834. // Returns:
  835. //
  836. // true = User pressed [OK] or had checked "don't show me
  837. // this again" at some time in the past.
  838. // false = User cancelled operation.
  839. //
  840. bool
  841. CFolderDeleteHandler::ConfirmDeleteFiles(
  842. HWND hwndParent
  843. )
  844. {
  845. //
  846. // See if user has already seen this dialog and checked
  847. // the "don't bug me again" checkbox".
  848. //
  849. DWORD dwType = REG_DWORD;
  850. DWORD cbData = sizeof(DWORD);
  851. DWORD bNoShow = 0;
  852. SHGetValue(HKEY_CURRENT_USER,
  853. c_szCSCKey,
  854. c_szConfirmDelShown,
  855. &dwType,
  856. &bNoShow,
  857. &cbData);
  858. return bNoShow || IDOK == DialogBox(g_hInstance,
  859. MAKEINTRESOURCE(IDD_CONFIRM_DELETE),
  860. hwndParent,
  861. ConfirmDeleteFilesDlgProc);
  862. }
  863. INT_PTR
  864. CFolderDeleteHandler::ConfirmDeleteFilesDlgProc(
  865. HWND hwnd,
  866. UINT uMsg,
  867. WPARAM wParam,
  868. LPARAM lParam
  869. )
  870. {
  871. switch(uMsg)
  872. {
  873. case WM_INITDIALOG:
  874. return TRUE;
  875. case WM_COMMAND:
  876. {
  877. UINT idCmd = LOWORD(wParam);
  878. switch(LOWORD(idCmd))
  879. {
  880. case IDOK:
  881. {
  882. //
  883. // Save the "Don't bug me" value if the checkbox is
  884. // checked. If it's not checked, no need to take up
  885. // reg space with a 0 value.
  886. //
  887. if (BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_CBX_CONFIRMDEL_NOSHOW))
  888. {
  889. DWORD dwNoShow = 1;
  890. SHSetValue(HKEY_CURRENT_USER,
  891. c_szCSCKey,
  892. c_szConfirmDelShown,
  893. REG_DWORD,
  894. &dwNoShow,
  895. sizeof(dwNoShow));
  896. }
  897. EndDialog(hwnd, IDOK);
  898. break;
  899. }
  900. case IDCANCEL:
  901. EndDialog(hwnd, IDCANCEL);
  902. break;
  903. default:
  904. break;
  905. }
  906. }
  907. break;
  908. }
  909. return FALSE;
  910. }
  911. //
  912. // Inform user that the file they're about to delete has been
  913. // modified offline and the changes haven't been synchronized.
  914. // Ask if they still want to delete it.
  915. // The choices are Yes, No, NoToAll, Cancel.
  916. //
  917. //
  918. // Arguments:
  919. //
  920. // hwndParent - Dialog parent.
  921. //
  922. // pszFile - Address of filename string to embed in
  923. // dialog text. Passed to dialog proc in the
  924. // LPARAM of DialogBoxParam.
  925. //
  926. // pbNoToAll - On return, indicates if the user pressed
  927. // the "No To All" button.
  928. //
  929. // pbCancel - On return, indicates if the user pressed
  930. // the "Cancel" button.
  931. // Returns:
  932. //
  933. // true = Delete it.
  934. // false = Don't delete it.
  935. //
  936. bool
  937. CFolderDeleteHandler::ConfirmDeleteModifiedFile(
  938. HWND hwndParent,
  939. LPCTSTR pszFile,
  940. bool *pbNoToAll,
  941. bool *pbCancel
  942. )
  943. {
  944. TraceAssert(NULL != pszFile);
  945. TraceAssert(NULL != pbNoToAll);
  946. TraceAssert(NULL != pbCancel);
  947. INT_PTR iResult = DialogBoxParam(g_hInstance,
  948. MAKEINTRESOURCE(IDD_CONFIRM_DELETEMOD),
  949. hwndParent,
  950. ConfirmDeleteModifiedFileDlgProc,
  951. (LPARAM)pszFile);
  952. bool bResult = false;
  953. *pbNoToAll = false;
  954. *pbCancel = false;
  955. switch(iResult)
  956. {
  957. case IDYES:
  958. bResult = true;
  959. break;
  960. case IDCANCEL:
  961. *pbCancel = true;
  962. break;
  963. case IDIGNORE:
  964. *pbNoToAll = true;
  965. break;
  966. case IDNO:
  967. default:
  968. break;
  969. }
  970. return bResult;
  971. }
  972. INT_PTR
  973. CFolderDeleteHandler::ConfirmDeleteModifiedFileDlgProc(
  974. HWND hwnd,
  975. UINT uMsg,
  976. WPARAM wParam,
  977. LPARAM lParam
  978. )
  979. {
  980. switch(uMsg)
  981. {
  982. case WM_INITDIALOG:
  983. {
  984. //
  985. // lParam is the address of the filename string to be
  986. // embedded in the dialog text. If the path is too long
  987. // to fit in the text control, shorten it with an embedded
  988. // ellipsis.
  989. //
  990. LPTSTR pszPath = NULL;
  991. if (LocalAllocString(&pszPath, (LPCTSTR)lParam))
  992. {
  993. LPTSTR pszText = NULL;
  994. RECT rc;
  995. GetWindowRect(GetDlgItem(hwnd, IDC_TXT_CONFIRM_DELETEMOD), &rc);
  996. PathCompactPath(NULL, pszPath, rc.right - rc.left);
  997. FormatStringID(&pszText,
  998. g_hInstance,
  999. IDS_CONFIRM_DELETEMOD,
  1000. pszPath);
  1001. if (NULL != pszText)
  1002. {
  1003. SetWindowText(GetDlgItem(hwnd, IDC_TXT_CONFIRM_DELETEMOD), pszText);
  1004. LocalFree(pszText);
  1005. }
  1006. LocalFreeString(&pszPath);
  1007. }
  1008. return TRUE;
  1009. }
  1010. case WM_COMMAND:
  1011. EndDialog(hwnd, LOWORD(wParam));
  1012. break;
  1013. default:
  1014. break;
  1015. }
  1016. return FALSE;
  1017. }
  1018. //
  1019. // Determine if a particular file has been modified offline.
  1020. //
  1021. bool
  1022. CFolderDeleteHandler::FileModifiedOffline(
  1023. LPCTSTR pszFile
  1024. )
  1025. {
  1026. TraceAssert(NULL != pszFile);
  1027. DWORD dwStatus = 0;
  1028. CSCQueryFileStatus(pszFile, &dwStatus, NULL, NULL);
  1029. return 0 != (FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY & dwStatus);
  1030. }
  1031. //
  1032. // Determine if a particular file can be access by another
  1033. // user other than guest.
  1034. //
  1035. bool
  1036. CFolderDeleteHandler::OthersHaveAccess(
  1037. LPCTSTR pszFile
  1038. )
  1039. {
  1040. TraceAssert(NULL != pszFile);
  1041. DWORD dwStatus = 0;
  1042. CSCQueryFileStatus(pszFile, &dwStatus, NULL, NULL);
  1043. return CscAccessOther(dwStatus);
  1044. }
  1045. //----------------------------------------------------------------------------
  1046. // COfflineFilesFolder
  1047. //----------------------------------------------------------------------------
  1048. COfflineFilesFolder::COfflineFilesFolder(
  1049. void
  1050. ) : _cRef(1),
  1051. _psfvcb(NULL), // Non ref-counted interface ptr.
  1052. m_FileTypeCache(101) // Bucket count should be prime.
  1053. {
  1054. DllAddRef();
  1055. _pidl = NULL;
  1056. }
  1057. COfflineFilesFolder::~COfflineFilesFolder(
  1058. void
  1059. )
  1060. {
  1061. if (_pidl)
  1062. ILFree(_pidl);
  1063. DllRelease();
  1064. }
  1065. // class factory constructor
  1066. STDAPI
  1067. COfflineFilesFolder_CreateInstance(
  1068. REFIID riid,
  1069. void **ppv
  1070. )
  1071. {
  1072. HRESULT hr;
  1073. COfflineFilesFolder* polff = new COfflineFilesFolder();
  1074. if (polff)
  1075. {
  1076. hr = polff->QueryInterface(riid, ppv);
  1077. polff->Release();
  1078. }
  1079. else
  1080. {
  1081. *ppv = NULL;
  1082. hr = E_OUTOFMEMORY;
  1083. }
  1084. return hr;
  1085. }
  1086. LPCOLID
  1087. COfflineFilesFolder::_Validate(
  1088. LPCITEMIDLIST pidl
  1089. )
  1090. {
  1091. LPCOLID polid = (LPCOLID)pidl;
  1092. if (polid && (polid->cbFixed == sizeof(*polid)) && (polid->uSig == OLID_SIG))
  1093. return polid;
  1094. return NULL;
  1095. }
  1096. //
  1097. // External version of _Validate but returns only T/F.
  1098. //
  1099. bool
  1100. COfflineFilesFolder::ValidateIDList(
  1101. LPCITEMIDLIST pidl
  1102. )
  1103. {
  1104. return NULL != _Validate(pidl);
  1105. }
  1106. STDMETHODIMP
  1107. COfflineFilesFolder::QueryInterface(
  1108. REFIID riid,
  1109. void **ppv
  1110. )
  1111. {
  1112. static const QITAB qit[] = {
  1113. QITABENT(COfflineFilesFolder, IShellFolder),
  1114. QITABENT(COfflineFilesFolder, IPersistFolder2),
  1115. QITABENTMULTI(COfflineFilesFolder, IPersistFolder, IPersistFolder2),
  1116. QITABENTMULTI(COfflineFilesFolder, IPersist, IPersistFolder2),
  1117. QITABENT(COfflineFilesFolder, IShellIcon),
  1118. QITABENT(COfflineFilesFolder, IShellIconOverlay),
  1119. { 0 },
  1120. };
  1121. HRESULT hr = QISearch(this, qit, riid, ppv);
  1122. if (FAILED(hr))
  1123. {
  1124. //
  1125. // OK, this is a little slimy. The "delete handler" needs to
  1126. // get at the folder's IShellFolderViewCB interface so it can
  1127. // update the view following a deletion (remember, we're only deleting
  1128. // from the cache so no meaningful FS notification will occur).
  1129. // We define this secret IID that only our folder knows about. This
  1130. // way the delete handler can safely QI any IShellFolder interface
  1131. // and only our folder will respond with a view CB pointer.
  1132. // [brianau - 5/5/99]
  1133. //
  1134. if (riid == IID_OfflineFilesFolderViewCB && NULL != _psfvcb)
  1135. {
  1136. _psfvcb->AddRef();
  1137. *ppv = (void **)_psfvcb;
  1138. hr = NOERROR;
  1139. }
  1140. }
  1141. return hr;
  1142. }
  1143. STDMETHODIMP_ (ULONG)
  1144. COfflineFilesFolder::AddRef(
  1145. void
  1146. )
  1147. {
  1148. return InterlockedIncrement(&_cRef);
  1149. }
  1150. STDMETHODIMP_ (ULONG)
  1151. COfflineFilesFolder::Release(
  1152. void
  1153. )
  1154. {
  1155. if (InterlockedDecrement(&_cRef))
  1156. return _cRef;
  1157. delete this;
  1158. return 0;
  1159. }
  1160. // IPersist methods
  1161. STDMETHODIMP
  1162. COfflineFilesFolder::GetClassID(
  1163. CLSID *pclsid
  1164. )
  1165. {
  1166. *pclsid = CLSID_OfflineFilesFolder;
  1167. return S_OK;
  1168. }
  1169. HRESULT
  1170. COfflineFilesFolder::Initialize(
  1171. LPCITEMIDLIST pidl
  1172. )
  1173. {
  1174. if (_pidl)
  1175. ILFree(_pidl);
  1176. _pidl = ILClone(pidl);
  1177. return _pidl ? S_OK : E_OUTOFMEMORY;
  1178. }
  1179. HRESULT
  1180. COfflineFilesFolder::GetCurFolder(
  1181. LPITEMIDLIST *ppidl
  1182. )
  1183. {
  1184. if (_pidl)
  1185. {
  1186. *ppidl = ILClone(_pidl);
  1187. return *ppidl ? NOERROR : E_OUTOFMEMORY;
  1188. }
  1189. *ppidl = NULL;
  1190. return S_FALSE; // success but empty
  1191. }
  1192. STDMETHODIMP
  1193. COfflineFilesFolder::ParseDisplayName(
  1194. HWND hwnd,
  1195. LPBC pbc,
  1196. LPOLESTR pDisplayName,
  1197. ULONG* pchEaten,
  1198. LPITEMIDLIST* ppidl,
  1199. ULONG *pdwAttributes
  1200. )
  1201. {
  1202. return E_NOTIMPL;
  1203. }
  1204. STDMETHODIMP
  1205. COfflineFilesFolder::EnumObjects(
  1206. HWND hwnd,
  1207. DWORD grfFlags,
  1208. IEnumIDList **ppenum
  1209. )
  1210. {
  1211. *ppenum = NULL;
  1212. HRESULT hr = E_FAIL;
  1213. COfflineFilesEnum *penum = new COfflineFilesEnum(grfFlags, this);
  1214. if (penum)
  1215. {
  1216. if (penum->IsValid())
  1217. hr = penum->QueryInterface(IID_IEnumIDList, (void **)ppenum);
  1218. penum->Release();
  1219. }
  1220. else
  1221. hr = E_OUTOFMEMORY;
  1222. return hr;
  1223. }
  1224. STDMETHODIMP
  1225. COfflineFilesFolder::BindToObject(
  1226. LPCITEMIDLIST pidl,
  1227. LPBC pbc,
  1228. REFIID riid,
  1229. void **ppv
  1230. )
  1231. {
  1232. return E_NOTIMPL;
  1233. }
  1234. STDMETHODIMP
  1235. COfflineFilesFolder::BindToStorage(
  1236. LPCITEMIDLIST pidl,
  1237. LPBC pbc,
  1238. REFIID riid,
  1239. void **ppv
  1240. )
  1241. {
  1242. return E_NOTIMPL;
  1243. }
  1244. void
  1245. COfflineFilesFolder::_GetSyncStatusString(
  1246. LPCOLID polid,
  1247. LPTSTR pszStatus,
  1248. UINT cchStatus
  1249. )
  1250. {
  1251. //
  1252. // Translate a file status into a stale state code.
  1253. // Note that the stale state codes are the same values as their
  1254. // corresponding string resource IDs. Order of this array is
  1255. // important. The first match is the message that is displayed.
  1256. // In the case of multiple bits being set, we want to display a
  1257. // message for the most "serious" reason.
  1258. //
  1259. static const struct
  1260. {
  1261. DWORD dwStatusMask;
  1262. UINT idMsg;
  1263. } rgStaleInfo[] = {
  1264. { FLAG_CSC_COPY_STATUS_SUSPECT, IDS_STALEREASON_SUSPECT },
  1265. { FLAG_CSC_COPY_STATUS_ORPHAN, IDS_STALEREASON_ORPHAN },
  1266. { FLAG_CSC_COPY_STATUS_STALE, IDS_STALEREASON_STALE },
  1267. { FLAG_CSC_COPY_STATUS_LOCALLY_CREATED, IDS_STALEREASON_LOCALLY_CREATED },
  1268. { FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED, IDS_STALEREASON_LOCALLY_MODDATA },
  1269. { FLAG_CSC_COPY_STATUS_TIME_LOCALLY_MODIFIED, IDS_STALEREASON_LOCALLY_MODTIME },
  1270. { FLAG_CSC_COPY_STATUS_ATTRIB_LOCALLY_MODIFIED, IDS_STALEREASON_LOCALLY_MODATTR },
  1271. { FLAG_CSC_COPY_STATUS_SPARSE, IDS_STALEREASON_SPARSE }
  1272. };
  1273. int idStatusText = IDS_STALEREASON_NOTSTALE; // Default is "not stale".
  1274. for (int i = 0; i < ARRAYSIZE(rgStaleInfo); i++)
  1275. {
  1276. if (0 != (rgStaleInfo[i].dwStatusMask & polid->dwStatus))
  1277. {
  1278. idStatusText = rgStaleInfo[i].idMsg;
  1279. break;
  1280. }
  1281. }
  1282. LoadString(g_hInstance, idStatusText, pszStatus, cchStatus);
  1283. }
  1284. void
  1285. COfflineFilesFolder::_GetPinStatusString(
  1286. LPCOLID polid,
  1287. LPTSTR pszStatus,
  1288. UINT cchStatus
  1289. )
  1290. {
  1291. LoadString(g_hInstance,
  1292. (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN) & polid->dwHintFlags ? IDS_FILE_PINNED : IDS_FILE_NOTPINNED,
  1293. pszStatus,
  1294. cchStatus);
  1295. }
  1296. void
  1297. COfflineFilesFolder::_GetServerStatusString(
  1298. LPCOLID polid,
  1299. LPTSTR pszStatus,
  1300. UINT cchStatus
  1301. )
  1302. {
  1303. //
  1304. // Only two possible status strings: "Online" and "Offline".
  1305. //
  1306. UINT idText = IDS_SHARE_STATUS_ONLINE;
  1307. if (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & polid->dwServerStatus)
  1308. idText = IDS_SHARE_STATUS_OFFLINE;
  1309. LoadString(g_hInstance, idText, pszStatus, cchStatus);
  1310. }
  1311. void
  1312. COfflineFilesFolder::_GetTypeString(
  1313. LPCOLID polid,
  1314. LPTSTR pszType,
  1315. UINT cchType
  1316. )
  1317. {
  1318. PCTSTR pszName;
  1319. //
  1320. // We utilize a local cache of type name information to reduce
  1321. // the number of calls to SHGetFileInfo. This speeds things up
  1322. // tremendously. The shell does something similar in DefView.
  1323. // Note that the filetype cache is a member of COfflineFilesFolder
  1324. // so that it lives only while the folder is active. The alternative
  1325. // would be to make a local static object here in this function.
  1326. // The problem with that is that once created the cache would remain
  1327. // in memory until our DLL is unloaded; which in explorer.exe is NEVER.
  1328. //
  1329. TSTR_ALIGNED_STACK_COPY( &pszName,
  1330. polid->szPath + polid->cchNameOfs );
  1331. m_FileTypeCache.GetTypeName(pszName,
  1332. polid->dwFileAttributes,
  1333. pszType,
  1334. cchType);
  1335. }
  1336. void
  1337. COfflineFilesFolder::_GetAccessString(
  1338. LPCOLID polid,
  1339. LPTSTR pszAccess,
  1340. UINT cchAccess
  1341. )
  1342. {
  1343. //
  1344. // Three strings containing the replacement text for the rgFmts[i] template.
  1345. // Note that the index value corresponds directly with the access values
  1346. // obtained from the OLID's dwStatus member. This makes the translation from
  1347. // OLID access info to text string very fast. These are small enough to
  1348. // cache. Caching saves us three LoadStrings each time.
  1349. //
  1350. // Index String resource (english)
  1351. // -------- ---------------------- ---------
  1352. // 0 IDS_ACCESS_READ "R"
  1353. // 1 IDS_ACCESS_WRITE "W"
  1354. // 2 IDS_ACCESS_READWRITE "R/W"
  1355. //
  1356. static TCHAR rgszAccess[3][4] = {0};
  1357. //
  1358. // This table lists the "mask" and "shift count" used to retrieve the access
  1359. // information from the OLID dwStatus value.
  1360. //
  1361. static const struct
  1362. {
  1363. DWORD dwMask;
  1364. DWORD dwShift;
  1365. } rgAccess[] = {{ FLAG_CSC_USER_ACCESS_MASK, FLAG_CSC_USER_ACCESS_SHIFT_COUNT },
  1366. { FLAG_CSC_GUEST_ACCESS_MASK, FLAG_CSC_GUEST_ACCESS_SHIFT_COUNT },
  1367. { FLAG_CSC_OTHER_ACCESS_MASK, FLAG_CSC_OTHER_ACCESS_SHIFT_COUNT }};
  1368. //
  1369. // These IDs specify which format string to use for a given
  1370. // item access value in the OLID's dwStatus member.
  1371. // The index into this array is calculated below from the access bits
  1372. // set for this OLID. Note that these are "message" formats defined
  1373. // in msg.mc and not resource strings. This way we eliminate the
  1374. // need for a LoadString and do everything with FormatMessage.
  1375. //
  1376. // iFmt (see below)
  1377. static const UINT rgFmts[] = { 0, // 0x0000
  1378. MSG_FMT_ACCESS_USER, // 0x0001
  1379. MSG_FMT_ACCESS_GUEST, // 0x0002
  1380. MSG_FMT_ACCESS_USERGUEST, // 0x0003
  1381. MSG_FMT_ACCESS_OTHER, // 0x0004
  1382. MSG_FMT_ACCESS_USEROTHER, // 0x0005
  1383. MSG_FMT_ACCESS_GUESTOTHER, // 0x0006
  1384. MSG_FMT_ACCESS_USERGUESTOTHER }; // 0x0007
  1385. const DWORD dwAccess = polid->dwStatus & FLAG_CSC_ACCESS_MASK;
  1386. int i;
  1387. if (TEXT('\0') == rgszAccess[0][0])
  1388. {
  1389. //
  1390. // First-time init for strings used in access text.
  1391. // This stuff happens only once.
  1392. //
  1393. const UINT rgidStr[] = { IDS_ACCESS_READ,
  1394. IDS_ACCESS_WRITE,
  1395. IDS_ACCESS_READWRITE };
  1396. //
  1397. // Load up the "R", "W", "R/W" strings.
  1398. //
  1399. for (i = 0; i < ARRAYSIZE(rgidStr); i++)
  1400. {
  1401. TraceAssert(i < ARRAYSIZE(rgszAccess));
  1402. LoadString(g_hInstance, rgidStr[i], rgszAccess[i], ARRAYSIZE(rgszAccess[i]));
  1403. }
  1404. }
  1405. //
  1406. // Build an index into rgFmts[] based on the access bits set on the olid.
  1407. //
  1408. int iFmt = 0;
  1409. if (FLAG_CSC_USER_ACCESS_MASK & dwAccess)
  1410. iFmt |= 0x0001;
  1411. if (FLAG_CSC_GUEST_ACCESS_MASK & dwAccess)
  1412. iFmt |= 0x0002;
  1413. if (FLAG_CSC_OTHER_ACCESS_MASK & dwAccess)
  1414. iFmt |= 0x0004;
  1415. *pszAccess = TEXT('\0');
  1416. if (0 != iFmt)
  1417. {
  1418. //
  1419. // Fill in the argument array passed to FormatMessage.
  1420. // Each of the elements will contain the address of one element in the
  1421. // rgszAccess[] string array.
  1422. //
  1423. LPCTSTR rgpszArgs[ARRAYSIZE(rgszAccess)] = {0};
  1424. int iArg = 0;
  1425. for (i = 0; i < ARRAYSIZE(rgpszArgs); i++)
  1426. {
  1427. int a = dwAccess & rgAccess[i].dwMask;
  1428. if (0 != a)
  1429. {
  1430. rgpszArgs[iArg++] = &rgszAccess[(a >> rgAccess[i].dwShift) - 1][0];
  1431. }
  1432. }
  1433. //
  1434. // Finally, format the message text.
  1435. //
  1436. FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
  1437. FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1438. g_hInstance,
  1439. rgFmts[iFmt],
  1440. 0,
  1441. pszAccess,
  1442. cchAccess,
  1443. (va_list *)rgpszArgs);
  1444. }
  1445. }
  1446. STDMETHODIMP
  1447. COfflineFilesFolder::CompareIDs(
  1448. LPARAM lParam,
  1449. LPCITEMIDLIST pidl1,
  1450. LPCITEMIDLIST pidl2
  1451. )
  1452. {
  1453. HRESULT hres;
  1454. LPCOLID polid1 = _Validate(pidl1);
  1455. LPCOLID polid2 = _Validate(pidl2);
  1456. if (polid1 && polid2)
  1457. {
  1458. TCHAR szStr1[MAX_PATH], szStr2[MAX_PATH];
  1459. switch (lParam & SHCIDS_COLUMNMASK)
  1460. {
  1461. case ICOL_NAME:
  1462. hres = ResultFromShort(ualstrcmpi(polid1->szPath + polid1->cchNameOfs,
  1463. polid2->szPath + polid2->cchNameOfs));
  1464. if (0 == hres)
  1465. {
  1466. //
  1467. // Since we present a "flat" view of the CSC cache,
  1468. // we can't compare only by name. We have to include
  1469. // path for items with the same name. This is because the
  1470. // shell uses column 0 as the unique identifying column for
  1471. // an ID.
  1472. //
  1473. hres = ResultFromShort(ualstrcmpi(polid1->szPath, polid2->szPath));
  1474. }
  1475. break;
  1476. case ICOL_TYPE:
  1477. _GetTypeString(polid1, szStr1, ARRAYSIZE(szStr1));
  1478. _GetTypeString(polid2, szStr2, ARRAYSIZE(szStr2));
  1479. hres = ResultFromShort(lstrcmpi(szStr1, szStr2));
  1480. break;
  1481. case ICOL_SYNCSTATUS:
  1482. _GetSyncStatusString(polid1, szStr1, ARRAYSIZE(szStr1));
  1483. _GetSyncStatusString(polid2, szStr2, ARRAYSIZE(szStr2));
  1484. hres = ResultFromShort(lstrcmpi(szStr1, szStr2));
  1485. break;
  1486. case ICOL_PINSTATUS:
  1487. _GetPinStatusString(polid1, szStr1, ARRAYSIZE(szStr1));
  1488. _GetPinStatusString(polid2, szStr2, ARRAYSIZE(szStr2));
  1489. hres = ResultFromShort(lstrcmpi(szStr1, szStr2));
  1490. break;
  1491. case ICOL_ACCESS:
  1492. _GetAccessString(polid1, szStr1, ARRAYSIZE(szStr1));
  1493. _GetAccessString(polid2, szStr2, ARRAYSIZE(szStr2));
  1494. hres = ResultFromShort(lstrcmpi(szStr1, szStr2));
  1495. break;
  1496. case ICOL_SERVERSTATUS:
  1497. _GetServerStatusString(polid1, szStr1, ARRAYSIZE(szStr1));
  1498. _GetServerStatusString(polid2, szStr2, ARRAYSIZE(szStr2));
  1499. hres = ResultFromShort(lstrcmpi(szStr1, szStr2));
  1500. break;
  1501. case ICOL_LOCATION:
  1502. hres = ResultFromShort(ualstrcmpi(polid1->szPath, polid2->szPath));
  1503. break;
  1504. case ICOL_SIZE:
  1505. if (polid1->dwFileSizeLow > polid2->dwFileSizeLow)
  1506. hres = ResultFromShort(1);
  1507. else if (polid1->dwFileSizeLow < polid2->dwFileSizeLow)
  1508. hres = ResultFromShort(-1);
  1509. else
  1510. hres = ResultFromShort(0);
  1511. break;
  1512. case ICOL_DATE:
  1513. hres = ResultFromShort(uaCompareFileTime(&polid1->ft, &polid2->ft));
  1514. break;
  1515. }
  1516. if (hres == S_OK && (lParam & SHCIDS_ALLFIELDS))
  1517. {
  1518. hres = CompareIDs(ICOL_PINSTATUS, pidl1, pidl2);
  1519. if (hres == S_OK)
  1520. {
  1521. hres = CompareIDs(ICOL_SYNCSTATUS, pidl1, pidl2);
  1522. if (hres == S_OK)
  1523. {
  1524. hres = CompareIDs(ICOL_SIZE, pidl1, pidl2);
  1525. if (hres == S_OK)
  1526. {
  1527. hres = CompareIDs(ICOL_DATE, pidl1, pidl2);
  1528. }
  1529. }
  1530. }
  1531. }
  1532. }
  1533. else
  1534. hres = E_INVALIDARG;
  1535. return hres;
  1536. }
  1537. STDMETHODIMP
  1538. COfflineFilesFolder::CreateViewObject(
  1539. HWND hwnd,
  1540. REFIID riid,
  1541. void **ppv
  1542. )
  1543. {
  1544. HRESULT hres;
  1545. if (IsEqualIID(riid, IID_IShellView))
  1546. {
  1547. COfflineFilesViewCallback *pViewCB = new COfflineFilesViewCallback(this);
  1548. if (pViewCB)
  1549. {
  1550. SFV_CREATE sSFV;
  1551. sSFV.cbSize = sizeof(sSFV);
  1552. sSFV.psvOuter = NULL;
  1553. sSFV.pshf = this;
  1554. sSFV.psfvcb = pViewCB;
  1555. hres = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
  1556. pViewCB->Release();
  1557. if (SUCCEEDED(hres))
  1558. {
  1559. //
  1560. // Save the view callback pointer so we can use it in our context menu
  1561. // handler for view notifications. Note we don't take a ref count as that
  1562. // would create a ref count cycle. The view will live as long as the
  1563. // folder does.
  1564. //
  1565. _psfvcb = pViewCB;
  1566. }
  1567. }
  1568. else
  1569. hres = E_OUTOFMEMORY;
  1570. }
  1571. else if (IsEqualIID(riid, IID_IShellDetails))
  1572. {
  1573. COfflineDetails *pDetails = new COfflineDetails(this);
  1574. if (pDetails)
  1575. {
  1576. *ppv = (IShellDetails *)pDetails;
  1577. hres = S_OK;
  1578. }
  1579. else
  1580. hres = E_OUTOFMEMORY;
  1581. }
  1582. else if (IsEqualIID(riid, IID_IDropTarget))
  1583. {
  1584. hres = COfflineFilesDropTarget::CreateInstance(hwnd, riid, ppv);
  1585. }
  1586. else if (IsEqualIID(riid, IID_IContextMenu))
  1587. {
  1588. IContextMenu *pcmCSCUI;
  1589. hres = CreateOfflineFilesContextMenu(NULL, riid, (void **)ppv);
  1590. }
  1591. else
  1592. {
  1593. *ppv = NULL;
  1594. hres = E_NOINTERFACE;
  1595. }
  1596. return hres;
  1597. }
  1598. STDMETHODIMP
  1599. COfflineFilesFolder::GetAttributesOf(
  1600. UINT cidl,
  1601. LPCITEMIDLIST* apidl,
  1602. ULONG *rgfInOut
  1603. )
  1604. {
  1605. HRESULT hr = NOERROR;
  1606. IShellFolder *psf = NULL;
  1607. ULONG ulAttrRequested = *rgfInOut;
  1608. *rgfInOut = (ULONG)-1;
  1609. for (UINT i = 0; i < cidl && SUCCEEDED(hr); i++)
  1610. {
  1611. CShellObjProxy<IShellFolder> pxy(IID_IShellFolder, (LPCOLID)*apidl++);
  1612. if (SUCCEEDED(hr = pxy.Result()))
  1613. {
  1614. ULONG ulThis = ulAttrRequested;
  1615. LPCITEMIDLIST pidlItem = pxy.ItemIDList();
  1616. hr = pxy->GetAttributesOf(1, &pidlItem, &ulThis);
  1617. if (SUCCEEDED(hr))
  1618. {
  1619. //
  1620. // Build up the intersection of attributes for all items
  1621. // in the IDList. Note that we don't allow move.
  1622. //
  1623. *rgfInOut &= (ulThis & ~SFGAO_CANMOVE);
  1624. }
  1625. }
  1626. }
  1627. return hr;
  1628. }
  1629. HRESULT
  1630. COfflineFilesFolder::GetAssociations(
  1631. LPCOLID polid,
  1632. void **ppvQueryAssociations
  1633. )
  1634. {
  1635. TraceAssert(NULL != polid);
  1636. TraceAssert(NULL != ppvQueryAssociations);
  1637. HRESULT hr = NOERROR;
  1638. *ppvQueryAssociations = NULL;
  1639. CCoInit coinit;
  1640. if (SUCCEEDED(hr = coinit.Result()))
  1641. {
  1642. CShellObjProxy<IShellFolder> pxy(IID_IShellFolder, polid);
  1643. if (SUCCEEDED(hr = pxy.Result()))
  1644. {
  1645. LPCITEMIDLIST pidlItem = pxy.ItemIDList();
  1646. hr = pxy->GetUIObjectOf(NULL, 1, &pidlItem, IID_IQueryAssociations, NULL, ppvQueryAssociations);
  1647. if (FAILED(hr))
  1648. {
  1649. // this means that the folder doesnt support
  1650. // the IQueryAssociations. so we will
  1651. // just check to see if this is a folder
  1652. ULONG rgfAttrs = SFGAO_FOLDER | SFGAO_BROWSABLE;
  1653. IQueryAssociations *pqa;
  1654. if (SUCCEEDED(pxy->GetAttributesOf(1, &pidlItem, &rgfAttrs))
  1655. && (rgfAttrs & SFGAO_FOLDER | SFGAO_BROWSABLE)
  1656. && (SUCCEEDED(AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (void **)&pqa))))
  1657. {
  1658. hr = pqa->Init(0, L"Folder", NULL, NULL);
  1659. if (SUCCEEDED(hr))
  1660. *ppvQueryAssociations = (void *)pqa;
  1661. else
  1662. pqa->Release();
  1663. }
  1664. }
  1665. }
  1666. }
  1667. return hr;
  1668. }
  1669. BOOL
  1670. COfflineFilesFolder::GetClassKey(
  1671. LPCOLID polid,
  1672. HKEY *phkeyProgID,
  1673. HKEY *phkeyBaseID
  1674. )
  1675. {
  1676. TraceAssert(NULL != polid);
  1677. BOOL bRet = FALSE;
  1678. IQueryAssociations *pqa;
  1679. if (phkeyProgID)
  1680. *phkeyProgID = NULL;
  1681. if (phkeyBaseID)
  1682. *phkeyBaseID = NULL;
  1683. if (SUCCEEDED(GetAssociations(polid, (void **)&pqa)))
  1684. {
  1685. if (phkeyProgID)
  1686. pqa->GetKey(ASSOCF_IGNOREBASECLASS, ASSOCKEY_CLASS, NULL, phkeyProgID);
  1687. if (phkeyBaseID)
  1688. pqa->GetKey(0, ASSOCKEY_BASECLASS, NULL, phkeyBaseID);
  1689. pqa->Release();
  1690. bRet = TRUE;
  1691. }
  1692. return bRet;
  1693. }
  1694. HRESULT
  1695. COfflineFilesFolder::ContextMenuCB(
  1696. IShellFolder *psf,
  1697. HWND hwndOwner,
  1698. IDataObject *pdtobj,
  1699. UINT uMsg,
  1700. WPARAM wParam,
  1701. LPARAM lParam
  1702. )
  1703. {
  1704. HRESULT hr = NOERROR;
  1705. switch(uMsg)
  1706. {
  1707. case DFM_MERGECONTEXTMENU:
  1708. //
  1709. // Return NOERROR.
  1710. // This causes the shell to add the default verbs
  1711. // (i.e. Open, Print etc) to the menu.
  1712. //
  1713. break;
  1714. case DFM_INVOKECOMMAND:
  1715. switch(wParam)
  1716. {
  1717. case DFM_CMD_DELETE:
  1718. {
  1719. IShellFolderViewCB *psfvcb = NULL;
  1720. if (SUCCEEDED(psf->QueryInterface(IID_OfflineFilesFolderViewCB, (void **)&psfvcb)))
  1721. {
  1722. CFolderDeleteHandler handler(hwndOwner, pdtobj, psfvcb);
  1723. handler.DeleteFiles();
  1724. psfvcb->Release();
  1725. }
  1726. break;
  1727. }
  1728. case DFM_CMD_COPY:
  1729. SetPreferredDropEffect(pdtobj, DROPEFFECT_COPY);
  1730. hr = S_FALSE;
  1731. break;
  1732. case DFM_CMD_PROPERTIES:
  1733. SHMultiFileProperties(pdtobj, 0);
  1734. break;
  1735. default:
  1736. hr = S_FALSE; // Execute default code.
  1737. break;
  1738. }
  1739. break;
  1740. default:
  1741. hr = E_NOTIMPL;
  1742. break;
  1743. }
  1744. return hr;
  1745. }
  1746. /*
  1747. // Used for dumping out interface requests. Uncomment if you want to use it.
  1748. //
  1749. //
  1750. LPCTSTR IIDToStr(REFIID riid, LPTSTR pszDest, UINT cchDest)
  1751. {
  1752. struct
  1753. {
  1754. const IID *piid;
  1755. LPCTSTR s;
  1756. } rgMap[] = { { &IID_IDataObject, TEXT("IID_IDataObject") },
  1757. { &IID_IUnknown, TEXT("IID_IUnknown") },
  1758. { &IID_IContextMenu, TEXT("IID_IContextMenu") },
  1759. { &IID_IExtractIconA, TEXT("IID_IExtractIconA") },
  1760. { &IID_IExtractIconW, TEXT("IID_IExtractIconW") },
  1761. { &IID_IExtractImage, TEXT("IID_IExtractImage") },
  1762. { &IID_IPersistFolder2, TEXT("IID_IPersistFolder2") },
  1763. { &IID_IQueryInfo, TEXT("IID_IQueryInfo") },
  1764. { &IID_IDropTarget, TEXT("IID_IDropTarget") },
  1765. { &IID_IQueryAssociations, TEXT("IID_IQueryAssociations") }
  1766. };
  1767. StringFromGUID2(riid, pszDest, cchDest);
  1768. for (int i = 0; i < ARRAYSIZE(rgMap); i++)
  1769. {
  1770. if (riid == *(rgMap[i].piid))
  1771. {
  1772. lstrcpyn(pszDest, rgMap[i].s, cchDest);
  1773. break;
  1774. }
  1775. }
  1776. return pszDest;
  1777. }
  1778. */
  1779. STDMETHODIMP
  1780. COfflineFilesFolder::GetUIObjectOf(
  1781. HWND hwnd,
  1782. UINT cidl,
  1783. LPCITEMIDLIST *ppidl,
  1784. REFIID riid,
  1785. UINT* prgfReserved,
  1786. void **ppv
  1787. )
  1788. {
  1789. HRESULT hr;
  1790. if (IID_IDataObject == riid)
  1791. {
  1792. LPITEMIDLIST pidlOfflineFiles;
  1793. hr = COfflineFilesFolder::CreateIDList(&pidlOfflineFiles);
  1794. if (SUCCEEDED(hr))
  1795. {
  1796. hr = COfflineItemsData::CreateInstance((IDataObject **)ppv,
  1797. pidlOfflineFiles,
  1798. cidl,
  1799. ppidl,
  1800. hwnd);
  1801. if (SUCCEEDED(hr))
  1802. {
  1803. SetPreferredDropEffect((IDataObject *)*ppv, DROPEFFECT_COPY);
  1804. }
  1805. ILFree(pidlOfflineFiles);
  1806. }
  1807. }
  1808. else if (riid == IID_IContextMenu)
  1809. {
  1810. HKEY hkeyBaseProgID = NULL;
  1811. HKEY hkeyProgID = NULL;
  1812. HKEY hkeyAllFileSys = NULL;
  1813. //
  1814. // Get the hkeyProgID and hkeyBaseProgID from the first item.
  1815. //
  1816. GetClassKey((LPCOLID)*ppidl, &hkeyProgID, &hkeyBaseProgID);
  1817. //
  1818. // Pick up "Send To..."
  1819. //
  1820. RegOpenKeyEx(HKEY_CLASSES_ROOT,
  1821. TEXT("AllFilesystemObjects"),
  1822. 0,
  1823. KEY_READ,
  1824. &hkeyAllFileSys);
  1825. LPITEMIDLIST pidlOfflineFilesFolder;
  1826. hr = COfflineFilesFolder::CreateIDList(&pidlOfflineFilesFolder);
  1827. if (SUCCEEDED(hr))
  1828. {
  1829. HKEY rgClassKeys[] = { hkeyProgID, hkeyBaseProgID, hkeyAllFileSys };
  1830. hr = CDefFolderMenu_Create2(pidlOfflineFilesFolder,
  1831. hwnd,
  1832. cidl,
  1833. ppidl,
  1834. this,
  1835. COfflineFilesFolder::ContextMenuCB,
  1836. ARRAYSIZE(rgClassKeys),
  1837. rgClassKeys,
  1838. (IContextMenu **)ppv);
  1839. ILFree(pidlOfflineFilesFolder);
  1840. }
  1841. if (NULL != hkeyBaseProgID)
  1842. RegCloseKey(hkeyBaseProgID);
  1843. if (NULL != hkeyProgID)
  1844. RegCloseKey(hkeyProgID);
  1845. if (NULL != hkeyAllFileSys)
  1846. RegCloseKey(hkeyAllFileSys);
  1847. }
  1848. else if (1 == cidl)
  1849. {
  1850. CShellObjProxy<IShellFolder> pxy(IID_IShellFolder, (LPCOLID)*ppidl);
  1851. if (SUCCEEDED(hr = pxy.Result()))
  1852. {
  1853. //
  1854. // Forward single-item selection to the filesystem implementation.
  1855. //
  1856. LPCITEMIDLIST pidlItem = pxy.ItemIDList();
  1857. hr = pxy->GetUIObjectOf(hwnd, 1, &pidlItem, riid, prgfReserved, ppv);
  1858. }
  1859. }
  1860. else if (0 == cidl)
  1861. {
  1862. hr = E_INVALIDARG;
  1863. }
  1864. else
  1865. {
  1866. *ppv = NULL;
  1867. hr = E_FAIL;
  1868. }
  1869. return hr;
  1870. }
  1871. STDMETHODIMP
  1872. COfflineFilesFolder::GetDisplayNameOf(
  1873. LPCITEMIDLIST pidl,
  1874. DWORD uFlags,
  1875. STRRET *pName
  1876. )
  1877. {
  1878. TraceAssert(NULL != pidl);
  1879. HRESULT hres = E_INVALIDARG;
  1880. LPCOLID polid = _Validate(pidl);
  1881. if (polid)
  1882. {
  1883. if (uFlags & SHGDN_FORPARSING)
  1884. {
  1885. TCHAR szPath[MAX_PATH];
  1886. OLID_GetFullPath(polid, szPath, ARRAYSIZE(szPath));
  1887. hres = StringToStrRet(szPath, pName);
  1888. }
  1889. else
  1890. {
  1891. CShellObjProxy<IShellFolder> pxy(IID_IShellFolder, polid);
  1892. if (SUCCEEDED(hres = pxy.Result()))
  1893. {
  1894. hres = pxy->GetDisplayNameOf(pxy.ItemIDList(), uFlags, pName);
  1895. }
  1896. }
  1897. }
  1898. return hres;
  1899. }
  1900. STDMETHODIMP
  1901. COfflineFilesFolder::SetNameOf(
  1902. HWND hwnd,
  1903. LPCITEMIDLIST pidl,
  1904. LPCOLESTR pName,
  1905. DWORD uFlags,
  1906. LPITEMIDLIST *ppidlOut
  1907. )
  1908. {
  1909. HRESULT hr;
  1910. CShellObjProxy<IShellFolder> pxy(IID_IShellFolder, _Validate(pidl));
  1911. if (SUCCEEDED(hr = pxy.Result()))
  1912. {
  1913. hr = pxy->SetNameOf(hwnd, pxy.ItemIDList(), pName, uFlags, ppidlOut);
  1914. }
  1915. return hr;
  1916. }
  1917. //
  1918. // Forward IShellIcon methods to parent filesystem folder.
  1919. //
  1920. HRESULT
  1921. COfflineFilesFolder::GetIconOf(
  1922. LPCITEMIDLIST pidl,
  1923. UINT gil,
  1924. int *pnIcon
  1925. )
  1926. {
  1927. TraceAssert(NULL != pidl);
  1928. HRESULT hr;
  1929. CShellObjProxy<IShellIcon> pxy(IID_IShellIcon, _Validate(pidl));
  1930. if (SUCCEEDED(hr = pxy.Result()))
  1931. {
  1932. hr = pxy->GetIconOf(pxy.ItemIDList(), gil, pnIcon);
  1933. }
  1934. return hr;
  1935. }
  1936. //
  1937. // Defer IShellIconOverlay methods to parent filesystem folder.
  1938. //
  1939. HRESULT
  1940. COfflineFilesFolder::GetOverlayIndex(
  1941. LPCITEMIDLIST pidl,
  1942. int *pIndex
  1943. )
  1944. {
  1945. TraceAssert(NULL != pidl);
  1946. HRESULT hr;
  1947. CShellObjProxy<IShellIconOverlay> pxy(IID_IShellIconOverlay, _Validate(pidl));
  1948. if (SUCCEEDED(hr = pxy.Result()))
  1949. {
  1950. hr = pxy->GetOverlayIndex(pxy.ItemIDList(), pIndex);
  1951. }
  1952. return hr;
  1953. }
  1954. //
  1955. // Defer IShellIconOverlay methods to parent filesystem folder.
  1956. //
  1957. HRESULT
  1958. COfflineFilesFolder::GetOverlayIconIndex(
  1959. LPCITEMIDLIST pidl,
  1960. int * pIconIndex
  1961. )
  1962. {
  1963. TraceAssert(NULL != pidl);
  1964. HRESULT hr;
  1965. CShellObjProxy<IShellIconOverlay> pxy(IID_IShellIconOverlay, _Validate(pidl));
  1966. if (SUCCEEDED(hr = pxy.Result()))
  1967. {
  1968. hr = pxy->GetOverlayIconIndex(pxy.ItemIDList(), pIconIndex);
  1969. }
  1970. return hr;
  1971. }
  1972. //
  1973. // Static member function for creating and opening the offline files folder.
  1974. //
  1975. INT
  1976. COfflineFilesFolder::Open( // [static]
  1977. void
  1978. )
  1979. {
  1980. INT iReturn = 0;
  1981. if (CConfig::GetSingleton().NoCacheViewer())
  1982. {
  1983. CscMessageBox(NULL,
  1984. MB_OK | MB_ICONINFORMATION,
  1985. g_hInstance,
  1986. IDS_ERR_POLICY_NOVIEWCACHE);
  1987. iReturn = -1;
  1988. }
  1989. else
  1990. {
  1991. SHELLEXECUTEINFO shei = { 0 };
  1992. shei.cbSize = sizeof(shei);
  1993. shei.fMask = SEE_MASK_IDLIST | SEE_MASK_INVOKEIDLIST;
  1994. shei.nShow = SW_SHOWNORMAL;
  1995. if (SUCCEEDED(COfflineFilesFolder::CreateIDList((LPITEMIDLIST *)(&shei.lpIDList))))
  1996. {
  1997. ShellExecuteEx(&shei);
  1998. ILFree((LPITEMIDLIST)(shei.lpIDList));
  1999. }
  2000. }
  2001. return iReturn;
  2002. }
  2003. //
  2004. // Static member function for creating the folder's IDList.
  2005. //
  2006. HRESULT
  2007. COfflineFilesFolder::CreateIDList( // [static]
  2008. LPITEMIDLIST *ppidl
  2009. )
  2010. {
  2011. TraceAssert(NULL != ppidl);
  2012. IShellFolder *psf;
  2013. HRESULT hr = SHGetDesktopFolder(&psf);
  2014. if (SUCCEEDED(hr))
  2015. {
  2016. IBindCtx *pbc;
  2017. hr = CreateBindCtx(0, &pbc);
  2018. if (SUCCEEDED(hr))
  2019. {
  2020. BIND_OPTS bo;
  2021. memset(&bo, 0, sizeof(bo));
  2022. bo.cbStruct = sizeof(bo);
  2023. bo.grfFlags = BIND_JUSTTESTEXISTENCE;
  2024. bo.grfMode = STGM_CREATE;
  2025. pbc->SetBindOptions(&bo);
  2026. WCHAR wszPath[80] = L"::";
  2027. StringFromGUID2(CLSID_OfflineFilesFolder,
  2028. &wszPath[2],
  2029. sizeof(wszPath) - (2 * sizeof(WCHAR)));
  2030. hr = psf->ParseDisplayName(NULL, pbc, wszPath, NULL, ppidl, NULL);
  2031. pbc->Release();
  2032. }
  2033. psf->Release();
  2034. }
  2035. return hr;
  2036. }
  2037. //
  2038. // Static function for creating a link to the folder on the desktop.
  2039. //
  2040. HRESULT
  2041. COfflineFilesFolder::CreateLinkOnDesktop( // [static]
  2042. HWND hwndParent
  2043. )
  2044. {
  2045. IShellLink* psl;
  2046. CCoInit coinit;
  2047. HRESULT hr = coinit.Result();
  2048. if (SUCCEEDED(hr))
  2049. {
  2050. hr = CoCreateInstance(CLSID_ShellLink,
  2051. NULL,
  2052. CLSCTX_INPROC_SERVER,
  2053. IID_IShellLink,
  2054. (void **)&psl);
  2055. if (SUCCEEDED(hr))
  2056. {
  2057. LPITEMIDLIST pidl = NULL;
  2058. hr = COfflineFilesFolder::CreateIDList(&pidl);
  2059. if (SUCCEEDED(hr))
  2060. {
  2061. hr = psl->SetIDList(pidl);
  2062. if (SUCCEEDED(hr))
  2063. {
  2064. TCHAR szLinkTitle[80] = { 0 };
  2065. LoadString(g_hInstance, IDS_FOLDER_LINK_NAME, szLinkTitle, ARRAYSIZE(szLinkTitle));
  2066. psl->SetDescription(szLinkTitle);
  2067. IPersistFile* ppf;
  2068. hr = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
  2069. if (SUCCEEDED(hr))
  2070. {
  2071. TCHAR szLinkPath[MAX_PATH];
  2072. hr = SHGetSpecialFolderPath(hwndParent, szLinkPath, CSIDL_DESKTOPDIRECTORY, FALSE) ? S_OK : E_FAIL;
  2073. if (SUCCEEDED(hr))
  2074. {
  2075. TCHAR szLinkFileName[80];
  2076. LoadStringW(g_hInstance, IDS_FOLDER_LINK_NAME, szLinkFileName, ARRAYSIZE(szLinkFileName));
  2077. lstrcat(szLinkFileName, TEXT(".LNK"));
  2078. PathAppend(szLinkPath, szLinkFileName);
  2079. hr = ppf->Save(szLinkPath, TRUE);
  2080. if (SUCCEEDED(hr))
  2081. {
  2082. //
  2083. // Record that we've created a folder shortcut on the
  2084. // desktop. This is used to minimize the number of
  2085. // times we look for the shortcut on the desktop.
  2086. // DeleteOfflineFilesFolderLink_PerfSensitive() will look
  2087. // for this value to avoid unnecessary scans of the desktop
  2088. // when looking for our LINK file.
  2089. //
  2090. DWORD dwValue = 1;
  2091. DWORD cbValue = sizeof(dwValue);
  2092. SHSetValue(HKEY_CURRENT_USER,
  2093. REGSTR_KEY_OFFLINEFILES,
  2094. REGSTR_VAL_FOLDERSHORTCUTCREATED,
  2095. REG_DWORD,
  2096. &dwValue,
  2097. cbValue);
  2098. }
  2099. }
  2100. ppf->Release();
  2101. }
  2102. }
  2103. ILFree(pidl);
  2104. }
  2105. psl->Release();
  2106. }
  2107. }
  2108. return hr;
  2109. }
  2110. //
  2111. // Static function for determining if there's a link to the offline files
  2112. // folder sitting on the user's desktop.
  2113. //
  2114. HRESULT
  2115. COfflineFilesFolder::IsLinkOnDesktop( // [static]
  2116. HWND hwndParent,
  2117. LPTSTR pszPathOut,
  2118. UINT cchPathOut
  2119. )
  2120. {
  2121. TCHAR szDesktop[MAX_PATH];
  2122. HRESULT hr = SHGetSpecialFolderPath(hwndParent, szDesktop, CSIDL_DESKTOPDIRECTORY, FALSE) ? S_OK : E_FAIL;
  2123. if (SUCCEEDED(hr))
  2124. {
  2125. hr = S_FALSE; // Assume not found.
  2126. TCHAR szPath[MAX_PATH];
  2127. PathCombine(szPath, szDesktop, TEXT("*.LNK"));
  2128. WIN32_FIND_DATA fd;
  2129. HANDLE hFind = FindFirstFile(szPath, &fd);
  2130. if (INVALID_HANDLE_VALUE != hFind)
  2131. {
  2132. do
  2133. {
  2134. PathRemoveFileSpec(szPath);
  2135. PathAppend(szPath, fd.cFileName);
  2136. hr = IsOurLink(szPath);
  2137. if (S_OK == hr)
  2138. {
  2139. if (NULL != pszPathOut)
  2140. {
  2141. lstrcpyn(pszPathOut, szPath, cchPathOut);
  2142. }
  2143. break;
  2144. }
  2145. }
  2146. while(FindNextFile(hFind, &fd));
  2147. FindClose(hFind);
  2148. }
  2149. }
  2150. return hr;
  2151. }
  2152. //
  2153. // Given a link file path, determine if it's a link to the
  2154. // offline files folder.
  2155. //
  2156. HRESULT
  2157. COfflineFilesFolder::IsOurLink( // [static]
  2158. LPCTSTR pszFile
  2159. )
  2160. {
  2161. IShellLink *psl;
  2162. CCoInit coinit;
  2163. HRESULT hr = coinit.Result();
  2164. if (SUCCEEDED(hr))
  2165. {
  2166. hr = CoCreateInstance(CLSID_ShellLink,
  2167. NULL,
  2168. CLSCTX_INPROC_SERVER,
  2169. IID_IShellLink,
  2170. (void **)&psl);
  2171. if (SUCCEEDED(hr))
  2172. {
  2173. IPersistFile *ppf;
  2174. hr = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
  2175. if (SUCCEEDED(hr))
  2176. {
  2177. hr = ppf->Load(pszFile, STGM_DIRECT);
  2178. if (SUCCEEDED(hr))
  2179. {
  2180. LPITEMIDLIST pidlLink;
  2181. hr = psl->GetIDList(&pidlLink);
  2182. if (SUCCEEDED(hr))
  2183. {
  2184. hr = COfflineFilesFolder::IdentifyIDList(pidlLink);
  2185. ILFree(pidlLink);
  2186. }
  2187. }
  2188. ppf->Release();
  2189. }
  2190. psl->Release();
  2191. }
  2192. }
  2193. return hr;
  2194. }
  2195. //
  2196. // Determines if a given IDList is the IDList of the
  2197. // offline files folder.
  2198. //
  2199. // Returns:
  2200. //
  2201. // S_OK = It's our IDList.
  2202. // S_FALSE = It's not our IDList.
  2203. //
  2204. HRESULT
  2205. COfflineFilesFolder::IdentifyIDList( // [static]
  2206. LPCITEMIDLIST pidl
  2207. )
  2208. {
  2209. IShellFolder *psf;
  2210. HRESULT hr = SHGetDesktopFolder(&psf);
  2211. if (SUCCEEDED(hr))
  2212. {
  2213. IBindCtx *pbc;
  2214. hr = CreateBindCtx(0, &pbc);
  2215. if (SUCCEEDED(hr))
  2216. {
  2217. STRRET strret;
  2218. BIND_OPTS bo;
  2219. memset(&bo, 0, sizeof(bo));
  2220. bo.cbStruct = sizeof(bo);
  2221. bo.grfFlags = BIND_JUSTTESTEXISTENCE;
  2222. bo.grfMode = STGM_CREATE;
  2223. pbc->SetBindOptions(&bo);
  2224. hr = psf->GetDisplayNameOf(pidl,
  2225. SHGDN_FORPARSING,
  2226. &strret);
  2227. if (SUCCEEDED(hr))
  2228. {
  2229. TCHAR szIDList[80];
  2230. TCHAR szPath[80] = TEXT("::");
  2231. StrRetToBuf(&strret, pidl, szIDList, ARRAYSIZE(szIDList));
  2232. StringFromGUID2(CLSID_OfflineFilesFolder,
  2233. &szPath[2],
  2234. sizeof(szPath) - (2 * sizeof(TCHAR)));
  2235. if (0 == lstrcmpi(szIDList, szPath))
  2236. hr = S_OK;
  2237. else
  2238. hr = S_FALSE;
  2239. }
  2240. pbc->Release();
  2241. }
  2242. psf->Release();
  2243. }
  2244. return hr;
  2245. }
  2246. HRESULT
  2247. COfflineFilesFolder::GetFolder( // [static]
  2248. IShellFolder **ppsf
  2249. )
  2250. {
  2251. TraceAssert(NULL != ppsf);
  2252. *ppsf = NULL;
  2253. IShellFolder *psfDesktop;
  2254. HRESULT hr = SHGetDesktopFolder(&psfDesktop);
  2255. if (SUCCEEDED(hr))
  2256. {
  2257. LPITEMIDLIST pidlOfflineFiles;
  2258. hr = COfflineFilesFolder::CreateIDList(&pidlOfflineFiles);
  2259. if (SUCCEEDED(hr))
  2260. {
  2261. hr = psfDesktop->BindToObject(pidlOfflineFiles, NULL, IID_IShellFolder, (void **)ppsf);
  2262. ILFree(pidlOfflineFiles);
  2263. }
  2264. psfDesktop->Release();
  2265. }
  2266. return hr;
  2267. }
  2268. //
  2269. // Generate a new OLID from a UNC path.
  2270. //
  2271. HRESULT
  2272. COfflineFilesFolder::OLID_CreateFromUNCPath( // [static]
  2273. LPCTSTR pszPath,
  2274. const WIN32_FIND_DATA *pfd,
  2275. DWORD dwStatus,
  2276. DWORD dwPinCount,
  2277. DWORD dwHintFlags,
  2278. DWORD dwServerStatus,
  2279. LPOLID *ppolid
  2280. )
  2281. {
  2282. HRESULT hr = E_OUTOFMEMORY;
  2283. int cchPath = lstrlen(pszPath) + 1;
  2284. int cbIDL = sizeof(OLID) + (cchPath * sizeof(TCHAR)) + sizeof(WORD); // NULL terminator WORD
  2285. WIN32_FIND_DATA fd;
  2286. if (NULL == pfd)
  2287. {
  2288. //
  2289. // Caller didn't provide a finddata block. Use a default one
  2290. // with all zeros.
  2291. //
  2292. ZeroMemory(&fd, sizeof(fd));
  2293. pfd = &fd;
  2294. }
  2295. *ppolid = NULL;
  2296. LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cbIDL);
  2297. if (NULL != pidl)
  2298. {
  2299. OLID *polid = (OLID *)pidl;
  2300. memset(pidl, 0, cbIDL);
  2301. polid->cb = (USHORT)(cbIDL - sizeof(WORD));
  2302. polid->uSig = OLID_SIG;
  2303. polid->cbFixed = sizeof(OLID);
  2304. polid->cchNameOfs = (DWORD)(PathFindFileName(pszPath) - pszPath);
  2305. polid->dwStatus = dwStatus;
  2306. polid->dwPinCount = dwPinCount;
  2307. polid->dwHintFlags = dwHintFlags;
  2308. polid->dwServerStatus = dwServerStatus;
  2309. polid->dwFileAttributes = pfd->dwFileAttributes;
  2310. polid->dwFileSizeLow = pfd->nFileSizeLow;
  2311. polid->dwFileSizeHigh = pfd->nFileSizeHigh;
  2312. polid->ft = pfd->ftLastWriteTime;
  2313. lstrcpy(polid->szPath, pszPath);
  2314. OLID_SplitPathAndName(polid);
  2315. *ppolid = polid;
  2316. hr = NOERROR;
  2317. }
  2318. return hr;
  2319. }
  2320. void
  2321. COfflineFilesFolder::OLID_GetWin32FindData( // [static]
  2322. LPCOLID polid,
  2323. WIN32_FIND_DATA *pfd
  2324. )
  2325. {
  2326. TraceAssert(NULL != polid);
  2327. TraceAssert(NULL != pfd);
  2328. ZeroMemory(pfd, sizeof(*pfd));
  2329. pfd->dwFileAttributes = polid->dwFileAttributes;
  2330. pfd->nFileSizeLow = polid->dwFileSizeLow;
  2331. pfd->nFileSizeHigh = polid->dwFileSizeHigh;
  2332. pfd->ftLastWriteTime = polid->ft;
  2333. OLID_GetFileName(polid, pfd->cFileName, ARRAYSIZE(pfd->cFileName));
  2334. }
  2335. //
  2336. // Retrieve the full path (including filename) from an OLID.
  2337. //
  2338. LPCTSTR
  2339. COfflineFilesFolder::OLID_GetFullPath( // [static]
  2340. LPCOLID polid,
  2341. LPTSTR pszPath,
  2342. UINT cchPath
  2343. )
  2344. {
  2345. PCTSTR pszInPath;
  2346. PCTSTR pszInName;
  2347. TraceAssert(NULL != polid);
  2348. TraceAssert(NULL != pszPath);
  2349. TSTR_ALIGNED_STACK_COPY( &pszInPath, polid->szPath );
  2350. TSTR_ALIGNED_STACK_COPY( &pszInName,
  2351. polid->szPath + polid->cchNameOfs );
  2352. PathCombine(pszPath, pszInPath, pszInName);
  2353. return pszPath;
  2354. }
  2355. //
  2356. // Retrieve only the path portion of the OLID.
  2357. //
  2358. LPCTSTR
  2359. COfflineFilesFolder::OLID_GetPath( // [static]
  2360. LPCOLID polid,
  2361. LPTSTR pszPath,
  2362. UINT cchPath
  2363. )
  2364. {
  2365. TraceAssert(NULL != polid);
  2366. TraceAssert(NULL != pszPath);
  2367. ualstrcpyn(pszPath, polid->szPath, cchPath);
  2368. return pszPath;
  2369. }
  2370. //
  2371. // Retrieve only the filename portion of the OLID.
  2372. //
  2373. LPCTSTR
  2374. COfflineFilesFolder::OLID_GetFileName( // [static]
  2375. LPCOLID polid,
  2376. LPTSTR pszName,
  2377. UINT cchName
  2378. )
  2379. {
  2380. TraceAssert(NULL != polid);
  2381. TraceAssert(NULL != pszName);
  2382. ualstrcpyn(pszName, polid->szPath + polid->cchNameOfs, cchName);
  2383. return pszName;
  2384. }
  2385. //
  2386. // Restore the backslash separating the path from the filename
  2387. // in the OLID.
  2388. //
  2389. void
  2390. COfflineFilesFolder::OLID_CombinePathAndName( // [static]
  2391. LPOLID polid
  2392. )
  2393. {
  2394. TraceAssert(NULL != polid);
  2395. TraceAssert(COfflineFilesFolder::ValidateIDList((LPCITEMIDLIST)polid));
  2396. if (0 < polid->cchNameOfs)
  2397. polid->szPath[polid->cchNameOfs - 1] = TEXT('\\');
  2398. }
  2399. //
  2400. // Remove the backslash separating the path from the filename
  2401. // in the OLID.
  2402. //
  2403. void
  2404. COfflineFilesFolder::OLID_SplitPathAndName( // [static]
  2405. LPOLID polid
  2406. )
  2407. {
  2408. TraceAssert(NULL != polid);
  2409. TraceAssert(COfflineFilesFolder::ValidateIDList((LPCITEMIDLIST)polid));
  2410. if (0 < polid->cchNameOfs)
  2411. polid->szPath[polid->cchNameOfs - 1] = TEXT('\0');
  2412. }
  2413. //
  2414. // Given an OLID this function creates a fully-qualified simple
  2415. // IDList for use by the shell. The returned IDList is relative to the
  2416. // desktop folder.
  2417. //
  2418. HRESULT
  2419. COfflineFilesFolder::OLID_CreateSimpleIDList( // [static]
  2420. LPCOLID polid,
  2421. LPITEMIDLIST *ppidlOut
  2422. )
  2423. {
  2424. TraceAssert(NULL != polid);
  2425. TraceAssert(NULL != ppidlOut);
  2426. TraceAssert(COfflineFilesFolder::ValidateIDList((LPCITEMIDLIST)polid));
  2427. WIN32_FIND_DATA fd;
  2428. TCHAR szFullPath[MAX_PATH];
  2429. OLID_GetWin32FindData(polid, &fd);
  2430. OLID_GetFullPath(polid, szFullPath, ARRAYSIZE(szFullPath));
  2431. return SHSimpleIDListFromFindData(szFullPath, &fd, ppidlOut);
  2432. }
  2433. HRESULT
  2434. COfflineFilesFolder::OLID_Bind( // [static]
  2435. LPCOLID polid,
  2436. REFIID riid,
  2437. void **ppv,
  2438. LPITEMIDLIST *ppidlFull,
  2439. LPCITEMIDLIST *ppidlItem
  2440. )
  2441. {
  2442. *ppidlFull = NULL;
  2443. *ppidlItem = NULL;
  2444. HRESULT hr = OLID_CreateSimpleIDList(polid, ppidlFull);
  2445. if (SUCCEEDED(hr))
  2446. {
  2447. hr = ::BindToIDListParent((LPCITEMIDLIST)*ppidlFull, riid, ppv, ppidlItem);
  2448. }
  2449. return hr;
  2450. }
  2451. //-----------------------------------------------------------------------------
  2452. // COfflineFilesDropTarget
  2453. //-----------------------------------------------------------------------------
  2454. COfflineFilesDropTarget::COfflineFilesDropTarget(
  2455. HWND hwnd
  2456. ) : m_cRef(1),
  2457. m_hwnd(hwnd),
  2458. m_pcm(NULL),
  2459. m_bIsOurData(false)
  2460. {
  2461. }
  2462. COfflineFilesDropTarget::~COfflineFilesDropTarget(
  2463. void
  2464. )
  2465. {
  2466. DoRelease(m_pcm);
  2467. }
  2468. HRESULT
  2469. COfflineFilesDropTarget::QueryInterface(
  2470. REFIID riid,
  2471. void **ppv
  2472. )
  2473. {
  2474. static const QITAB qit[] = {
  2475. QITABENT(COfflineFilesDropTarget, IDropTarget),
  2476. { 0 },
  2477. };
  2478. return QISearch(this, qit, riid, ppv);
  2479. }
  2480. ULONG
  2481. COfflineFilesDropTarget::AddRef(
  2482. void
  2483. )
  2484. {
  2485. return InterlockedIncrement(&m_cRef);
  2486. }
  2487. ULONG
  2488. COfflineFilesDropTarget::Release(
  2489. void
  2490. )
  2491. {
  2492. if (InterlockedDecrement(&m_cRef))
  2493. return m_cRef;
  2494. delete this;
  2495. return 0;
  2496. }
  2497. HRESULT
  2498. COfflineFilesDropTarget::DragEnter(
  2499. IDataObject *pDataObject,
  2500. DWORD grfKeyState,
  2501. POINTL pt,
  2502. DWORD *pdwEffect
  2503. )
  2504. {
  2505. HRESULT hr;
  2506. *pdwEffect = DROPEFFECT_NONE;
  2507. // The context menu handler has logic to check whether
  2508. // the selected files are cacheable, etc. It only adds
  2509. // verbs to the context menu when it makes sense to do so.
  2510. // We can make use of this by calling QueryContextMenu
  2511. // here and seeing whether anything is added to the menu.
  2512. DoRelease(m_pcm);
  2513. if (!(m_bIsOurData = IsOurDataObject(pDataObject)))
  2514. {
  2515. hr = CreateOfflineFilesContextMenu(pDataObject, IID_IContextMenu, (void **)&m_pcm);
  2516. if (SUCCEEDED(hr))
  2517. {
  2518. HMENU hmenu = CreateMenu();
  2519. if (hmenu)
  2520. {
  2521. hr = m_pcm->QueryContextMenu(hmenu, 0, 0, 100, 0);
  2522. DestroyMenu(hmenu);
  2523. }
  2524. else
  2525. hr = E_OUTOFMEMORY;
  2526. // Did the context menu add anything?
  2527. if (FAILED(hr) || ResultFromShort(0) == hr)
  2528. {
  2529. // No, release m_pcm and set it to NULL
  2530. DoRelease(m_pcm);
  2531. }
  2532. else
  2533. {
  2534. // Yes
  2535. *pdwEffect |= DROPEFFECT_COPY;
  2536. }
  2537. }
  2538. }
  2539. return NOERROR;
  2540. }
  2541. HRESULT
  2542. COfflineFilesDropTarget::DragOver(
  2543. DWORD grfKeyState,
  2544. POINTL pt,
  2545. DWORD *pdwEffect
  2546. )
  2547. {
  2548. *pdwEffect = DROPEFFECT_NONE;
  2549. if (m_pcm && !m_bIsOurData)
  2550. *pdwEffect = DROPEFFECT_COPY;
  2551. return NOERROR;
  2552. }
  2553. HRESULT
  2554. COfflineFilesDropTarget::DragLeave(
  2555. void
  2556. )
  2557. {
  2558. DoRelease(m_pcm);
  2559. return NOERROR;
  2560. }
  2561. HRESULT
  2562. COfflineFilesDropTarget::Drop(
  2563. IDataObject *pDataObject,
  2564. DWORD grfKeyState,
  2565. POINTL pt,
  2566. DWORD *pdwEffect
  2567. )
  2568. {
  2569. HRESULT hr = E_FAIL;
  2570. *pdwEffect = DROPEFFECT_NONE;
  2571. if (m_pcm && !m_bIsOurData)
  2572. {
  2573. CMINVOKECOMMANDINFO cmi;
  2574. ZeroMemory(&cmi, sizeof(cmi));
  2575. cmi.cbSize = sizeof(cmi);
  2576. cmi.hwnd = m_hwnd;
  2577. cmi.lpVerb = STR_PIN_VERB;
  2578. cmi.nShow = SW_SHOWNORMAL;
  2579. hr = m_pcm->InvokeCommand(&cmi);
  2580. if (SUCCEEDED(hr))
  2581. {
  2582. *pdwEffect = DROPEFFECT_COPY;
  2583. }
  2584. }
  2585. DoRelease(m_pcm);
  2586. return hr;
  2587. }
  2588. HRESULT
  2589. COfflineFilesDropTarget::CreateInstance(
  2590. HWND hwnd,
  2591. REFIID riid,
  2592. void **ppv
  2593. )
  2594. {
  2595. HRESULT hr = E_NOINTERFACE;
  2596. *ppv = NULL;
  2597. COfflineFilesDropTarget* pdt = new COfflineFilesDropTarget(hwnd);
  2598. if (NULL != pdt)
  2599. {
  2600. hr = pdt->QueryInterface(riid, ppv);
  2601. pdt->Release();
  2602. }
  2603. else
  2604. hr = E_OUTOFMEMORY;
  2605. return hr;
  2606. }
  2607. //
  2608. // If the source of the data is the Offline Files folder the data object
  2609. // will support the "Data Source Clsid" clipboard format and the CLSID
  2610. // will be CLSID_OfflineFilesFolder.
  2611. // Checking for this is how we keep from dropping our own data on ourselves.
  2612. //
  2613. bool
  2614. COfflineFilesDropTarget::IsOurDataObject(
  2615. IDataObject *pdtobj
  2616. )
  2617. {
  2618. TraceAssert(NULL != pdtobj);
  2619. bool bIsOurData = false;
  2620. CLIPFORMAT cfSrcClsid = (CLIPFORMAT)RegisterClipboardFormat(c_szCFDataSrcClsid);
  2621. FORMATETC fe = { cfSrcClsid, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  2622. STGMEDIUM medium;
  2623. HRESULT hr = pdtobj->GetData(&fe, &medium);
  2624. if (SUCCEEDED(hr))
  2625. {
  2626. const CLSID *pclsid = (const CLSID *)GlobalLock(medium.hGlobal);
  2627. if (pclsid)
  2628. {
  2629. bIsOurData = boolify(IsEqualCLSID(CLSID_OfflineFilesFolder, *pclsid));
  2630. GlobalUnlock(medium.hGlobal);
  2631. }
  2632. ReleaseStgMedium(&medium);
  2633. }
  2634. return bIsOurData;
  2635. }
  2636. //-----------------------------------------------------------------------------
  2637. // COfflineFilesViewCallback
  2638. //-----------------------------------------------------------------------------
  2639. COfflineFilesViewCallback::COfflineFilesViewCallback(
  2640. COfflineFilesFolder *pfolder
  2641. ) : _cRef(1)
  2642. {
  2643. m_hwnd = NULL;
  2644. _psfv = NULL;
  2645. _pfolder = pfolder;
  2646. _pfolder->AddRef();
  2647. InitializeCriticalSection(&m_cs);
  2648. }
  2649. COfflineFilesViewCallback::~COfflineFilesViewCallback(
  2650. void
  2651. )
  2652. {
  2653. _pfolder->Release();
  2654. if (_psfv)
  2655. _psfv->Release();
  2656. //
  2657. // Since the folder cache is global we don't want it taking up space while the
  2658. // Offline Folders view isn't active. Clear it when the view callback is
  2659. // destroyed.
  2660. //
  2661. CFolderCache::Singleton().Clear();
  2662. DeleteCriticalSection(&m_cs);
  2663. }
  2664. STDMETHODIMP
  2665. COfflineFilesViewCallback::QueryInterface(
  2666. REFIID riid,
  2667. void **ppv
  2668. )
  2669. {
  2670. static const QITAB qit[] = {
  2671. QITABENT(COfflineFilesViewCallback, IShellFolderViewCB), // IID_IShellFolderViewCB
  2672. QITABENT(COfflineFilesViewCallback, IObjectWithSite), // IID_IObjectWithSite
  2673. { 0 },
  2674. };
  2675. return QISearch(this, qit, riid, ppv);
  2676. }
  2677. STDMETHODIMP_ (ULONG)
  2678. COfflineFilesViewCallback::AddRef(
  2679. void
  2680. )
  2681. {
  2682. return InterlockedIncrement(&_cRef);
  2683. }
  2684. STDMETHODIMP_ (ULONG)
  2685. COfflineFilesViewCallback::Release(
  2686. void
  2687. )
  2688. {
  2689. if (InterlockedDecrement(&_cRef))
  2690. return _cRef;
  2691. delete this;
  2692. return 0;
  2693. }
  2694. HRESULT
  2695. COfflineFilesViewCallback::SetSite(
  2696. IUnknown *punkSite
  2697. )
  2698. {
  2699. if (_psfv)
  2700. {
  2701. _psfv->Release();
  2702. _psfv = NULL;
  2703. }
  2704. if (punkSite)
  2705. punkSite->QueryInterface(IID_IShellFolderView, (void **)&_psfv);
  2706. return S_OK;
  2707. }
  2708. HRESULT
  2709. COfflineFilesViewCallback::GetSite(
  2710. REFIID riid,
  2711. void **ppv
  2712. )
  2713. {
  2714. if (_psfv)
  2715. return _psfv->QueryInterface(riid, ppv);
  2716. *ppv = NULL;
  2717. return E_FAIL;
  2718. }
  2719. STDMETHODIMP
  2720. COfflineFilesViewCallback::MessageSFVCB(
  2721. UINT uMsg,
  2722. WPARAM wParam,
  2723. LPARAM lParam
  2724. )
  2725. {
  2726. HRESULT hres = S_OK;
  2727. switch (uMsg)
  2728. {
  2729. case SFVM_COLUMNCLICK:
  2730. if (_psfv)
  2731. return _psfv->Rearrange((int)wParam);
  2732. break;
  2733. case SFVM_WINDOWCREATED:
  2734. OnSFVM_WindowCreated((HWND)wParam);
  2735. break;
  2736. case SFVM_ADDPROPERTYPAGES:
  2737. OnSFVM_AddPropertyPages((DWORD)wParam, (SFVM_PROPPAGE_DATA *)lParam);
  2738. break;
  2739. case SFVM_GETHELPTOPIC:
  2740. StrCpyW(((SFVM_HELPTOPIC_DATA *)lParam)->wszHelpFile, L"offlinefolders.chm > windefault");
  2741. break;
  2742. case SFVM_QUERYFSNOTIFY:
  2743. hres = OnSFVM_QueryFSNotify((SHChangeNotifyEntry *)lParam);
  2744. break;
  2745. case SFVM_GETNOTIFY:
  2746. hres = OnSFVM_GetNotify((LPITEMIDLIST *)wParam, (LONG *)lParam);
  2747. break;
  2748. case SFVM_FSNOTIFY:
  2749. hres = OnSFVM_FSNotify((LPCITEMIDLIST *)wParam, (LONG)lParam);
  2750. break;
  2751. case SFVM_GETVIEWS:
  2752. hres = OnSFVM_GetViews((SHELLVIEWID *)wParam, (IEnumSFVViews **)lParam);
  2753. break;
  2754. case SFVM_ALTERDROPEFFECT:
  2755. hres = OnSFVM_AlterDropEffect((DWORD *)wParam, (IDataObject *)lParam);
  2756. break;
  2757. case SFVMP_SETVIEWREDRAW:
  2758. hres = OnSFVMP_SetViewRedraw(lParam != FALSE);
  2759. break;
  2760. case SFVMP_DELVIEWITEM:
  2761. hres = OnSFVMP_DelViewItem((LPCTSTR)lParam);
  2762. break;
  2763. default:
  2764. hres = E_NOTIMPL;
  2765. }
  2766. return hres;
  2767. }
  2768. HRESULT
  2769. COfflineFilesViewCallback::OnSFVM_WindowCreated(
  2770. HWND hwnd
  2771. )
  2772. {
  2773. m_hwnd = hwnd;
  2774. return NOERROR;
  2775. }
  2776. HRESULT
  2777. COfflineFilesViewCallback::OnSFVM_AddPropertyPages(
  2778. DWORD pv,
  2779. SFVM_PROPPAGE_DATA *ppagedata
  2780. )
  2781. {
  2782. const CLSID *c_rgFilePages[] = {
  2783. &CLSID_FileTypes,
  2784. &CLSID_OfflineFilesOptions
  2785. };
  2786. IShellPropSheetExt * pspse;
  2787. HRESULT hr;
  2788. for (int i = 0; i < ARRAYSIZE(c_rgFilePages); i++)
  2789. {
  2790. hr = SHCoCreateInstance(NULL,
  2791. c_rgFilePages[i],
  2792. NULL,
  2793. IID_IShellPropSheetExt,
  2794. (void **)&pspse);
  2795. if (SUCCEEDED(hr))
  2796. {
  2797. pspse->AddPages(ppagedata->pfn, ppagedata->lParam);
  2798. pspse->Release();
  2799. }
  2800. }
  2801. return S_OK;
  2802. }
  2803. HRESULT
  2804. COfflineFilesViewCallback::OnSFVM_GetViews(
  2805. SHELLVIEWID *pvid,
  2806. IEnumSFVViews **ppev
  2807. )
  2808. {
  2809. //
  2810. // Offline files folder prefers details view.
  2811. //
  2812. *pvid = VID_Details;
  2813. return COfflineFilesViewEnum::CreateInstance(ppev);
  2814. }
  2815. HRESULT
  2816. COfflineFilesViewCallback::OnSFVM_GetNotify(
  2817. LPITEMIDLIST *ppidl,
  2818. LONG *plEvents
  2819. )
  2820. {
  2821. *ppidl = NULL;
  2822. *plEvents = GetChangeNotifyEvents();
  2823. return NOERROR;
  2824. }
  2825. HRESULT
  2826. COfflineFilesViewCallback::OnSFVM_QueryFSNotify(
  2827. SHChangeNotifyEntry *pfsne
  2828. )
  2829. {
  2830. //
  2831. // Register to receive global events
  2832. //
  2833. pfsne->pidl = NULL;
  2834. pfsne->fRecursive = TRUE;
  2835. return NOERROR;
  2836. }
  2837. HRESULT
  2838. COfflineFilesViewCallback::OnSFVMP_SetViewRedraw(
  2839. BOOL bRedraw
  2840. )
  2841. {
  2842. if (_psfv)
  2843. _psfv->SetRedraw(bRedraw);
  2844. return NOERROR;
  2845. }
  2846. HRESULT
  2847. COfflineFilesViewCallback::OnSFVMP_DelViewItem(
  2848. LPCTSTR pszPath
  2849. )
  2850. {
  2851. Lock();
  2852. HRESULT hr = RemoveItem(pszPath);
  2853. Unlock();
  2854. return hr;
  2855. }
  2856. //
  2857. // This is called immediately before the shell calls DoDragDrop().
  2858. // It let's us turn off "move" after all of the other drop effect
  2859. // modifications have taken place.
  2860. //
  2861. HRESULT
  2862. COfflineFilesViewCallback::OnSFVM_AlterDropEffect(
  2863. DWORD *pdwEffect,
  2864. IDataObject *pdtobj // unused.
  2865. )
  2866. {
  2867. *pdwEffect &= ~DROPEFFECT_MOVE; // Disable move.
  2868. return NOERROR;
  2869. }
  2870. //
  2871. // Handler for shell change notifications.
  2872. //
  2873. // We handle SHCNE_UPDATEITEM, SHCNE_UPDATEDIR, SHCNE_DELETE
  2874. // and SHCNE_RENAMEITEM
  2875. //
  2876. HRESULT
  2877. COfflineFilesViewCallback::OnSFVM_FSNotify(
  2878. LPCITEMIDLIST *ppidl,
  2879. LONG lEvent
  2880. )
  2881. {
  2882. HRESULT hr = NOERROR;
  2883. if (GetChangeNotifyEvents() & lEvent)
  2884. {
  2885. Lock();
  2886. if (SHCNE_RENAMEITEM & lEvent)
  2887. {
  2888. hr = RenameItem(*ppidl, *(ppidl + 1));
  2889. }
  2890. else
  2891. {
  2892. //
  2893. // Convert the full pidl to a UNC path.
  2894. //
  2895. TCHAR szPath[MAX_PATH];
  2896. if (SHGetPathFromIDList(*ppidl, szPath))
  2897. {
  2898. if (SHCNE_UPDATEDIR & lEvent)
  2899. hr = UpdateDir(szPath);
  2900. else if (SHCNE_UPDATEITEM & lEvent)
  2901. hr = UpdateItem(szPath);
  2902. else if (SHCNE_DELETE & lEvent)
  2903. hr = RemoveItem(szPath);
  2904. }
  2905. }
  2906. Unlock();
  2907. }
  2908. return hr;
  2909. }
  2910. //
  2911. // Handler for SHCNE_RENAMEITEM notifications.
  2912. //
  2913. HRESULT
  2914. COfflineFilesViewCallback::RenameItem(
  2915. LPCITEMIDLIST pidlOld,
  2916. LPCITEMIDLIST pidl
  2917. )
  2918. {
  2919. TraceAssert(NULL != pidlOld);
  2920. TraceAssert(NULL != pidl);
  2921. //
  2922. // Get the full path for the original pidl.
  2923. //
  2924. TCHAR szPath[MAX_PATH];
  2925. HRESULT hr = NOERROR;
  2926. if (SHGetPathFromIDList(pidlOld, szPath))
  2927. {
  2928. //
  2929. // Find the original OLID in the listview.
  2930. //
  2931. LPCOLID polid = NULL;
  2932. hr = FindOLID(szPath, &polid);
  2933. if (SUCCEEDED(hr))
  2934. {
  2935. //
  2936. // Get the full path for the new renamed pidl.
  2937. //
  2938. if (SHGetPathFromIDList(pidl, szPath))
  2939. {
  2940. //
  2941. // Create a new OLID for the newly renamed pidl.
  2942. //
  2943. LPOLID polidNew;
  2944. WIN32_FIND_DATA fd;
  2945. ZeroMemory(&fd, sizeof(fd));
  2946. fd.nFileSizeHigh = polid->dwFileSizeHigh;
  2947. fd.nFileSizeLow = polid->dwFileSizeLow;
  2948. fd.ftLastWriteTime = polid->ft;
  2949. fd.dwFileAttributes = polid->dwFileAttributes;
  2950. hr = COfflineFilesFolder::OLID_CreateFromUNCPath(szPath,
  2951. &fd,
  2952. polid->dwStatus,
  2953. polid->dwPinCount,
  2954. polid->dwHintFlags,
  2955. polid->dwServerStatus,
  2956. &polidNew);
  2957. if (SUCCEEDED(hr))
  2958. {
  2959. UINT iItem;
  2960. //
  2961. // Replace the old olid in the view with the new olid.
  2962. // DefView will free the old one if successful.
  2963. //
  2964. hr = _psfv->UpdateObject((LPITEMIDLIST)polid,
  2965. (LPITEMIDLIST)polidNew,
  2966. &iItem);
  2967. if (FAILED(hr))
  2968. {
  2969. //
  2970. // View wouldn't accept the new OLID so free it.
  2971. //
  2972. ILFree((LPITEMIDLIST)polidNew);
  2973. }
  2974. }
  2975. }
  2976. }
  2977. }
  2978. return hr;
  2979. }
  2980. //
  2981. // Locates an OLID in the view and returns the address of the
  2982. // OLID. The returned pointer is to a const object so the caller
  2983. // should not call ILFree on it.
  2984. //
  2985. HRESULT
  2986. COfflineFilesViewCallback::FindOLID(
  2987. LPCTSTR pszPath,
  2988. LPCOLID *ppolid
  2989. )
  2990. {
  2991. TraceAssert(NULL != pszPath);
  2992. TraceAssert(NULL != ppolid);
  2993. //
  2994. // Create one of our OLIDs from the UNC path to use as a search key.
  2995. //
  2996. LPOLID polid = NULL;
  2997. HRESULT hr = COfflineFilesFolder::OLID_CreateFromUNCPath(pszPath, NULL, 0, 0, 0, 0, &polid);
  2998. if (SUCCEEDED(hr))
  2999. {
  3000. //
  3001. // Lock so that index returned by IndexItemFromOLID() is
  3002. // still valid in call to GetObject().
  3003. //
  3004. Lock();
  3005. //
  3006. // Get our item's index in the listview.
  3007. //
  3008. UINT iItem = ItemIndexFromOLID(polid);
  3009. if ((UINT)-1 != iItem)
  3010. hr = _psfv->GetObject((LPITEMIDLIST *)ppolid, iItem);
  3011. else
  3012. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  3013. Unlock();
  3014. ILFree((LPITEMIDLIST)polid);
  3015. }
  3016. return hr;
  3017. }
  3018. //
  3019. // Handler for SHCNE_UPDATEDIR notifications.
  3020. //
  3021. // Enumerates each immediate child of the directory and performs
  3022. // an update.
  3023. //
  3024. HRESULT
  3025. COfflineFilesViewCallback::UpdateDir(
  3026. LPCTSTR pszPath
  3027. )
  3028. {
  3029. TraceAssert(NULL != pszPath);
  3030. HRESULT hr = NOERROR;
  3031. //
  3032. // First remove all items from the listview that are immediate children
  3033. // of this directory. This in effect causes a refresh.
  3034. //
  3035. RemoveItems(pszPath);
  3036. //
  3037. // Now scan the CSC cache for all items in this directory and update/add
  3038. // to the listview as appropriate.
  3039. //
  3040. WIN32_FIND_DATA fd;
  3041. FILETIME ft;
  3042. DWORD dwHintFlags;
  3043. DWORD dwPinCount;
  3044. DWORD dwStatus;
  3045. CCscFindHandle hFind = CacheFindFirst(pszPath, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft);
  3046. if (hFind.IsValid())
  3047. {
  3048. TCHAR szPath[MAX_PATH];
  3049. do
  3050. {
  3051. if (0 == (FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes))
  3052. {
  3053. if (NULL != PathCombine(szPath, pszPath, fd.cFileName))
  3054. UpdateItem(szPath, fd, dwStatus, dwPinCount, dwHintFlags);
  3055. }
  3056. }
  3057. while(CacheFindNext(hFind, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft));
  3058. }
  3059. else
  3060. hr = HRESULT_FROM_WIN32(GetLastError());
  3061. return hr;
  3062. }
  3063. //
  3064. // Given a directory path, remove all immediate children from the listview.
  3065. //
  3066. HRESULT
  3067. COfflineFilesViewCallback::RemoveItems(
  3068. LPCTSTR pszDir
  3069. )
  3070. {
  3071. TraceAssert(NULL != pszDir);
  3072. UINT cItems;
  3073. if (SUCCEEDED(_psfv->GetObjectCount(&cItems)))
  3074. {
  3075. LPCOLID polid;
  3076. for (UINT i = 0; i < cItems; i++)
  3077. {
  3078. if (SUCCEEDED(_psfv->GetObject((LPITEMIDLIST *)&polid, i)))
  3079. {
  3080. if (0 == ualstrcmpi(pszDir, polid->szPath))
  3081. {
  3082. //
  3083. // This item is from the "pszDir" directory.
  3084. // Remove it from the listview.
  3085. //
  3086. RemoveItem(polid);
  3087. //
  3088. // Adjust item count and loop variable for deleted
  3089. // item.
  3090. //
  3091. cItems--;
  3092. i--;
  3093. }
  3094. }
  3095. }
  3096. }
  3097. return NOERROR;
  3098. }
  3099. //
  3100. // Given an OLID, remove an item from the view.
  3101. //
  3102. HRESULT
  3103. COfflineFilesViewCallback::RemoveItem(
  3104. LPCOLID polid
  3105. )
  3106. {
  3107. TraceAssert(NULL != polid);
  3108. HRESULT hr = E_FAIL;
  3109. UINT iItem = ItemIndexFromOLID(polid);
  3110. if ((UINT)-1 != iItem)
  3111. {
  3112. //
  3113. // File is in the listview. Remove it.
  3114. //
  3115. hr = _psfv->RemoveObject((LPITEMIDLIST)polid, &iItem);
  3116. }
  3117. return hr;
  3118. }
  3119. //
  3120. // Give a UNC path, remove an item from the view.
  3121. //
  3122. HRESULT
  3123. COfflineFilesViewCallback::RemoveItem(
  3124. LPCTSTR pszPath
  3125. )
  3126. {
  3127. TraceAssert(NULL != pszPath);
  3128. LPOLID polid = NULL;
  3129. HRESULT hr = COfflineFilesFolder::OLID_CreateFromUNCPath(pszPath, NULL, 0, 0, 0, 0, &polid);
  3130. if (SUCCEEDED(hr))
  3131. {
  3132. hr = RemoveItem(polid);
  3133. ILFree((LPITEMIDLIST)polid);
  3134. }
  3135. return hr;
  3136. }
  3137. //
  3138. // Handler for SHCNE_UPDATEITEM notifications.
  3139. //
  3140. // Updates a single item in the viewer. If the item no longer
  3141. // exists in the cache, it is removed from the view.
  3142. //
  3143. HRESULT
  3144. COfflineFilesViewCallback::UpdateItem(
  3145. LPCTSTR pszPath
  3146. )
  3147. {
  3148. TraceAssert(NULL != pszPath);
  3149. HRESULT hr = NOERROR;
  3150. DWORD dwAttr = ::GetFileAttributes(pszPath);
  3151. if (DWORD(-1) != dwAttr)
  3152. {
  3153. if (0 == (FILE_ATTRIBUTE_DIRECTORY & dwAttr))
  3154. {
  3155. DWORD dwHintFlags = 0;
  3156. DWORD dwPinCount = 0;
  3157. DWORD dwStatus = 0;
  3158. WIN32_FIND_DATA fd;
  3159. FILETIME ft;
  3160. CCscFindHandle hFind = CacheFindFirst(pszPath, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft);
  3161. if (hFind.IsValid())
  3162. {
  3163. hr = UpdateItem(pszPath, fd, dwStatus, dwPinCount, dwHintFlags);
  3164. }
  3165. else
  3166. {
  3167. hr = RemoveItem(pszPath);
  3168. }
  3169. }
  3170. }
  3171. return hr;
  3172. }
  3173. //
  3174. // Update a single item in the cache. This instance of UpdateItem()
  3175. // is called once we have information on the item from the CSC cache.
  3176. // If an item doesn't already exist in the viewer, it is added.
  3177. // If an item does exist, it is updated with the new CSC info.
  3178. //
  3179. // This function assumes the item is NOT a directory.
  3180. //
  3181. HRESULT
  3182. COfflineFilesViewCallback::UpdateItem(
  3183. LPCTSTR pszPath,
  3184. const WIN32_FIND_DATA& fd,
  3185. DWORD dwStatus,
  3186. DWORD dwPinCount,
  3187. DWORD dwHintFlags
  3188. )
  3189. {
  3190. TraceAssert(NULL != pszPath);
  3191. TraceAssert(0 == (FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes));
  3192. HRESULT hr = NOERROR;
  3193. UINT iItem = (UINT)-1;
  3194. //
  3195. // Now create one of our OLIDs from the UNC path.
  3196. //
  3197. LPOLID polid = NULL;
  3198. hr = COfflineFilesFolder::OLID_CreateFromUNCPath(pszPath, NULL, 0, 0, 0, 0, &polid);
  3199. if (SUCCEEDED(hr))
  3200. {
  3201. //
  3202. // Get our item's index in the listview.
  3203. //
  3204. LPCITEMIDLIST pidlOld = NULL;
  3205. //
  3206. // Lock so that index returned by ItemIndexFromOLID() is
  3207. // still valid in call to GetObject().
  3208. //
  3209. Lock();
  3210. iItem = ItemIndexFromOLID(polid);
  3211. if ((UINT)-1 != iItem)
  3212. {
  3213. //
  3214. // Won't be using this olid. We'll be cloning the one from the
  3215. // listview.
  3216. //
  3217. ILFree((LPITEMIDLIST)polid);
  3218. polid = NULL;
  3219. //
  3220. // Item is in the view. Get the existing OLID and clone it.
  3221. // IMPORTANT: We DON'T call ILFree on pidlOld. Despite the
  3222. // argument to GetObject being non-const, it's
  3223. // really returning a pointer to a const object.
  3224. // In actuality, it's the address of the listview
  3225. // item's LPARAM.
  3226. //
  3227. hr = _psfv->GetObject((LPITEMIDLIST *)&pidlOld, iItem);
  3228. if (SUCCEEDED(hr))
  3229. {
  3230. polid = (LPOLID)ILClone(pidlOld);
  3231. if (NULL == polid)
  3232. {
  3233. hr = E_OUTOFMEMORY;
  3234. }
  3235. }
  3236. }
  3237. Unlock();
  3238. if (NULL != polid)
  3239. {
  3240. //
  3241. // polid either points to the new partial OLID we created
  3242. // with OLID_CreateFromUNCPath() or a clone of the existing
  3243. // OLID in the listview. Fill/update the file and
  3244. // CSC information.
  3245. //
  3246. polid->dwFileSizeHigh = fd.nFileSizeHigh;
  3247. polid->dwFileSizeLow = fd.nFileSizeLow;
  3248. polid->ft = fd.ftLastWriteTime;
  3249. polid->dwFileAttributes = fd.dwFileAttributes;
  3250. polid->dwStatus = dwStatus;
  3251. polid->dwHintFlags = dwHintFlags;
  3252. polid->dwPinCount = dwPinCount;
  3253. if ((UINT)-1 != iItem)
  3254. {
  3255. //
  3256. // Replace the old olid in the view with the new olid.
  3257. // DefView will free the old one if successful.
  3258. //
  3259. hr = _psfv->UpdateObject((LPITEMIDLIST)pidlOld,
  3260. (LPITEMIDLIST)polid,
  3261. &iItem);
  3262. }
  3263. else
  3264. {
  3265. //
  3266. // Add the new olid to the view.
  3267. //
  3268. hr = _psfv->AddObject((LPITEMIDLIST)polid, &iItem);
  3269. }
  3270. if (SUCCEEDED(hr))
  3271. {
  3272. //
  3273. // Added new OLID to the listview. Null out the local
  3274. // ptr so we don't free the IDList later.
  3275. //
  3276. polid = NULL;
  3277. }
  3278. }
  3279. if (NULL != polid)
  3280. ILFree((LPITEMIDLIST)polid);
  3281. }
  3282. return hr;
  3283. }
  3284. //
  3285. // Retrieve the listview index for a give OLID.
  3286. // Returns: Index of item or -1 if not found.
  3287. //
  3288. UINT
  3289. COfflineFilesViewCallback::ItemIndexFromOLID(
  3290. LPCOLID polid
  3291. )
  3292. {
  3293. TraceAssert(NULL != polid);
  3294. UINT iItem = (UINT)-1;
  3295. UINT cItems;
  3296. //
  3297. // Lock so that list remains consistent while we locate the item.
  3298. //
  3299. Lock();
  3300. if (SUCCEEDED(_psfv->GetObjectCount(&cItems)))
  3301. {
  3302. for (UINT i = 0; i < cItems; i++)
  3303. {
  3304. LPCITEMIDLIST pidl;
  3305. if (SUCCEEDED(_psfv->GetObject((LPITEMIDLIST *)&pidl, i)))
  3306. {
  3307. //
  3308. // Do name comparison first since it is least likely to find a match.
  3309. //
  3310. if (S_OK == _pfolder->CompareIDs(ICOL_NAME, pidl, (LPCITEMIDLIST)polid) &&
  3311. S_OK == _pfolder->CompareIDs(ICOL_LOCATION, pidl, (LPCITEMIDLIST)polid))
  3312. {
  3313. iItem = i;
  3314. break;
  3315. }
  3316. }
  3317. }
  3318. }
  3319. Unlock();
  3320. return (UINT)iItem;
  3321. }
  3322. //-----------------------------------------------------------------------------
  3323. // COfflineFilesViewEnum
  3324. //-----------------------------------------------------------------------------
  3325. COfflineFilesViewEnum::COfflineFilesViewEnum(
  3326. void
  3327. )
  3328. : m_cRef(1),
  3329. m_iAddView(0)
  3330. {
  3331. }
  3332. COfflineFilesViewEnum::~COfflineFilesViewEnum(
  3333. void
  3334. )
  3335. {
  3336. }
  3337. HRESULT
  3338. COfflineFilesViewEnum::CreateInstance(
  3339. IEnumSFVViews **ppenum
  3340. )
  3341. {
  3342. HRESULT hr = E_OUTOFMEMORY;
  3343. COfflineFilesViewEnum *pEnum = new COfflineFilesViewEnum;
  3344. if (NULL != pEnum)
  3345. {
  3346. hr = pEnum->QueryInterface(IID_IEnumSFVViews, (void **)ppenum);
  3347. }
  3348. return hr;
  3349. }
  3350. STDMETHODIMP
  3351. COfflineFilesViewEnum::QueryInterface (
  3352. REFIID riid,
  3353. void **ppv
  3354. )
  3355. {
  3356. static const QITAB qit[] = {
  3357. QITABENT(COfflineFilesViewEnum, IEnumSFVViews),
  3358. { 0 },
  3359. };
  3360. return QISearch(this, qit, riid, ppv);
  3361. }
  3362. STDMETHODIMP_(ULONG)
  3363. COfflineFilesViewEnum::AddRef(
  3364. void
  3365. )
  3366. {
  3367. return InterlockedIncrement(&m_cRef);
  3368. }
  3369. STDMETHODIMP_(ULONG)
  3370. COfflineFilesViewEnum::Release(
  3371. void
  3372. )
  3373. {
  3374. if (InterlockedDecrement(&m_cRef))
  3375. return m_cRef;
  3376. delete this;
  3377. return 0;
  3378. }
  3379. STDMETHODIMP
  3380. COfflineFilesViewEnum::Next(
  3381. ULONG celt,
  3382. SFVVIEWSDATA **ppData,
  3383. ULONG *pceltFetched
  3384. )
  3385. {
  3386. HRESULT hr = S_FALSE;
  3387. ULONG celtFetched = 0;
  3388. if (!celt || !ppData || (celt > 1 && !pceltFetched))
  3389. {
  3390. return E_INVALIDARG;
  3391. }
  3392. if (0 == m_iAddView)
  3393. {
  3394. //
  3395. // All we add is Thumbnail view.
  3396. //
  3397. ppData[0] = (SFVVIEWSDATA *) SHAlloc(sizeof(SFVVIEWSDATA));
  3398. if (ppData[0])
  3399. {
  3400. ppData[0]->idView = CLSID_ThumbnailViewExt;
  3401. ppData[0]->idExtShellView = CLSID_ThumbnailViewExt;
  3402. ppData[0]->dwFlags = SFVF_TREATASNORMAL | SFVF_NOWEBVIEWFOLDERCONTENTS;
  3403. ppData[0]->lParam = 0x00000011;
  3404. ppData[0]->wszMoniker[0] = 0;
  3405. celtFetched++;
  3406. m_iAddView++;
  3407. hr = S_OK;
  3408. }
  3409. else
  3410. hr = E_OUTOFMEMORY;
  3411. }
  3412. if ( pceltFetched )
  3413. {
  3414. *pceltFetched = celtFetched;
  3415. }
  3416. return hr;
  3417. }
  3418. STDMETHODIMP
  3419. COfflineFilesViewEnum::Skip(
  3420. ULONG celt
  3421. )
  3422. {
  3423. if (celt && !m_iAddView)
  3424. {
  3425. m_iAddView++;
  3426. celt--;
  3427. }
  3428. return (celt ? S_FALSE : S_OK );
  3429. }
  3430. STDMETHODIMP COfflineFilesViewEnum::Reset(
  3431. void
  3432. )
  3433. {
  3434. m_iAddView = 0;
  3435. return NOERROR;
  3436. }
  3437. STDMETHODIMP
  3438. COfflineFilesViewEnum::Clone(
  3439. IEnumSFVViews **ppenum
  3440. )
  3441. {
  3442. return CreateInstance(ppenum);
  3443. }
  3444. //-----------------------------------------------------------------------------
  3445. // COfflineDetails
  3446. //-----------------------------------------------------------------------------
  3447. COfflineDetails::COfflineDetails(
  3448. COfflineFilesFolder *pfolder
  3449. ) : _cRef (1)
  3450. {
  3451. _pfolder = pfolder;
  3452. _pfolder->AddRef();
  3453. }
  3454. COfflineDetails::~COfflineDetails()
  3455. {
  3456. if (_pfolder)
  3457. _pfolder->Release();
  3458. }
  3459. STDMETHODIMP
  3460. COfflineDetails::QueryInterface(
  3461. REFIID riid,
  3462. void **ppv
  3463. )
  3464. {
  3465. static const QITAB qit[] = {
  3466. QITABENT(COfflineDetails, IShellDetails),
  3467. { 0 },
  3468. };
  3469. return QISearch(this, qit, riid, ppv);
  3470. }
  3471. STDMETHODIMP_(ULONG)
  3472. COfflineDetails::AddRef(
  3473. void
  3474. )
  3475. {
  3476. return InterlockedIncrement(&_cRef);
  3477. }
  3478. STDMETHODIMP_(ULONG)
  3479. COfflineDetails::Release(
  3480. void
  3481. )
  3482. {
  3483. if (InterlockedDecrement(&_cRef))
  3484. return _cRef;
  3485. delete this;
  3486. return 0;
  3487. }
  3488. //-----------------------------------------------------------------------------
  3489. // CFileTypeCache
  3490. //
  3491. // Implements a simple hash table for storing file type strings keyed on
  3492. // file extension.
  3493. //
  3494. //-----------------------------------------------------------------------------
  3495. CFileTypeCache::CFileTypeCache(
  3496. int cBuckets
  3497. ) : m_cBuckets(cBuckets),
  3498. m_prgBuckets(NULL)
  3499. {
  3500. InitializeCriticalSection(&m_cs);
  3501. }
  3502. CFileTypeCache::~CFileTypeCache(
  3503. void
  3504. )
  3505. {
  3506. Lock();
  3507. if (NULL != m_prgBuckets)
  3508. {
  3509. for (int i = 0; i < m_cBuckets; i++)
  3510. {
  3511. while(NULL != m_prgBuckets[i])
  3512. {
  3513. CEntry *pDelThis = m_prgBuckets[i];
  3514. m_prgBuckets[i] = m_prgBuckets[i]->Next();
  3515. delete pDelThis;
  3516. }
  3517. }
  3518. delete[] m_prgBuckets;
  3519. m_prgBuckets = NULL;
  3520. }
  3521. Unlock();
  3522. DeleteCriticalSection(&m_cs);
  3523. }
  3524. CFileTypeCache::CEntry *
  3525. CFileTypeCache::Lookup(
  3526. LPCTSTR pszExt
  3527. )
  3528. {
  3529. if (NULL != m_prgBuckets)
  3530. {
  3531. for (CEntry *pEntry = m_prgBuckets[Hash(pszExt)]; pEntry; pEntry = pEntry->Next())
  3532. {
  3533. if (0 == pEntry->CompareExt(pszExt))
  3534. return pEntry;
  3535. }
  3536. }
  3537. return NULL;
  3538. }
  3539. HRESULT
  3540. CFileTypeCache::Add(
  3541. LPCTSTR pszExt,
  3542. LPCTSTR pszTypeName
  3543. )
  3544. {
  3545. HRESULT hr = E_OUTOFMEMORY;
  3546. if (NULL != m_prgBuckets)
  3547. {
  3548. CEntry *pNewEntry = new CEntry(pszExt, pszTypeName);
  3549. if (NULL != pNewEntry && pNewEntry->IsValid())
  3550. {
  3551. //
  3552. // Link new entry at the head of the bucket's linked list.
  3553. //
  3554. int iHash = Hash(pszExt);
  3555. pNewEntry->SetNext(m_prgBuckets[iHash]);
  3556. m_prgBuckets[iHash] = pNewEntry;
  3557. hr = NOERROR;
  3558. }
  3559. else
  3560. {
  3561. delete pNewEntry;
  3562. }
  3563. }
  3564. return hr;
  3565. }
  3566. HRESULT
  3567. CFileTypeCache::GetTypeName(
  3568. LPCTSTR pszPath, // Can be full path or only "filename.ext".
  3569. DWORD dwFileAttributes,
  3570. LPTSTR pszDest,
  3571. int cchDest
  3572. )
  3573. {
  3574. HRESULT hr = S_OK;
  3575. Lock();
  3576. if (NULL == m_prgBuckets)
  3577. {
  3578. //
  3579. // Create hash bucket array on-demand. This way it's not
  3580. // created until someone asks for something from the cache.
  3581. // Simple "creation" of the cache object is therefore cheap.
  3582. //
  3583. m_prgBuckets = new CEntry* [m_cBuckets];
  3584. if (NULL != m_prgBuckets)
  3585. {
  3586. ZeroMemory(m_prgBuckets, sizeof(m_prgBuckets[0]) * m_cBuckets);
  3587. }
  3588. else
  3589. {
  3590. hr = E_OUTOFMEMORY;
  3591. }
  3592. }
  3593. if (SUCCEEDED(hr))
  3594. {
  3595. SHFILEINFO sfi;
  3596. LPCTSTR pszTypeName = NULL;
  3597. LPCTSTR pszExt = ::PathFindExtension(pszPath);
  3598. //
  3599. // Note that Lookup will gracefully fail if the hash bucket array
  3600. // creation failed. In that case we'll get the info from
  3601. // SHGetFileInfo and return it directly to the caller. This means
  3602. // that failure to create the cache is not fatal. It just means we
  3603. // don't cache any data.
  3604. //
  3605. CEntry *pEntry = Lookup(pszExt);
  3606. if (NULL != pEntry)
  3607. {
  3608. // Cache hit.
  3609. pszTypeName = pEntry->TypeName();
  3610. }
  3611. if (NULL == pszTypeName)
  3612. {
  3613. // Cache miss.
  3614. if (SHGetFileInfo(::PathFindFileName(pszPath),
  3615. dwFileAttributes,
  3616. &sfi,
  3617. sizeof(sfi),
  3618. SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES))
  3619. {
  3620. //
  3621. // Add new entry to cache. We're not concerned if the
  3622. // addition fails. It just means we'll get a cache miss
  3623. // on this item next time and repeat the SHGetFileInfo call.
  3624. //
  3625. pszTypeName = sfi.szTypeName;
  3626. Add(pszExt, sfi.szTypeName);
  3627. }
  3628. }
  3629. if (NULL != pszTypeName)
  3630. {
  3631. lstrcpyn(pszDest, pszTypeName, cchDest);
  3632. hr = S_OK;
  3633. }
  3634. else
  3635. {
  3636. hr = E_FAIL;
  3637. }
  3638. }
  3639. Unlock();
  3640. return hr;
  3641. }
  3642. int
  3643. CFileTypeCache::Hash(
  3644. LPCTSTR pszExt
  3645. )
  3646. {
  3647. int iSum = 0;
  3648. while(*pszExt)
  3649. iSum += int(*pszExt++);
  3650. return iSum % m_cBuckets;
  3651. }
  3652. CFileTypeCache::CEntry::CEntry(
  3653. LPCTSTR pszExt,
  3654. LPCTSTR pszTypeName
  3655. ) : m_pNext(NULL)
  3656. {
  3657. m_pszExt = StrDup(pszExt);
  3658. m_pszTypeName = StrDup(pszTypeName);
  3659. }
  3660. CFileTypeCache::CEntry::~CEntry(
  3661. void
  3662. )
  3663. {
  3664. if (NULL != m_pszExt)
  3665. {
  3666. LocalFree(m_pszExt);
  3667. }
  3668. if (NULL != m_pszTypeName)
  3669. {
  3670. LocalFree(m_pszTypeName);
  3671. }
  3672. }
  3673. //
  3674. // This function creates our standard offline-files context menu.
  3675. // This is the one used by the shell that inserts the
  3676. // "Make Available Offline" and "Synchronize" items.
  3677. //
  3678. HRESULT
  3679. CreateOfflineFilesContextMenu(
  3680. IDataObject *pdtobj,
  3681. REFIID riid,
  3682. void **ppv
  3683. )
  3684. {
  3685. TraceAssert(NULL != ppv);
  3686. HRESULT hr = E_OUTOFMEMORY;
  3687. *ppv = NULL;
  3688. CCscShellExt *pse = new CCscShellExt;
  3689. if (NULL != pse)
  3690. {
  3691. IShellExtInit *psei;
  3692. hr = pse->QueryInterface(IID_IShellExtInit, (void **)&psei);
  3693. pse->Release();
  3694. if (SUCCEEDED(hr))
  3695. {
  3696. if (NULL != pdtobj)
  3697. hr = psei->Initialize(NULL, pdtobj, NULL);
  3698. if (SUCCEEDED(hr))
  3699. {
  3700. hr = psei->QueryInterface(riid, ppv);
  3701. }
  3702. psei->Release();
  3703. }
  3704. }
  3705. return hr;
  3706. }