[BROWSEUI] Fix CSHEnumClassesOfCategories::Initialize() parameters validation. (...
[reactos.git] / dll / win32 / browseui / shellbars / CSHEnumClassesOfCategories.cpp
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 virtual HRESULT STDMETHODCALLTYPE Initialize(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired);
226 // *** IEnumGUID methods ***
227 virtual HRESULT STDMETHODCALLTYPE Clone(IEnumCLSID **ppvOut);
228 virtual HRESULT STDMETHODCALLTYPE Next(ULONG cElt, CLSID *pElts, ULONG *pFetched);
229 virtual HRESULT STDMETHODCALLTYPE Reset();
230 virtual HRESULT STDMETHODCALLTYPE Skip(ULONG nbElts);
231
232 BEGIN_COM_MAP(CSHEnumClassesOfCategories)
233 COM_INTERFACE_ENTRY_IID(IID_IEnumGUID, IEnumGUID)
234 END_COM_MAP()
235 };
236
237 CSHEnumClassesOfCategories::CSHEnumClassesOfCategories()
238 {
239 fCursor = 0;
240 fDsa = DSA_Create(sizeof(GUID), 5);
241 }
242
243 CSHEnumClassesOfCategories::~CSHEnumClassesOfCategories()
244 {
245 if (fDsa)
246 DSA_Destroy(fDsa);
247 }
248
249 HRESULT CSHEnumClassesOfCategories::Initialize(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired)
250 {
251 UINT i;
252 HRESULT hr;
253
254 if (!fDsa)
255 return E_FAIL;
256
257 // Parameter validation:
258 // - We must have at least one category to manage.
259 // - The array pointers must not be NULL if there is a non-zero
260 // element count specified for them.
261 if (cImplemented == 0 && cRequired == 0)
262 return E_INVALIDARG;
263 if ((cImplemented && !pImplemented) || (cRequired && !pRequired))
264 return E_INVALIDARG;
265
266 // For each implemented category, create a cache and add it to our local DSA.
267 for (i = 0; i < cImplemented; i++)
268 {
269 CComCatCachedCategory cachedCat;
270 hr = cachedCat.Initialize(pImplemented[i], FALSE);
271 if (FAILED_UNEXPECTEDLY(hr))
272 return hr;
273 cachedCat.WriteCacheToDSA(fDsa);
274 }
275
276 // TODO: Implement caching of the required categories.
277 if (cRequired > 0)
278 {
279 FIXME("Implement required categories class enumeration\n");
280
281 // Only fail in case we didn't look at the implemented categories.
282 if (cImplemented == 0)
283 return E_NOTIMPL;
284 }
285
286 return S_OK;
287 }
288
289 // *** IEnumGUID methods ***
290
291 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Clone(IEnumCLSID **ppvOut)
292 {
293 return E_NOTIMPL;
294 }
295
296 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Next(ULONG cElt, CLSID *pElts, ULONG *pFetched)
297 {
298 ULONG i;
299 ULONG read;
300 GUID *tmp;
301
302 if (!pElts)
303 return E_INVALIDARG;
304 read = 0;
305 for (i = 0; i < cElt && (fCursor < (ULONG)DSA_GetItemCount(fDsa)); i++)
306 {
307 tmp = (GUID*)DSA_GetItemPtr(fDsa, fCursor + i);
308 if (!tmp)
309 break;
310 pElts[i] = *tmp;
311 read++;
312 }
313 fCursor += read;
314 if (pFetched)
315 *pFetched = read;
316 return S_OK;
317 }
318
319 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Reset()
320 {
321 fCursor = 0;
322 return S_OK;
323 }
324
325 HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Skip(ULONG nbElts)
326 {
327 if (fCursor + nbElts >= (ULONG)DSA_GetItemCount(fDsa))
328 return E_INVALIDARG;
329 fCursor += nbElts;
330 return S_OK;
331 }
332
333 /*************************************************************************
334 * SHEnumClassesOfCategories [BROWSEUI.136]
335 */
336 extern "C" HRESULT WINAPI SHEnumClassesOfCategories(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired, IEnumGUID **out)
337 {
338 HRESULT hr;
339
340 hr = ShellObjectCreatorInit<CSHEnumClassesOfCategories>(
341 cImplemented, pImplemented, cRequired, pRequired, IID_PPV_ARG(IEnumGUID, out));
342 if (FAILED_UNEXPECTEDLY(hr))
343 return hr;
344 return S_OK;
345 }