enum.cpp
上传用户:xhy777
上传日期:2007-02-14
资源大小:24088k
文件大小:9k
源码类别:

系统编程

开发平台:

Visual C++

  1. #include "shellprv.h"
  2. #include "enum.h"
  3. #ifdef WINNT
  4. #define ILEnumClone ILClone
  5. #define ILEnumFree  ILFree
  6. #else
  7. // on win95, the cache is global to all processes
  8. #define ILEnumClone ILGlobalClone
  9. #define ILEnumFree  ILGlobalFree
  10. #endif
  11. class SFENUMCACHEELT
  12. {
  13. public:
  14.     DWORD grfFlags;
  15.     LPITEMIDLIST pidl;
  16. };
  17. class SFEnumCache : public IEnumIDList
  18. {
  19. public:
  20.     // *** IUnknown methods ***
  21.     STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  22.     STDMETHOD_(ULONG,AddRef)();
  23.     STDMETHOD_(ULONG,Release)();
  24.     // *** IEnumIDList methods ***
  25.     STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  26.     STDMETHOD(Skip)(ULONG celt);
  27.     STDMETHOD(Reset)();
  28.     STDMETHOD(Clone)(IEnumIDList **ppenum);
  29.     
  30.     SFEnumCache(IEnumIDList* penum, DWORD grfFlags, SFEnumCacheData* pcache, IShellFolder* psf);
  31.     virtual ~SFEnumCache();
  32. protected:
  33.     
  34.     
  35.     void _InitCache();
  36.     BOOL _IsCacheValid();
  37.     static _CacheFreeCallback(LPVOID p, LPVOID d);
  38.     void _FreeCache(HDSA hdsa) { DSA_DestroyCallback(hdsa, _CacheFreeCallback, 0); };
  39.     void _FreePidlDPA();
  40.     void _InitGlobalCache();
  41.     HDSA _GetGlobalCacheDSA();
  42.     LONG _cRef;
  43.     int _iIndex;
  44.     IEnumIDList *_penum;  // the thing we're wrapping
  45.     DWORD _grfFlags;
  46.     SFEnumCacheData *_pcache;
  47.     IShellFolder *_psf;
  48.     HDPA _hdpa;
  49. };
  50. SFEnumCache::~SFEnumCache()
  51. {
  52.     _FreePidlDPA();
  53.     _penum->Release();
  54.     _psf->Release();
  55. }
  56. void SFEnumCache_Terminate(SFEnumCacheData * pcache)
  57. {
  58.     // we can have a process detach before we put anything into the dsa
  59.     // avoid the rip and don't call DSA_Destroy unless we really have one.
  60.     if (pcache->hdsa)
  61.     {
  62.         // we don't free the id's because this is called on process detach
  63.         // and the system process cleanup will take care of this for us
  64.         DSA_Destroy(pcache->hdsa);
  65.         pcache->hdsa = NULL;
  66.     }
  67. }
  68. void SFEnumCache_Invalidate(SFEnumCacheData * pcache, DWORD grfFlags)
  69. {
  70.     ENTERCRITICAL;
  71.     pcache->grfInvalid |= grfFlags;
  72.     LEAVECRITICAL;
  73. }
  74. HRESULT SFEnumCache_Create(IEnumIDList* penum, DWORD grfFlags, SFEnumCacheData* pcache, IShellFolder* psf, IEnumIDList **ppenumUnknown)
  75. {
  76.     HRESULT hres = E_OUTOFMEMORY;
  77.     SFEnumCache* pec = new SFEnumCache(penum, grfFlags, pcache, psf);
  78.     
  79.     if (pec) {
  80.         *ppenumUnknown = pec;
  81.         hres = S_OK;
  82.     }
  83.     return hres;
  84. }
  85. //SHCONTF_FOLDERS         = 32,       // for shell browser
  86. //    SHCONTF_NONFOLDERS      = 64,       // for default view
  87. //    SHCONTF_INCLUDEHIDDEN   = 128,      // for hidden/system objects
  88. SFEnumCache::SFEnumCache(IEnumIDList* penum, DWORD grfFlags, SFEnumCacheData* pcache, IShellFolder* psf) :
  89.     _cRef(1), _penum(penum), _grfFlags(grfFlags), _psf(psf)
  90. {
  91.     ASSERT(_penum);
  92.     ASSERT(_psf);
  93.     _penum->AddRef();
  94.     _psf->AddRef();
  95.     _pcache = pcache;
  96. }
  97. STDMETHODIMP_(ULONG) SFEnumCache::AddRef()
  98. {
  99.     return InterlockedIncrement(&_cRef);
  100. }
  101. STDMETHODIMP SFEnumCache::QueryInterface(REFIID riid, void **ppv)
  102. {
  103.     if (IsEqualIID(riid, IID_IUnknown) ||
  104.         IsEqualIID(riid, IID_IEnumIDList))
  105.     {
  106.         *ppv = SAFECAST(this, IEnumIDList*);
  107.     }
  108.     else
  109.     {
  110.         *ppv = NULL;
  111.         return E_NOINTERFACE;
  112.     }
  113.     ((IUnknown *)*ppv)->AddRef();
  114.     return NOERROR;
  115. }
  116. STDMETHODIMP_(ULONG) SFEnumCache::Release()
  117. {
  118.     if (InterlockedDecrement(&_cRef))
  119.         return _cRef;
  120.     delete this;
  121.     return 0;
  122. }
  123. BOOL SFEnumCache::_IsCacheValid()
  124. {
  125.     BOOL fValid;
  126.     
  127.     fValid = (_pcache->hdsa && !(_pcache->grfInvalid & _grfFlags));
  128.     
  129.     return fValid;
  130. }
  131. int SFEnumCache::_CacheFreeCallback(LPVOID p, LPVOID d)
  132. {
  133.     SFENUMCACHEELT* pce = (SFENUMCACHEELT*)p;
  134.     ILEnumFree(pce->pidl);
  135.     return 1;
  136. }
  137. STDAPI_(int) DPA_ILFreeCallback(LPVOID p, LPVOID d)
  138. {
  139.     ILFree((LPITEMIDLIST)p);    // ILFree checks for NULL pointer
  140.     return 1;
  141. }
  142. STDAPI_(void) DPA_FreeIDArray(HDPA hdpa)
  143. {
  144.     if (hdpa)
  145.         DPA_DestroyCallback(hdpa, DPA_ILFreeCallback, 0);
  146. }
  147. void SFEnumCache::_FreePidlDPA()
  148. {
  149.     DPA_FreeIDArray(_hdpa);
  150.     _hdpa = NULL;
  151. }
  152. void SFEnumCache::_InitGlobalCache()
  153. {
  154.     if (!_IsCacheValid()) 
  155.     {
  156.         HDSA hdsa;
  157.         LPITEMIDLIST pidl;
  158.         ULONG celt;
  159.         ASSERTNONCRITICAL;
  160.         
  161.         hdsa = DSA_Create(SIZEOF(SFENUMCACHEELT), 16);
  162.         
  163.         while ((_penum->Next(1, &pidl, &celt) == S_OK) &&
  164.                (celt == 1))
  165.         {
  166.             SFENUMCACHEELT ce;
  167.             DWORD dwAttr = SFGAO_GHOSTED | SFGAO_FOLDER;
  168.             ce.grfFlags = 0;
  169.             
  170.             _psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidl, &dwAttr);
  171.             
  172.             if (dwAttr & SFGAO_GHOSTED)
  173.                 ce.grfFlags |= SHCONTF_INCLUDEHIDDEN;
  174.             
  175.             if (dwAttr & SFGAO_FOLDER)
  176.                 ce.grfFlags |= SHCONTF_FOLDERS;
  177.             else
  178.                 ce.grfFlags |= SHCONTF_NONFOLDERS;
  179. #ifdef WINNT
  180.             ce.pidl = pidl;
  181. #else
  182.             ce.pidl = ILEnumClone(pidl);
  183.             ILFree(pidl);
  184. #endif
  185.             DSA_AppendItem(hdsa, &ce);
  186.         }
  187.         
  188.         ENTERCRITICAL; 
  189.         // between the outside IsCacheValid and now, antoher thread could have
  190.         // validated this cache.  we either take this hdsa and free the invalid one
  191.         // or free this local hdsa.
  192.         
  193.         HDSA hdsaFree = hdsa;
  194.         if(!_IsCacheValid()) {
  195.             
  196.             hdsaFree = _pcache->hdsa;
  197.             _pcache->hdsa = hdsa;
  198.             _pcache->grfInvalid = 0;
  199.         }
  200.         
  201.         LEAVECRITICAL;
  202.         
  203.         if (hdsaFree)
  204.             _FreeCache(hdsaFree);
  205.     }
  206. }
  207. HDSA SFEnumCache::_GetGlobalCacheDSA()
  208. {
  209.     if (_IsCacheValid())
  210.         return _pcache->hdsa;
  211.    
  212.     return NULL;
  213. }
  214. // this is a little strange... but it's done this way to avoid holding the
  215. // critical section while doing the enumeration.
  216. // 1) InitGlobalCache verifies that the cache is valid for this type of
  217. //    enumeration
  218. // 2) GetGlobalCacheDSA gets the dsa, and revalidates (but within the crit section)
  219. //    that the dsa is valid for this enumeration.  if it's invalid, it returns NULL
  220. // 3) if it's valid, we do a clone of just the IDs we need.  otherwise we
  221. //    go back to 1
  222. void SFEnumCache::_InitCache()
  223. {
  224.     if (_hdpa)  // this means we've initialized already
  225.         return;
  226.      
  227.     HDSA hdsa;
  228.     _hdpa = DPA_Create(16);
  229.     
  230.     do {
  231.         
  232.         // this enumerates the folder and stores it into the global dsa
  233.         _InitGlobalCache();
  234.     
  235.         ENTERCRITICAL;
  236.     
  237.         hdsa = _GetGlobalCacheDSA();
  238.         if (hdsa) {
  239.             int i;
  240.             for (i = 0; i < DSA_GetItemCount(_pcache->hdsa); i++) {
  241.                 SFENUMCACHEELT *pce = (SFENUMCACHEELT*)DSA_GetItemPtr(_pcache->hdsa, i);
  242.                 
  243.                 // make sure it's appropriately a folder or non-folder
  244.                 // then makes ure that either it's not hidden, or that we're allowing
  245.                 // hidden items
  246.                 if ((pce->grfFlags & (_grfFlags & ~SHCONTF_INCLUDEHIDDEN)) && 
  247.                     ((!(pce->grfFlags & SHCONTF_INCLUDEHIDDEN)) || (_grfFlags & SHCONTF_INCLUDEHIDDEN))) {
  248.                     // this is our type.  add it to our own dpa
  249.                     LPITEMIDLIST pidl = ILClone(pce->pidl);
  250.                     if (pidl) {
  251.                         DPA_AppendPtr(_hdpa, pidl);
  252.                     }
  253.                 }
  254.             }
  255.         }
  256.         LEAVECRITICAL;
  257.         
  258.     } while (!hdsa);
  259. }
  260. // *** IEnumIDList methods ***
  261. STDMETHODIMP SFEnumCache::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) 
  262. {
  263.     HRESULT hres = S_OK;
  264.     UINT cFetched = 0;
  265.     _InitCache();
  266.     
  267.     if (_hdpa) {
  268.         for ( ; _iIndex < DPA_GetPtrCount(_hdpa) && cFetched < celt; _iIndex++, cFetched++)
  269.         {
  270.             rgelt[0] = ILClone((LPITEMIDLIST)DPA_GetPtr(_hdpa, _iIndex));
  271.             if (!rgelt[0]) {
  272.                 hres = E_OUTOFMEMORY;
  273.                 break;
  274.             }
  275.             rgelt++;
  276.         }
  277.         
  278.         if (SUCCEEDED(hres) && !cFetched){
  279.             // no more elements
  280.             hres = S_FALSE;
  281.         }
  282.     } else {
  283.         hres = E_OUTOFMEMORY;
  284.     }
  285.     if (pceltFetched)
  286.         *pceltFetched = cFetched;
  287.     return hres;
  288. }
  289. STDMETHODIMP SFEnumCache::Skip( ULONG celt) 
  290. {
  291.     // REVIEW: Implement it later.
  292.     return E_NOTIMPL;
  293. }
  294. STDMETHODIMP SFEnumCache::Reset() 
  295. {
  296.     _iIndex = 0;
  297.     _FreePidlDPA();
  298.     return S_OK;
  299. }
  300. STDMETHODIMP SFEnumCache::Clone( IEnumIDList **ppenum) 
  301. {
  302.     IEnumIDList* penum;
  303.     HRESULT hres = _penum->Clone(&penum);
  304.     if (SUCCEEDED(hres)) 
  305.     {
  306.         SFEnumCache* pec = new SFEnumCache(penum, _grfFlags, _pcache, _psf);
  307.         
  308.         if (pec) 
  309.         {
  310.             *ppenum = SAFECAST(pec, IEnumIDList*);
  311.         } 
  312.         else 
  313.         {
  314.             // we can't just return the penum on failed enum
  315.             // because the penum is a full enumerator, not a grfFlags limited one
  316.             hres = E_OUTOFMEMORY;
  317.         }
  318.         penum->Release();
  319.     }
  320.     return hres;
  321. }
  322.