shdocvw: Wrap Heap* finctions by inline functions.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 #define COM_NO_WINDOWS_H
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winreg.h"
34 #include "objbase.h"
35 #include "oleauto.h"
36
37 #include "shdocvw.h"
38
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
43
44 #define ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
45 #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)
46 #define CHARS_IN_GUID 39
47
48 /******************************************************************************
49  * RegistryPropertyBag 
50  *
51  * Gives access to a registry key's values via the IPropertyBag interface.
52  */
53 typedef struct _RegistryPropertyBag {
54     const IPropertyBagVtbl *lpIPropertyBagVtbl;
55     LONG                   m_cRef;
56     HKEY                   m_hInitPropertyBagKey;
57 } RegistryPropertyBag;
58
59 static void RegistryPropertyBag_Destroy(RegistryPropertyBag *This) {
60     TRACE("This=%p)\n", This);
61
62     RegCloseKey(This->m_hInitPropertyBagKey);
63     shdocvw_free(This);
64 }
65
66 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface,
67     REFIID riid, void **ppv)
68 {
69     RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, IPropertyBag, iface);
70
71     TRACE("(iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv);
72
73     if (!ppv)
74         return E_INVALIDARG;
75
76     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
77         *ppv = STATIC_CAST(IPropertyBag, This);
78     } else {
79         *ppv = NULL;
80         return E_NOINTERFACE;
81     }
82
83     IUnknown_AddRef((IUnknown*)*ppv);
84     return S_OK;
85 }
86
87 static ULONG WINAPI RegistryPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
88     RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, IPropertyBag, iface);
89     ULONG cRef;
90
91     TRACE("(iface=%p)\n", iface);
92
93     cRef = InterlockedIncrement(&This->m_cRef);
94
95     if (cRef == 1)
96         SHDOCVW_LockModule();
97
98     return cRef;
99 }
100
101 static ULONG WINAPI RegistryPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
102     RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, IPropertyBag, iface);
103     ULONG cRef;
104
105     TRACE("(iface=%p)\n", iface);
106
107     cRef = InterlockedDecrement(&This->m_cRef);
108
109     if (cRef == 0) { 
110         RegistryPropertyBag_Destroy(This);
111         SHDOCVW_UnlockModule();
112     }
113
114     return cRef;
115 }
116
117 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Read(IPropertyBag *iface,
118     LPCOLESTR pwszPropName, VARIANT *pVar, IErrorLog *pErrorLog)
119 {
120     RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, IPropertyBag, iface);
121     WCHAR *pwszValue;
122     DWORD dwType, cbData;
123     LONG res;
124     VARTYPE vtDst = V_VT(pVar);
125     HRESULT hr = S_OK;
126
127     TRACE("(iface=%p, pwszPropName=%s, pVar=%p, pErrorLog=%p)\n", iface, debugstr_w(pwszPropName), 
128           pVar, pErrorLog);
129
130     res = RegQueryValueExW(This->m_hInitPropertyBagKey, pwszPropName, NULL, &dwType, NULL, &cbData);
131     if (res != ERROR_SUCCESS) 
132         return E_INVALIDARG;
133
134     pwszValue = shdocvw_alloc(cbData);
135     if (!pwszValue)
136         return E_OUTOFMEMORY;
137  
138     res = RegQueryValueExW(This->m_hInitPropertyBagKey, pwszPropName, NULL, &dwType, 
139                            (LPBYTE)pwszValue, &cbData);
140     if (res != ERROR_SUCCESS) {
141         shdocvw_free(pwszValue);
142         return E_INVALIDARG;
143     }
144
145     V_VT(pVar) = VT_BSTR;
146     V_BSTR(pVar) = SysAllocString(pwszValue);
147     shdocvw_free(pwszValue);
148
149     if (vtDst != VT_BSTR) {
150         hr = VariantChangeTypeEx(pVar, pVar, LOCALE_SYSTEM_DEFAULT, 0, vtDst);
151         if (FAILED(hr))
152             SysFreeString(V_BSTR(pVar));
153     }
154
155     return hr;    
156 }
157
158 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Write(IPropertyBag *iface, 
159     LPCOLESTR pwszPropName, VARIANT *pVar)
160 {
161     FIXME("(iface=%p, pwszPropName=%s, pVar=%p) stub\n", iface, debugstr_w(pwszPropName), pVar);
162     return E_NOTIMPL;
163 }
164
165 static const IPropertyBagVtbl RegistryPropertyBag_IPropertyBagVtbl = {
166     RegistryPropertyBag_IPropertyBag_QueryInterface,
167     RegistryPropertyBag_IPropertyBag_AddRef,
168     RegistryPropertyBag_IPropertyBag_Release,
169     RegistryPropertyBag_IPropertyBag_Read,
170     RegistryPropertyBag_IPropertyBag_Write
171 };
172
173 HRESULT RegistryPropertyBag_Constructor(HKEY hInitPropertyBagKey, REFIID riid, LPVOID *ppvObject) {
174     HRESULT hr = E_FAIL;
175     RegistryPropertyBag *pRegistryPropertyBag;
176
177     TRACE("(hInitPropertyBagKey=%p, riid=%s, ppvObject=%p)\n", hInitPropertyBagKey, 
178         debugstr_guid(riid), ppvObject);
179     
180     pRegistryPropertyBag = shdocvw_alloc(sizeof(RegistryPropertyBag));
181     if (pRegistryPropertyBag) {
182         pRegistryPropertyBag->lpIPropertyBagVtbl = &RegistryPropertyBag_IPropertyBagVtbl;
183         pRegistryPropertyBag->m_cRef = 0;
184         pRegistryPropertyBag->m_hInitPropertyBagKey = hInitPropertyBagKey;
185
186         /* The clasping AddRef/Release is for the case that QueryInterface fails, which will result
187          * in a reference count of 0 in the Release call, which will result in object destruction.*/
188         IPropertyBag_AddRef(STATIC_CAST(IPropertyBag, pRegistryPropertyBag));
189         hr = IPropertyBag_QueryInterface(STATIC_CAST(IPropertyBag, pRegistryPropertyBag), riid, ppvObject);
190         IPropertyBag_Release(STATIC_CAST(IPropertyBag, pRegistryPropertyBag));
191     }
192
193     return hr;
194 }
195
196 /******************************************************************************
197  * InstanceObjectFactory
198  * Builds Instance Objects and asks them to initialize themselves based on the
199  * values of a PropertyBag.
200  */
201 typedef struct _InstanceObjectFactory {
202     const IClassFactoryVtbl *lpIClassFactoryVtbl;
203     LONG                    m_cRef;
204     CLSID                   m_clsidInstance; /* CLSID of the objects to create. */
205     IPropertyBag            *m_pPropertyBag; /* PropertyBag to initialize those objects. */
206 } InstanceObjectFactory;
207
208 static void InstanceObjectFactory_Destroy(InstanceObjectFactory *This) {
209     IPropertyBag_Release(This->m_pPropertyBag);
210     shdocvw_free(This);
211 }
212
213 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_QueryInterface(IClassFactory *iface, 
214     REFIID riid, LPVOID* ppv)
215 {
216     InstanceObjectFactory *This = ADJUST_THIS(InstanceObjectFactory, IClassFactory, iface);
217
218     TRACE("iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv);
219
220     if (!ppv)
221         return E_INVALIDARG;
222
223     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IClassFactory, riid)) {
224         *ppv = STATIC_CAST(IClassFactory, This);
225     } else {
226         *ppv = NULL;
227         return E_NOINTERFACE;
228     }
229
230     IUnknown_AddRef((IUnknown*)*ppv);
231     return S_OK;
232 }
233     
234 static ULONG WINAPI InstanceObjectFactory_IClassFactory_AddRef(IClassFactory *iface)
235 {
236     InstanceObjectFactory *This = ADJUST_THIS(InstanceObjectFactory, IClassFactory, iface);
237     ULONG cRef;
238
239     TRACE("(iface=%p)\n", iface);
240
241     cRef = InterlockedIncrement(&This->m_cRef);
242
243     if (cRef == 1)
244         IClassFactory_LockServer(iface, TRUE);
245
246     return cRef;
247 }
248
249 static ULONG WINAPI InstanceObjectFactory_IClassFactory_Release(IClassFactory *iface)
250 {
251     InstanceObjectFactory *This = ADJUST_THIS(InstanceObjectFactory, IClassFactory, iface);
252     ULONG cRef;
253
254     TRACE("(iface=%p)\n", iface);
255
256     cRef = InterlockedDecrement(&This->m_cRef);
257
258     if (cRef == 0) { 
259         IClassFactory_LockServer(iface, FALSE);
260         InstanceObjectFactory_Destroy(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 = ADJUST_THIS(InstanceObjectFactory, 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 = %08lx\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 ProperyBag: hr = %08lx\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 HRESULT InstanceObjectFactory_Constructor(REFCLSID rclsid, IPropertyBag *pPropertyBag, REFIID riid,
318     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 = shdocvw_alloc(sizeof(InstanceObjectFactory));
327     if (pInstanceObjectFactory) {
328         pInstanceObjectFactory->lpIClassFactoryVtbl = &InstanceObjectFactory_IClassFactoryVtbl;
329         pInstanceObjectFactory->m_cRef = 0;
330         memcpy(&pInstanceObjectFactory->m_clsidInstance, rclsid, sizeof(CLSID));
331         pInstanceObjectFactory->m_pPropertyBag = pPropertyBag;
332         IPropertyBag_AddRef(pPropertyBag);
333
334         IClassFactory_AddRef(STATIC_CAST(IClassFactory, pInstanceObjectFactory));
335         hr = IClassFactory_QueryInterface(STATIC_CAST(IClassFactory, pInstanceObjectFactory),
336                                           riid, ppvObject);
337         IClassFactory_Release(STATIC_CAST(IClassFactory, pInstanceObjectFactory));
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 aquire a handle. */
379     if (!StringFromGUID2(rclsid, wszInstanceKey + 6, CHARS_IN_GUID) ||
380         !(wszInstanceKey[5+CHARS_IN_GUID]='\\') || /* Repair the null-termination. */
381         ERROR_SUCCESS != RegOpenKeyExW(HKEY_CLASSES_ROOT, wszInstanceKey, 0, KEY_READ, &hInstanceKey))
382     {
383         /* If there is no 'Instance' subkey, then it's not a Shell Instance Object. */
384         return CLASS_E_CLASSNOTAVAILABLE;
385     }
386
387     if (RegQueryValueExW(hInstanceKey, wszCLSID, NULL, &dwType, (LPBYTE)wszCLSIDInstance, &cbBytes)
388         != ERROR_SUCCESS || FAILED(CLSIDFromString(wszCLSIDInstance, &clsidInstance)))
389     {
390         /* 'Instance' should have a 'CLSID' value with a well-formed clsid-string. */
391         FIXME("Failed to infer instance CLSID! %s\n", debugstr_w(wszCLSIDInstance));
392         RegCloseKey(hInstanceKey);
393         return CLASS_E_CLASSNOTAVAILABLE;
394     }
395
396     /* Try to open the 'InitPropertyBag' subkey. */
397     res = RegOpenKeyExW(hInstanceKey, wszInitPropertyBag, 0, KEY_READ, &hInitPropertyBagKey);
398     RegCloseKey(hInstanceKey);
399     if (res != ERROR_SUCCESS) {
400         /* Besides 'InitPropertyBag's, shell instance objects might be initialized by streams.
401          * So this case might not be an error. */
402         TRACE("No InitPropertyBag key found!\n");
403         return CLASS_E_CLASSNOTAVAILABLE;
404     }
405
406     /* If the construction succeeds, the new RegistryPropertyBag is responsible for closing 
407      * hInitProperyBagKey. */
408     hr = RegistryPropertyBag_Constructor(hInitPropertyBagKey, &IID_IPropertyBag, 
409                                          (LPVOID*)&pInitPropertyBag);
410     if (FAILED(hr)) {
411         RegCloseKey(hInitPropertyBagKey);
412         return hr;
413     }
414
415     /* Construct an Instance Object Factory, which creates objects of class 'clsidInstance'
416      * and asks them to initialize themselves with the help of the 'pInitiPropertyBag' */
417     hr = InstanceObjectFactory_Constructor(&clsidInstance, pInitPropertyBag, riid, ppvClassObj);
418     IPropertyBag_Release(pInitPropertyBag); /* The factory will hold a reference the bag. */
419         
420     return hr;
421 }