xref: /reactos/dll/win32/shdocvw/shlinstobj.c (revision f22fa382)
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 
55 static inline RegistryPropertyBag *impl_from_IPropertyBag(IPropertyBag *iface)
56 {
57     return CONTAINING_RECORD(iface, RegistryPropertyBag, IPropertyBag_iface);
58 }
59 
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 
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 
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 
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 
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 static const IPropertyBagVtbl RegistryPropertyBag_IPropertyBagVtbl = {
164     RegistryPropertyBag_IPropertyBag_QueryInterface,
165     RegistryPropertyBag_IPropertyBag_AddRef,
166     RegistryPropertyBag_IPropertyBag_Release,
167     RegistryPropertyBag_IPropertyBag_Read,
168     RegistryPropertyBag_IPropertyBag_Write
169 };
170 
171 static HRESULT RegistryPropertyBag_Constructor(HKEY hInitPropertyBagKey, REFIID riid, LPVOID *ppvObject) {
172     HRESULT hr = E_FAIL;
173     RegistryPropertyBag *pRegistryPropertyBag;
174 
175     TRACE("(hInitPropertyBagKey=%p, riid=%s, ppvObject=%p)\n", hInitPropertyBagKey,
176         debugstr_guid(riid), ppvObject);
177 
178     pRegistryPropertyBag = heap_alloc(sizeof(RegistryPropertyBag));
179     if (pRegistryPropertyBag) {
180         pRegistryPropertyBag->IPropertyBag_iface.lpVtbl = &RegistryPropertyBag_IPropertyBagVtbl;
181         pRegistryPropertyBag->m_cRef = 0;
182         pRegistryPropertyBag->m_hInitPropertyBagKey = hInitPropertyBagKey;
183 
184         /* The clasping AddRef/Release is for the case that QueryInterface fails, which will result
185          * in a reference count of 0 in the Release call, which will result in object destruction.*/
186         IPropertyBag_AddRef(&pRegistryPropertyBag->IPropertyBag_iface);
187         hr = IPropertyBag_QueryInterface(&pRegistryPropertyBag->IPropertyBag_iface, riid, ppvObject);
188         IPropertyBag_Release(&pRegistryPropertyBag->IPropertyBag_iface);
189     }
190 
191     return hr;
192 }
193 
194 /******************************************************************************
195  * InstanceObjectFactory
196  * Builds Instance Objects and asks them to initialize themselves based on the
197  * values of a PropertyBag.
198  */
199 typedef struct _InstanceObjectFactory {
200     IClassFactory           IClassFactory_iface;
201     LONG                    m_cRef;
202     CLSID                   m_clsidInstance; /* CLSID of the objects to create. */
203     IPropertyBag            *m_pPropertyBag; /* PropertyBag to initialize those objects. */
204 } InstanceObjectFactory;
205 
206 static inline InstanceObjectFactory *impl_from_IClassFactory(IClassFactory *iface)
207 {
208     return CONTAINING_RECORD(iface, InstanceObjectFactory, IClassFactory_iface);
209 }
210 
211 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_QueryInterface(IClassFactory *iface,
212     REFIID riid, void **ppv)
213 {
214     InstanceObjectFactory *This = impl_from_IClassFactory(iface);
215 
216     TRACE("iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv);
217 
218     if (!ppv)
219         return E_INVALIDARG;
220 
221     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IClassFactory, riid)) {
222         *ppv = &This->IClassFactory_iface;
223     } else {
224         *ppv = NULL;
225         return E_NOINTERFACE;
226     }
227 
228     IUnknown_AddRef((IUnknown*)*ppv);
229     return S_OK;
230 }
231 
232 static ULONG WINAPI InstanceObjectFactory_IClassFactory_AddRef(IClassFactory *iface)
233 {
234     InstanceObjectFactory *This = impl_from_IClassFactory(iface);
235     ULONG cRef;
236 
237     TRACE("(iface=%p)\n", iface);
238 
239     cRef = InterlockedIncrement(&This->m_cRef);
240 
241     if (cRef == 1)
242         IClassFactory_LockServer(iface, TRUE);
243 
244     return cRef;
245 }
246 
247 static ULONG WINAPI InstanceObjectFactory_IClassFactory_Release(IClassFactory *iface)
248 {
249     InstanceObjectFactory *This = impl_from_IClassFactory(iface);
250     ULONG cRef;
251 
252     TRACE("(iface=%p)\n", iface);
253 
254     cRef = InterlockedDecrement(&This->m_cRef);
255 
256     if (cRef == 0) {
257         IClassFactory_LockServer(iface, FALSE);
258         IPropertyBag_Release(This->m_pPropertyBag);
259         heap_free(This);
260     }
261 
262     return cRef;
263 }
264 
265 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_CreateInstance(IClassFactory *iface,
266     IUnknown *pUnkOuter, REFIID riid, LPVOID *ppvObj)
267 {
268     InstanceObjectFactory *This = impl_from_IClassFactory(iface);
269     IPersistPropertyBag *pPersistPropertyBag;
270     HRESULT hr;
271 
272     TRACE("(pUnkOuter=%p, riid=%s, ppvObj=%p)\n", pUnkOuter, debugstr_guid(riid), ppvObj);
273 
274     hr = CoCreateInstance(&This->m_clsidInstance, NULL, CLSCTX_INPROC_SERVER,
275                           &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
276     if (FAILED(hr)) {
277         TRACE("Failed to create instance of %s. hr = %08x\n",
278               debugstr_guid(&This->m_clsidInstance), hr);
279         return hr;
280     }
281 
282     hr = IPersistPropertyBag_Load(pPersistPropertyBag, This->m_pPropertyBag, NULL);
283     if (FAILED(hr)) {
284         TRACE("Failed to initialize object from PropertyBag: hr = %08x\n", hr);
285         IPersistPropertyBag_Release(pPersistPropertyBag);
286         return hr;
287     }
288 
289     hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, riid, ppvObj);
290     IPersistPropertyBag_Release(pPersistPropertyBag);
291 
292     return hr;
293 }
294 
295 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_LockServer(IClassFactory *iface,
296     BOOL fLock)
297 {
298     TRACE("(iface=%p, fLock=%d) stub\n", iface, fLock);
299 
300     if (fLock)
301         SHDOCVW_LockModule();
302     else
303         SHDOCVW_UnlockModule();
304 
305     return S_OK;
306 }
307 
308 static const IClassFactoryVtbl InstanceObjectFactory_IClassFactoryVtbl = {
309     InstanceObjectFactory_IClassFactory_QueryInterface,
310     InstanceObjectFactory_IClassFactory_AddRef,
311     InstanceObjectFactory_IClassFactory_Release,
312     InstanceObjectFactory_IClassFactory_CreateInstance,
313     InstanceObjectFactory_IClassFactory_LockServer
314 };
315 
316 static HRESULT InstanceObjectFactory_Constructor(REFCLSID rclsid, IPropertyBag *pPropertyBag,
317                                                  REFIID riid, LPVOID *ppvObject)
318 {
319     InstanceObjectFactory *pInstanceObjectFactory;
320     HRESULT hr = E_FAIL;
321 
322     TRACE("(RegistryPropertyBag=%p, riid=%s, ppvObject=%p)\n", pPropertyBag,
323         debugstr_guid(riid), ppvObject);
324 
325     pInstanceObjectFactory = heap_alloc(sizeof(InstanceObjectFactory));
326     if (pInstanceObjectFactory) {
327         pInstanceObjectFactory->IClassFactory_iface.lpVtbl = &InstanceObjectFactory_IClassFactoryVtbl;
328         pInstanceObjectFactory->m_cRef = 0;
329         pInstanceObjectFactory->m_clsidInstance = *rclsid;
330         pInstanceObjectFactory->m_pPropertyBag = pPropertyBag;
331         IPropertyBag_AddRef(pPropertyBag);
332 
333         IClassFactory_AddRef(&pInstanceObjectFactory->IClassFactory_iface);
334         hr = IClassFactory_QueryInterface(&pInstanceObjectFactory->IClassFactory_iface,
335                                           riid, ppvObject);
336         IClassFactory_Release(&pInstanceObjectFactory->IClassFactory_iface);
337     }
338 
339     return hr;
340 }
341 
342 /******************************************************************************
343  * SHDOCVW_GetShellInstanceObjectClassObject [Internal]
344  *
345  *  Figure if there is a 'Shell Instance Object' conformant registry entry for
346  *  the given CLSID and if so create and return a corresponding ClassObject.
347  *
348  * PARAMS
349  *  rclsid      [I] CLSID of the 'Shell Instance Object'.
350  *  riid        [I] Desired interface. Only IClassFactory supported.
351  *  ppvClassObj [O] The corresponding ClassObject.
352  *
353  * RETURNS
354  *  Success: S_OK,
355  *  Failure: CLASS_E_CLASSNOTAVAILABLE
356  */
357 HRESULT SHDOCVW_GetShellInstanceObjectClassObject(REFCLSID rclsid, REFIID riid,
358     LPVOID *ppvClassObj)
359 {
360     WCHAR wszInstanceKey[] = { 'C','L','S','I','D','\\','{','0','0','0','0','0','0','0','0','-',
361         '0','0','0','0','-','0','0','0','0','-','0','0','0','0','-','0','0','0','0','0','0','0','0',
362         '0','0','0','0','}','\\','I','n','s','t','a','n','c','e', 0 };
363     static const WCHAR wszCLSID[] = { 'C','L','S','I','D',0 };
364     static const WCHAR wszInitPropertyBag[] =
365         { 'I','n','i','t','P','r','o','p','e','r','t','y','B','a','g',0 };
366     WCHAR wszCLSIDInstance[CHARS_IN_GUID];
367     CLSID clsidInstance;
368     HKEY hInstanceKey, hInitPropertyBagKey;
369     DWORD dwType, cbBytes = sizeof(wszCLSIDInstance);
370     IPropertyBag *pInitPropertyBag;
371     HRESULT hr;
372     LONG res;
373 
374     TRACE("(rclsid=%s, riid=%s, ppvClassObject=%p)\n", debugstr_guid(rclsid), debugstr_guid(riid),
375           ppvClassObj);
376 
377     /* Figure if there is an 'Instance' subkey for the given CLSID and acquire a handle. */
378     if (!StringFromGUID2(rclsid, wszInstanceKey + 6, CHARS_IN_GUID))
379         return CLASS_E_CLASSNOTAVAILABLE;
380     wszInstanceKey[5+CHARS_IN_GUID] = '\\'; /* Repair the null-termination. */
381     if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_CLASSES_ROOT, wszInstanceKey, 0, KEY_READ, &hInstanceKey))
382         /* If there is no 'Instance' subkey, then it's not a Shell Instance Object. */
383         return CLASS_E_CLASSNOTAVAILABLE;
384 
385     if (ERROR_SUCCESS != RegQueryValueExW(hInstanceKey, wszCLSID, NULL, &dwType, (LPBYTE)wszCLSIDInstance, &cbBytes) ||
386         FAILED(CLSIDFromString(wszCLSIDInstance, &clsidInstance)))
387     {
388         /* 'Instance' should have a 'CLSID' value with a well-formed clsid-string. */
389         FIXME("Failed to infer instance CLSID! %s\n", debugstr_w(wszCLSIDInstance));
390         RegCloseKey(hInstanceKey);
391         return CLASS_E_CLASSNOTAVAILABLE;
392     }
393 
394     /* Try to open the 'InitPropertyBag' subkey. */
395     res = RegOpenKeyExW(hInstanceKey, wszInitPropertyBag, 0, KEY_READ, &hInitPropertyBagKey);
396     RegCloseKey(hInstanceKey);
397     if (res != ERROR_SUCCESS) {
398         /* Besides 'InitPropertyBag's, shell instance objects might be initialized by streams.
399          * So this case might not be an error. */
400         TRACE("No InitPropertyBag key found!\n");
401         return CLASS_E_CLASSNOTAVAILABLE;
402     }
403 
404     /* If the construction succeeds, the new RegistryPropertyBag is responsible for closing
405      * hInitPropertyBagKey. */
406     hr = RegistryPropertyBag_Constructor(hInitPropertyBagKey, &IID_IPropertyBag,
407                                          (LPVOID*)&pInitPropertyBag);
408     if (FAILED(hr)) {
409         RegCloseKey(hInitPropertyBagKey);
410         return hr;
411     }
412 
413     /* Construct an Instance Object Factory, which creates objects of class 'clsidInstance'
414      * and asks them to initialize themselves with the help of the 'pInitiPropertyBag' */
415     hr = InstanceObjectFactory_Constructor(&clsidInstance, pInitPropertyBag, riid, ppvClassObj);
416     IPropertyBag_Release(pInitPropertyBag); /* The factory will hold a reference the bag. */
417 
418     return hr;
419 }
420