xref: /reactos/dll/win32/shdocvw/shlinstobj.c (revision 69931a4a)
1 /*
2  * Shell Instance Objects - Add hot water and stir until dissolved.
3  *
4  * Copyright 2005 Michael Jung
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 /* 'Shell Instance Objects' allow you to add a node to the shell namespace
22  * (typically a shortcut to some location in the filesystem), just by setting
23  * some registry entries. This feature was introduced with win2k. Please
24  * search for 'Shell Instance Objects' on MSDN to get more information. */
25 
26 #include <stdarg.h>
27 
28 #define COBJMACROS
29 
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "objbase.h"
34 #include "oleauto.h"
35 
36 #include "shdocvw.h"
37 
38 #include "wine/debug.h"
39 
40 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
41 
42 #define CHARS_IN_GUID 39
43 
44 /******************************************************************************
45  * RegistryPropertyBag
46  *
47  * Gives access to a registry key's values via the IPropertyBag interface.
48  */
49 typedef struct _RegistryPropertyBag {
50     IPropertyBag           IPropertyBag_iface;
51     LONG                   m_cRef;
52     HKEY                   m_hInitPropertyBagKey;
53 } RegistryPropertyBag;
54 
impl_from_IPropertyBag(IPropertyBag * iface)55 static inline RegistryPropertyBag *impl_from_IPropertyBag(IPropertyBag *iface)
56 {
57     return CONTAINING_RECORD(iface, RegistryPropertyBag, IPropertyBag_iface);
58 }
59 
RegistryPropertyBag_IPropertyBag_QueryInterface(IPropertyBag * iface,REFIID riid,void ** ppv)60 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface,
61     REFIID riid, void **ppv)
62 {
63     RegistryPropertyBag *This = impl_from_IPropertyBag(iface);
64 
65     TRACE("(iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv);
66 
67     if (!ppv)
68         return E_INVALIDARG;
69 
70     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
71         *ppv = &This->IPropertyBag_iface;
72     } else {
73         *ppv = NULL;
74         return E_NOINTERFACE;
75     }
76 
77     IUnknown_AddRef((IUnknown*)*ppv);
78     return S_OK;
79 }
80 
RegistryPropertyBag_IPropertyBag_AddRef(IPropertyBag * iface)81 static ULONG WINAPI RegistryPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface)
82 {
83     RegistryPropertyBag *This = impl_from_IPropertyBag(iface);
84     ULONG cRef;
85 
86     TRACE("(iface=%p)\n", iface);
87 
88     cRef = InterlockedIncrement(&This->m_cRef);
89 
90     if (cRef == 1)
91         SHDOCVW_LockModule();
92 
93     return cRef;
94 }
95 
RegistryPropertyBag_IPropertyBag_Release(IPropertyBag * iface)96 static ULONG WINAPI RegistryPropertyBag_IPropertyBag_Release(IPropertyBag *iface)
97 {
98     RegistryPropertyBag *This = impl_from_IPropertyBag(iface);
99     ULONG cRef;
100 
101     TRACE("(iface=%p)\n", iface);
102 
103     cRef = InterlockedDecrement(&This->m_cRef);
104 
105     if (cRef == 0) {
106         TRACE("Destroying This=%p)\n", This);
107         RegCloseKey(This->m_hInitPropertyBagKey);
108         heap_free(This);
109         SHDOCVW_UnlockModule();
110     }
111 
112     return cRef;
113 }
114 
RegistryPropertyBag_IPropertyBag_Read(IPropertyBag * iface,LPCOLESTR pwszPropName,VARIANT * pVar,IErrorLog * pErrorLog)115 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Read(IPropertyBag *iface,
116     LPCOLESTR pwszPropName, VARIANT *pVar, IErrorLog *pErrorLog)
117 {
118     RegistryPropertyBag *This = impl_from_IPropertyBag(iface);
119     WCHAR *pwszValue;
120     DWORD dwType, cbData;
121     LONG res;
122     VARTYPE vtDst = V_VT(pVar);
123     HRESULT hr = S_OK;
124 
125     TRACE("(iface=%p, pwszPropName=%s, pVar=%p, pErrorLog=%p)\n", iface, debugstr_w(pwszPropName),
126           pVar, pErrorLog);
127 
128     res = RegQueryValueExW(This->m_hInitPropertyBagKey, pwszPropName, NULL, &dwType, NULL, &cbData);
129     if (res != ERROR_SUCCESS)
130         return E_INVALIDARG;
131 
132     pwszValue = heap_alloc(cbData);
133     if (!pwszValue)
134         return E_OUTOFMEMORY;
135 
136     res = RegQueryValueExW(This->m_hInitPropertyBagKey, pwszPropName, NULL, &dwType,
137                            (LPBYTE)pwszValue, &cbData);
138     if (res != ERROR_SUCCESS) {
139         heap_free(pwszValue);
140         return E_INVALIDARG;
141     }
142 
143     V_VT(pVar) = VT_BSTR;
144     V_BSTR(pVar) = SysAllocString(pwszValue);
145     heap_free(pwszValue);
146 
147     if (vtDst != VT_BSTR) {
148         hr = VariantChangeTypeEx(pVar, pVar, LOCALE_SYSTEM_DEFAULT, 0, vtDst);
149         if (FAILED(hr))
150             SysFreeString(V_BSTR(pVar));
151     }
152 
153     return hr;
154 }
155 
RegistryPropertyBag_IPropertyBag_Write(IPropertyBag * iface,LPCOLESTR pwszPropName,VARIANT * pVar)156 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Write(IPropertyBag *iface,
157     LPCOLESTR pwszPropName, VARIANT *pVar)
158 {
159     FIXME("(iface=%p, pwszPropName=%s, pVar=%p) stub\n", iface, debugstr_w(pwszPropName), pVar);
160     return E_NOTIMPL;
161 }
162 
163 #ifdef __REACTOS__
164 static IPropertyBagVtbl RegistryPropertyBag_IPropertyBagVtbl = {
165 #else
166 static const IPropertyBagVtbl RegistryPropertyBag_IPropertyBagVtbl = {
167 #endif
168     RegistryPropertyBag_IPropertyBag_QueryInterface,
169     RegistryPropertyBag_IPropertyBag_AddRef,
170     RegistryPropertyBag_IPropertyBag_Release,
171     RegistryPropertyBag_IPropertyBag_Read,
172     RegistryPropertyBag_IPropertyBag_Write
173 };
174 
RegistryPropertyBag_Constructor(HKEY hInitPropertyBagKey,REFIID riid,LPVOID * ppvObject)175 static HRESULT RegistryPropertyBag_Constructor(HKEY hInitPropertyBagKey, REFIID riid, LPVOID *ppvObject) {
176     HRESULT hr = E_FAIL;
177     RegistryPropertyBag *pRegistryPropertyBag;
178 
179     TRACE("(hInitPropertyBagKey=%p, riid=%s, ppvObject=%p)\n", hInitPropertyBagKey,
180         debugstr_guid(riid), ppvObject);
181 
182     pRegistryPropertyBag = heap_alloc(sizeof(RegistryPropertyBag));
183     if (pRegistryPropertyBag) {
184         pRegistryPropertyBag->IPropertyBag_iface.lpVtbl = &RegistryPropertyBag_IPropertyBagVtbl;
185         pRegistryPropertyBag->m_cRef = 0;
186         pRegistryPropertyBag->m_hInitPropertyBagKey = hInitPropertyBagKey;
187 
188         /* The clasping AddRef/Release is for the case that QueryInterface fails, which will result
189          * in a reference count of 0 in the Release call, which will result in object destruction.*/
190         IPropertyBag_AddRef(&pRegistryPropertyBag->IPropertyBag_iface);
191         hr = IPropertyBag_QueryInterface(&pRegistryPropertyBag->IPropertyBag_iface, riid, ppvObject);
192         IPropertyBag_Release(&pRegistryPropertyBag->IPropertyBag_iface);
193     }
194 
195     return hr;
196 }
197 
198 /******************************************************************************
199  * InstanceObjectFactory
200  * Builds Instance Objects and asks them to initialize themselves based on the
201  * values of a PropertyBag.
202  */
203 typedef struct _InstanceObjectFactory {
204     IClassFactory           IClassFactory_iface;
205     LONG                    m_cRef;
206     CLSID                   m_clsidInstance; /* CLSID of the objects to create. */
207     IPropertyBag            *m_pPropertyBag; /* PropertyBag to initialize those objects. */
208 } InstanceObjectFactory;
209 
impl_from_IClassFactory(IClassFactory * iface)210 static inline InstanceObjectFactory *impl_from_IClassFactory(IClassFactory *iface)
211 {
212     return CONTAINING_RECORD(iface, InstanceObjectFactory, IClassFactory_iface);
213 }
214 
InstanceObjectFactory_IClassFactory_QueryInterface(IClassFactory * iface,REFIID riid,void ** ppv)215 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_QueryInterface(IClassFactory *iface,
216     REFIID riid, void **ppv)
217 {
218     InstanceObjectFactory *This = impl_from_IClassFactory(iface);
219 
220     TRACE("iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv);
221 
222     if (!ppv)
223         return E_INVALIDARG;
224 
225     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IClassFactory, riid)) {
226         *ppv = &This->IClassFactory_iface;
227     } else {
228         *ppv = NULL;
229         return E_NOINTERFACE;
230     }
231 
232     IUnknown_AddRef((IUnknown*)*ppv);
233     return S_OK;
234 }
235 
InstanceObjectFactory_IClassFactory_AddRef(IClassFactory * iface)236 static ULONG WINAPI InstanceObjectFactory_IClassFactory_AddRef(IClassFactory *iface)
237 {
238     InstanceObjectFactory *This = impl_from_IClassFactory(iface);
239     ULONG cRef;
240 
241     TRACE("(iface=%p)\n", iface);
242 
243     cRef = InterlockedIncrement(&This->m_cRef);
244 
245     if (cRef == 1)
246         IClassFactory_LockServer(iface, TRUE);
247 
248     return cRef;
249 }
250 
InstanceObjectFactory_IClassFactory_Release(IClassFactory * iface)251 static ULONG WINAPI InstanceObjectFactory_IClassFactory_Release(IClassFactory *iface)
252 {
253     InstanceObjectFactory *This = impl_from_IClassFactory(iface);
254     ULONG cRef;
255 
256     TRACE("(iface=%p)\n", iface);
257 
258     cRef = InterlockedDecrement(&This->m_cRef);
259 
260     if (cRef == 0) {
261         IClassFactory_LockServer(iface, FALSE);
262         IPropertyBag_Release(This->m_pPropertyBag);
263         heap_free(This);
264     }
265 
266     return cRef;
267 }
268 
InstanceObjectFactory_IClassFactory_CreateInstance(IClassFactory * iface,IUnknown * pUnkOuter,REFIID riid,LPVOID * ppvObj)269 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_CreateInstance(IClassFactory *iface,
270     IUnknown *pUnkOuter, REFIID riid, LPVOID *ppvObj)
271 {
272     InstanceObjectFactory *This = impl_from_IClassFactory(iface);
273     IPersistPropertyBag *pPersistPropertyBag;
274     HRESULT hr;
275 
276     TRACE("(pUnkOuter=%p, riid=%s, ppvObj=%p)\n", pUnkOuter, debugstr_guid(riid), ppvObj);
277 
278     hr = CoCreateInstance(&This->m_clsidInstance, NULL, CLSCTX_INPROC_SERVER,
279                           &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
280     if (FAILED(hr)) {
281         TRACE("Failed to create instance of %s. hr = %08x\n",
282               debugstr_guid(&This->m_clsidInstance), hr);
283         return hr;
284     }
285 
286     hr = IPersistPropertyBag_Load(pPersistPropertyBag, This->m_pPropertyBag, NULL);
287     if (FAILED(hr)) {
288         TRACE("Failed to initialize object from PropertyBag: hr = %08x\n", hr);
289         IPersistPropertyBag_Release(pPersistPropertyBag);
290         return hr;
291     }
292 
293     hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, riid, ppvObj);
294     IPersistPropertyBag_Release(pPersistPropertyBag);
295 
296     return hr;
297 }
298 
InstanceObjectFactory_IClassFactory_LockServer(IClassFactory * iface,BOOL fLock)299 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_LockServer(IClassFactory *iface,
300     BOOL fLock)
301 {
302     TRACE("(iface=%p, fLock=%d) stub\n", iface, fLock);
303 
304     if (fLock)
305         SHDOCVW_LockModule();
306     else
307         SHDOCVW_UnlockModule();
308 
309     return S_OK;
310 }
311 
312 #ifdef __REACTOS__
313 static IClassFactoryVtbl InstanceObjectFactory_IClassFactoryVtbl = {
314 #else
315 static const IClassFactoryVtbl InstanceObjectFactory_IClassFactoryVtbl = {
316 #endif
317     InstanceObjectFactory_IClassFactory_QueryInterface,
318     InstanceObjectFactory_IClassFactory_AddRef,
319     InstanceObjectFactory_IClassFactory_Release,
320     InstanceObjectFactory_IClassFactory_CreateInstance,
321     InstanceObjectFactory_IClassFactory_LockServer
322 };
323 
InstanceObjectFactory_Constructor(REFCLSID rclsid,IPropertyBag * pPropertyBag,REFIID riid,LPVOID * ppvObject)324 static HRESULT InstanceObjectFactory_Constructor(REFCLSID rclsid, IPropertyBag *pPropertyBag,
325                                                  REFIID riid, LPVOID *ppvObject)
326 {
327     InstanceObjectFactory *pInstanceObjectFactory;
328     HRESULT hr = E_FAIL;
329 
330     TRACE("(RegistryPropertyBag=%p, riid=%s, ppvObject=%p)\n", pPropertyBag,
331         debugstr_guid(riid), ppvObject);
332 
333     pInstanceObjectFactory = heap_alloc(sizeof(InstanceObjectFactory));
334     if (pInstanceObjectFactory) {
335         pInstanceObjectFactory->IClassFactory_iface.lpVtbl = &InstanceObjectFactory_IClassFactoryVtbl;
336         pInstanceObjectFactory->m_cRef = 0;
337         pInstanceObjectFactory->m_clsidInstance = *rclsid;
338         pInstanceObjectFactory->m_pPropertyBag = pPropertyBag;
339         IPropertyBag_AddRef(pPropertyBag);
340 
341         IClassFactory_AddRef(&pInstanceObjectFactory->IClassFactory_iface);
342         hr = IClassFactory_QueryInterface(&pInstanceObjectFactory->IClassFactory_iface,
343                                           riid, ppvObject);
344         IClassFactory_Release(&pInstanceObjectFactory->IClassFactory_iface);
345     }
346 
347     return hr;
348 }
349 
350 /******************************************************************************
351  * SHDOCVW_GetShellInstanceObjectClassObject [Internal]
352  *
353  *  Figure if there is a 'Shell Instance Object' conformant registry entry for
354  *  the given CLSID and if so create and return a corresponding ClassObject.
355  *
356  * PARAMS
357  *  rclsid      [I] CLSID of the 'Shell Instance Object'.
358  *  riid        [I] Desired interface. Only IClassFactory supported.
359  *  ppvClassObj [O] The corresponding ClassObject.
360  *
361  * RETURNS
362  *  Success: S_OK,
363  *  Failure: CLASS_E_CLASSNOTAVAILABLE
364  */
SHDOCVW_GetShellInstanceObjectClassObject(REFCLSID rclsid,REFIID riid,LPVOID * ppvClassObj)365 HRESULT SHDOCVW_GetShellInstanceObjectClassObject(REFCLSID rclsid, REFIID riid,
366     LPVOID *ppvClassObj)
367 {
368     WCHAR wszInstanceKey[] = { 'C','L','S','I','D','\\','{','0','0','0','0','0','0','0','0','-',
369         '0','0','0','0','-','0','0','0','0','-','0','0','0','0','-','0','0','0','0','0','0','0','0',
370         '0','0','0','0','}','\\','I','n','s','t','a','n','c','e', 0 };
371     static const WCHAR wszCLSID[] = { 'C','L','S','I','D',0 };
372     static const WCHAR wszInitPropertyBag[] =
373         { 'I','n','i','t','P','r','o','p','e','r','t','y','B','a','g',0 };
374     WCHAR wszCLSIDInstance[CHARS_IN_GUID];
375     CLSID clsidInstance;
376     HKEY hInstanceKey, hInitPropertyBagKey;
377     DWORD dwType, cbBytes = sizeof(wszCLSIDInstance);
378     IPropertyBag *pInitPropertyBag;
379     HRESULT hr;
380     LONG res;
381 
382     TRACE("(rclsid=%s, riid=%s, ppvClassObject=%p)\n", debugstr_guid(rclsid), debugstr_guid(riid),
383           ppvClassObj);
384 
385     /* Figure if there is an 'Instance' subkey for the given CLSID and acquire a handle. */
386     if (!StringFromGUID2(rclsid, wszInstanceKey + 6, CHARS_IN_GUID))
387         return CLASS_E_CLASSNOTAVAILABLE;
388     wszInstanceKey[5+CHARS_IN_GUID] = '\\'; /* Repair the null-termination. */
389     if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_CLASSES_ROOT, wszInstanceKey, 0, KEY_READ, &hInstanceKey))
390         /* If there is no 'Instance' subkey, then it's not a Shell Instance Object. */
391         return CLASS_E_CLASSNOTAVAILABLE;
392 
393     if (ERROR_SUCCESS != RegQueryValueExW(hInstanceKey, wszCLSID, NULL, &dwType, (LPBYTE)wszCLSIDInstance, &cbBytes) ||
394         FAILED(CLSIDFromString(wszCLSIDInstance, &clsidInstance)))
395     {
396         /* 'Instance' should have a 'CLSID' value with a well-formed clsid-string. */
397         FIXME("Failed to infer instance CLSID! %s\n", debugstr_w(wszCLSIDInstance));
398         RegCloseKey(hInstanceKey);
399         return CLASS_E_CLASSNOTAVAILABLE;
400     }
401 
402     /* Try to open the 'InitPropertyBag' subkey. */
403     res = RegOpenKeyExW(hInstanceKey, wszInitPropertyBag, 0, KEY_READ, &hInitPropertyBagKey);
404     RegCloseKey(hInstanceKey);
405     if (res != ERROR_SUCCESS) {
406         /* Besides 'InitPropertyBag's, shell instance objects might be initialized by streams.
407          * So this case might not be an error. */
408         TRACE("No InitPropertyBag key found!\n");
409         return CLASS_E_CLASSNOTAVAILABLE;
410     }
411 
412     /* If the construction succeeds, the new RegistryPropertyBag is responsible for closing
413      * hInitPropertyBagKey. */
414     hr = RegistryPropertyBag_Constructor(hInitPropertyBagKey, &IID_IPropertyBag,
415                                          (LPVOID*)&pInitPropertyBag);
416     if (FAILED(hr)) {
417         RegCloseKey(hInitPropertyBagKey);
418         return hr;
419     }
420 
421     /* Construct an Instance Object Factory, which creates objects of class 'clsidInstance'
422      * and asks them to initialize themselves with the help of the 'pInitiPropertyBag' */
423     hr = InstanceObjectFactory_Constructor(&clsidInstance, pInitPropertyBag, riid, ppvClassObj);
424     IPropertyBag_Release(pInitPropertyBag); /* The factory will hold a reference the bag. */
425 
426     return hr;
427 }
428