cabinet: Add initial tests for FDI.
[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 ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
44 #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)
45 #define CHARS_IN_GUID 39
46
47 /******************************************************************************
48  * RegistryPropertyBag 
49  *
50  * Gives access to a registry key's values via the IPropertyBag interface.
51  */
52 typedef struct _RegistryPropertyBag {
53     const IPropertyBagVtbl *lpIPropertyBagVtbl;
54     LONG                   m_cRef;
55     HKEY                   m_hInitPropertyBagKey;
56 } RegistryPropertyBag;
57
58 static void RegistryPropertyBag_Destroy(RegistryPropertyBag *This) {
59     TRACE("This=%p)\n", This);
60
61     RegCloseKey(This->m_hInitPropertyBagKey);
62     shdocvw_free(This);
63 }
64
65 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface,
66     REFIID riid, void **ppv)
67 {
68     RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, IPropertyBag, iface);
69
70     TRACE("(iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv);
71
72     if (!ppv)
73         return E_INVALIDARG;
74
75     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
76         *ppv = STATIC_CAST(IPropertyBag, This);
77     } else {
78         *ppv = NULL;
79         return E_NOINTERFACE;
80     }
81
82     IUnknown_AddRef((IUnknown*)*ppv);
83     return S_OK;
84 }
85
86 static ULONG WINAPI RegistryPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
87     RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, IPropertyBag, iface);
88     ULONG cRef;
89
90     TRACE("(iface=%p)\n", iface);
91
92     cRef = InterlockedIncrement(&This->m_cRef);
93
94     if (cRef == 1)
95         SHDOCVW_LockModule();
96
97     return cRef;
98 }
99
100 static ULONG WINAPI RegistryPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
101     RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, IPropertyBag, iface);
102     ULONG cRef;
103
104     TRACE("(iface=%p)\n", iface);
105
106     cRef = InterlockedDecrement(&This->m_cRef);
107
108     if (cRef == 0) { 
109         RegistryPropertyBag_Destroy(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 = ADJUST_THIS(RegistryPropertyBag, 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 = shdocvw_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         shdocvw_free(pwszValue);
141         return E_INVALIDARG;
142     }
143
144     V_VT(pVar) = VT_BSTR;
145     V_BSTR(pVar) = SysAllocString(pwszValue);
146     shdocvw_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 = shdocvw_alloc(sizeof(RegistryPropertyBag));
180     if (pRegistryPropertyBag) {
181         pRegistryPropertyBag->lpIPropertyBagVtbl = &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(STATIC_CAST(IPropertyBag, pRegistryPropertyBag));
188         hr = IPropertyBag_QueryInterface(STATIC_CAST(IPropertyBag, pRegistryPropertyBag), riid, ppvObject);
189         IPropertyBag_Release(STATIC_CAST(IPropertyBag, pRegistryPropertyBag));
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     const IClassFactoryVtbl *lpIClassFactoryVtbl;
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 void InstanceObjectFactory_Destroy(InstanceObjectFactory *This) {
208     IPropertyBag_Release(This->m_pPropertyBag);
209     shdocvw_free(This);
210 }
211
212 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_QueryInterface(IClassFactory *iface, 
213     REFIID riid, LPVOID* ppv)
214 {
215     InstanceObjectFactory *This = ADJUST_THIS(InstanceObjectFactory, 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 = STATIC_CAST(IClassFactory, This);
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 = ADJUST_THIS(InstanceObjectFactory, 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 = ADJUST_THIS(InstanceObjectFactory, 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         InstanceObjectFactory_Destroy(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 = ADJUST_THIS(InstanceObjectFactory, 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 ProperyBag: 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 = shdocvw_alloc(sizeof(InstanceObjectFactory));
326     if (pInstanceObjectFactory) {
327         pInstanceObjectFactory->lpIClassFactoryVtbl = &InstanceObjectFactory_IClassFactoryVtbl;
328         pInstanceObjectFactory->m_cRef = 0;
329         memcpy(&pInstanceObjectFactory->m_clsidInstance, rclsid, sizeof(CLSID));
330         pInstanceObjectFactory->m_pPropertyBag = pPropertyBag;
331         IPropertyBag_AddRef(pPropertyBag);
332
333         IClassFactory_AddRef(STATIC_CAST(IClassFactory, pInstanceObjectFactory));
334         hr = IClassFactory_QueryInterface(STATIC_CAST(IClassFactory, pInstanceObjectFactory),
335                                           riid, ppvObject);
336         IClassFactory_Release(STATIC_CAST(IClassFactory, pInstanceObjectFactory));
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     const WCHAR wszCLSID[] = { 'C','L','S','I','D',0 };
364     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         !(wszInstanceKey[5+CHARS_IN_GUID]='\\') || /* Repair the null-termination. */
380         ERROR_SUCCESS != RegOpenKeyExW(HKEY_CLASSES_ROOT, wszInstanceKey, 0, KEY_READ, &hInstanceKey))
381     {
382         /* If there is no 'Instance' subkey, then it's not a Shell Instance Object. */
383         return CLASS_E_CLASSNOTAVAILABLE;
384     }
385
386     if (RegQueryValueExW(hInstanceKey, wszCLSID, NULL, &dwType, (LPBYTE)wszCLSIDInstance, &cbBytes)
387         != ERROR_SUCCESS || 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      * hInitProperyBagKey. */
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 }