1 /* 2 * ReactOS Explorer 3 * 4 * Copyright 2016 Sylvain Deverre <deverre dot sylv at gmail dot com> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 /* 22 * Wraps the component categories manager enum 23 */ 24 25 #include "shellbars.h" 26 27 #define REGPATH L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Discardable\\PostSetup\\Component Categories" 28 #define IMPLEMENTING L"Implementing" 29 #define REQUIRING L"Requiring" 30 31 typedef struct categoryCacheHeader 32 { 33 DWORD dwSize; // size of header only 34 DWORD version; // currently 1 35 SYSTEMTIME writeTime; // time we were written to registry 36 DWORD classCount; // number of classes following 37 } CATCACHEHDR, *PCATCACHEHDR; 38 39 /* 40 * This class manages a cached explorer component categories items, writing cache if it 41 * doesn't exist yet. 42 * It is used by CSHEnumClassesOfCategories internally. 43 */ 44 class CComCatCachedCategory 45 { 46 public: 47 CComCatCachedCategory(); 48 virtual ~CComCatCachedCategory(); 49 HRESULT WriteCacheToDSA(HDSA pDest); 50 HRESULT STDMETHODCALLTYPE Initialize(CATID &catID, BOOL reloadCache); 51 private: 52 BOOL LoadFromRegistry(); 53 HRESULT LoadFromComCatMgr(); 54 HRESULT CacheDSA(); 55 CATID fCategory; 56 HDSA fLocalDsa; 57 }; 58 59 CComCatCachedCategory::CComCatCachedCategory() 60 { 61 fLocalDsa = DSA_Create(sizeof(GUID), 5); 62 } 63 64 HRESULT STDMETHODCALLTYPE CComCatCachedCategory::Initialize(CATID &catID, BOOL reloadCache) 65 { 66 HRESULT hr; 67 68 fCategory = catID; 69 if (reloadCache || !LoadFromRegistry()) 70 { 71 hr = LoadFromComCatMgr(); 72 if (FAILED_UNEXPECTEDLY(hr)) 73 return hr; 74 75 hr = CacheDSA(); 76 if (FAILED_UNEXPECTEDLY(hr)) 77 return hr; 78 } 79 return S_OK; 80 } 81 82 CComCatCachedCategory::~CComCatCachedCategory() 83 { 84 DSA_Destroy(fLocalDsa); 85 } 86 87 BOOL CComCatCachedCategory::LoadFromRegistry() 88 { 89 WCHAR bufKey[MAX_PATH]; 90 WCHAR guidStr[MAX_PATH]; 91 DWORD dataSize, i; 92 CComHeapPtr<CATCACHEHDR> buffer; 93 GUID *guidArray; 94 95 if (!fLocalDsa) 96 return FALSE; 97 98 dataSize = 0; 99 if (!StringFromGUID2(fCategory, guidStr, MAX_PATH)) 100 return FALSE; 101 102 wsprintf(bufKey, L"%s\\%s\\%s", REGPATH , guidStr, L"Enum"); 103 104 // Try to read key and get proper value size 105 if (SHGetValue(HKEY_CURRENT_USER, bufKey, IMPLEMENTING, NULL, NULL, &dataSize)) 106 return FALSE; 107 108 buffer.Attach((PCATCACHEHDR)CoTaskMemAlloc(dataSize)); 109 110 SHGetValue(HKEY_CURRENT_USER, bufKey, IMPLEMENTING, NULL, buffer, &dataSize); 111 guidArray = (GUID*)(buffer + 1); 112 for (i = 0; i < buffer->classCount; i++) 113 { 114 // Add class to cache 115 DSA_InsertItem(fLocalDsa, DSA_APPEND, guidArray + i); 116 } 117 118 return TRUE; 119 } 120 121 HRESULT CComCatCachedCategory::CacheDSA() 122 { 123 WCHAR bufKey[MAX_PATH]; 124 WCHAR guidStr[MAX_PATH]; 125 UINT elemCount; 126 UINT i; 127 UINT bufferSize; 128 CComHeapPtr<CATCACHEHDR> buffer; 129 GUID *guidArray; 130 GUID *tmp; 131 132 elemCount = DSA_GetItemCount(fLocalDsa); 133 bufferSize = sizeof(CATCACHEHDR) + elemCount * sizeof(GUID); 134 if (!StringFromGUID2(fCategory, guidStr, MAX_PATH)) 135 return E_FAIL; 136 137 buffer.Attach((PCATCACHEHDR)CoTaskMemAlloc(bufferSize)); 138 if (!buffer) 139 return E_OUTOFMEMORY; 140 141 // Correctly fill cache header 142 buffer->dwSize = sizeof(CATCACHEHDR); 143 buffer->version = 1; 144 GetSystemTime(&buffer->writeTime); 145 buffer->classCount = (DWORD)elemCount; 146 147 guidArray = (GUID*)(buffer + 1); 148 wsprintf(bufKey, L"%s\\%s\\%s", REGPATH , guidStr, L"Enum"); 149 150 // Write DSA contents inside the memory buffer allocated 151 for(i = 0; i < elemCount; i++) 152 { 153 tmp = (GUID*)DSA_GetItemPtr(fLocalDsa, i); 154 if (tmp) 155 { 156 guidArray[i] = *tmp; 157 } 158 } 159 160 // Save items to registry 161 SHSetValue(HKEY_CURRENT_USER, bufKey, IMPLEMENTING, REG_BINARY, buffer, bufferSize); 162 163 guidArray = NULL; 164 return S_OK; 165 } 166 167 HRESULT CComCatCachedCategory::LoadFromComCatMgr() 168 { 169 HRESULT hr; 170 CComPtr<ICatInformation> pCatInformation; 171 CComPtr<IEnumGUID> pEnumGUID; 172 ULONG pFetched; 173 CLSID tmp; 174 175 // Get component categories manager instance 176 hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, 177 IID_PPV_ARG(ICatInformation, &pCatInformation)); 178 if (FAILED_UNEXPECTEDLY(hr)) 179 return hr; 180 181 // Get the proper enumerator 182 hr = pCatInformation->EnumClassesOfCategories(1, &fCategory, NULL, NULL, &pEnumGUID); 183 if (FAILED_UNEXPECTEDLY(hr)) 184 return hr; 185 186 // Enumerate elements 187 do 188 { 189 pFetched = 0; 190 pEnumGUID->Next(1, &tmp, &pFetched); 191 if (pFetched) 192 { 193 if (DSA_InsertItem(fLocalDsa, DSA_APPEND, &tmp) == E_OUTOFMEMORY) 194 return E_OUTOFMEMORY; 195 } 196 } 197 while (pFetched > 0); 198 return S_OK; 199 } 200 201 HRESULT CComCatCachedCategory::WriteCacheToDSA(HDSA pDest) 202 { 203 INT i; 204 for(i = 0; i < DSA_GetItemCount(fLocalDsa); i++) 205 { 206 if (DSA_InsertItem(pDest, DSA_APPEND, DSA_GetItemPtr(fLocalDsa, i)) == DSA_ERR) 207 return E_OUTOFMEMORY; 208 } 209 return S_OK; 210 } 211 212 class CSHEnumClassesOfCategories : 213 public CComCoClass<CSHEnumClassesOfCategories>, 214 public CComObjectRootEx<CComMultiThreadModelNoCS>, 215 public IEnumGUID 216 { 217 private: 218 CComPtr<ICatInformation> fCatInformation; 219 HDSA fDsa; 220 ULONG fCursor; 221 222 public: 223 CSHEnumClassesOfCategories(); 224 virtual ~CSHEnumClassesOfCategories(); 225 STDMETHOD(Initialize)(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired); 226 227 // *** IEnumGUID methods *** 228 STDMETHOD(Clone)(IEnumCLSID **ppvOut) override; 229 STDMETHOD(Next)(ULONG cElt, CLSID *pElts, ULONG *pFetched) override; 230 STDMETHOD(Reset)() override; 231 STDMETHOD(Skip)(ULONG nbElts) override; 232 233 BEGIN_COM_MAP(CSHEnumClassesOfCategories) 234 COM_INTERFACE_ENTRY_IID(IID_IEnumGUID, IEnumGUID) 235 END_COM_MAP() 236 }; 237 238 CSHEnumClassesOfCategories::CSHEnumClassesOfCategories() 239 { 240 fCursor = 0; 241 fDsa = DSA_Create(sizeof(GUID), 5); 242 } 243 244 CSHEnumClassesOfCategories::~CSHEnumClassesOfCategories() 245 { 246 if (fDsa) 247 DSA_Destroy(fDsa); 248 } 249 250 HRESULT CSHEnumClassesOfCategories::Initialize(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired) 251 { 252 UINT i; 253 HRESULT hr; 254 255 if (!fDsa) 256 return E_FAIL; 257 258 // Parameter validation: 259 // - We must have at least one category to manage. 260 // - The array pointers must not be NULL if there is a non-zero 261 // element count specified for them. 262 if (cImplemented == 0 && cRequired == 0) 263 return E_INVALIDARG; 264 if ((cImplemented && !pImplemented) || (cRequired && !pRequired)) 265 return E_INVALIDARG; 266 267 // For each implemented category, create a cache and add it to our local DSA. 268 for (i = 0; i < cImplemented; i++) 269 { 270 CComCatCachedCategory cachedCat; 271 hr = cachedCat.Initialize(pImplemented[i], FALSE); 272 if (FAILED_UNEXPECTEDLY(hr)) 273 return hr; 274 cachedCat.WriteCacheToDSA(fDsa); 275 } 276 277 // TODO: Implement caching of the required categories. 278 if (cRequired > 0) 279 { 280 FIXME("Implement required categories class enumeration\n"); 281 282 // Only fail in case we didn't look at the implemented categories. 283 if (cImplemented == 0) 284 return E_NOTIMPL; 285 } 286 287 return S_OK; 288 } 289 290 // *** IEnumGUID methods *** 291 292 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Clone(IEnumCLSID **ppvOut) 293 { 294 return E_NOTIMPL; 295 } 296 297 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Next(ULONG cElt, CLSID *pElts, ULONG *pFetched) 298 { 299 ULONG i; 300 ULONG read; 301 GUID *tmp; 302 303 if (!pElts) 304 return E_INVALIDARG; 305 read = 0; 306 for (i = 0; i < cElt && (fCursor < (ULONG)DSA_GetItemCount(fDsa)); i++) 307 { 308 tmp = (GUID*)DSA_GetItemPtr(fDsa, fCursor + i); 309 if (!tmp) 310 break; 311 pElts[i] = *tmp; 312 read++; 313 } 314 fCursor += read; 315 if (pFetched) 316 *pFetched = read; 317 return S_OK; 318 } 319 320 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Reset() 321 { 322 fCursor = 0; 323 return S_OK; 324 } 325 326 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Skip(ULONG nbElts) 327 { 328 if (fCursor + nbElts >= (ULONG)DSA_GetItemCount(fDsa)) 329 return E_INVALIDARG; 330 fCursor += nbElts; 331 return S_OK; 332 } 333 334 /************************************************************************* 335 * SHEnumClassesOfCategories [BROWSEUI.136] 336 */ 337 extern "C" HRESULT WINAPI SHEnumClassesOfCategories(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired, IEnumGUID **out) 338 { 339 HRESULT hr; 340 341 if (!out) 342 return E_INVALIDARG; 343 344 hr = ShellObjectCreatorInit<CSHEnumClassesOfCategories>( 345 cImplemented, pImplemented, cRequired, pRequired, IID_PPV_ARG(IEnumGUID, out)); 346 if (FAILED_UNEXPECTEDLY(hr)) 347 return hr; 348 return S_OK; 349 } 350