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