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