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

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