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
CComCatCachedCategory()59 CComCatCachedCategory::CComCatCachedCategory()
60 {
61 fLocalDsa = DSA_Create(sizeof(GUID), 5);
62 }
63
Initialize(CATID & catID,BOOL reloadCache)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
~CComCatCachedCategory()82 CComCatCachedCategory::~CComCatCachedCategory()
83 {
84 DSA_Destroy(fLocalDsa);
85 }
86
LoadFromRegistry()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
CacheDSA()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
LoadFromComCatMgr()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
WriteCacheToDSA(HDSA pDest)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
CSHEnumClassesOfCategories()238 CSHEnumClassesOfCategories::CSHEnumClassesOfCategories()
239 {
240 fCursor = 0;
241 fDsa = DSA_Create(sizeof(GUID), 5);
242 }
243
~CSHEnumClassesOfCategories()244 CSHEnumClassesOfCategories::~CSHEnumClassesOfCategories()
245 {
246 if (fDsa)
247 DSA_Destroy(fDsa);
248 }
249
Initialize(ULONG cImplemented,CATID * pImplemented,ULONG cRequired,CATID * pRequired)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
Clone(IEnumCLSID ** ppvOut)292 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Clone(IEnumCLSID **ppvOut)
293 {
294 return E_NOTIMPL;
295 }
296
Next(ULONG cElt,CLSID * pElts,ULONG * pFetched)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
Reset()320 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Reset()
321 {
322 fCursor = 0;
323 return S_OK;
324 }
325
Skip(ULONG nbElts)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 */
SHEnumClassesOfCategories(ULONG cImplemented,CATID * pImplemented,ULONG cRequired,CATID * pRequired,IEnumGUID ** out)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