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