ole32: Add tests for WdtpInterfacePointer_* functions.
[wine] / dlls / ole32 / ftmarshal.c
1 /*
2  *      free threaded marshaller
3  *
4  *  Copyright 2002  Juergen Schmied
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 #include "config.h"
22
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #define COBJMACROS
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "objbase.h"
34
35 #include "wine/debug.h"
36
37 #include "compobj_private.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(ole);
40
41 typedef struct _FTMarshalImpl {
42         const IUnknownVtbl *lpVtbl;
43         LONG ref;
44         const IMarshalVtbl *lpvtblFTM;
45
46         IUnknown *pUnkOuter;
47 } FTMarshalImpl;
48
49 #define _IFTMUnknown_(This)(IUnknown*)&(This->lpVtbl)
50 #define _IFTMarshal_(This) (IMarshal*)&(This->lpvtblFTM)
51
52 static inline FTMarshalImpl *impl_from_IMarshal( IMarshal *iface )
53 {
54     return (FTMarshalImpl *)((char*)iface - FIELD_OFFSET(FTMarshalImpl, lpvtblFTM));
55 }
56
57 /* inner IUnknown to handle aggregation */
58 static HRESULT WINAPI
59 IiFTMUnknown_fnQueryInterface (IUnknown * iface, REFIID riid, LPVOID * ppv)
60 {
61
62     FTMarshalImpl *This = (FTMarshalImpl *)iface;
63
64     TRACE ("\n");
65     *ppv = NULL;
66
67     if (IsEqualIID (&IID_IUnknown, riid))
68         *ppv = _IFTMUnknown_ (This);
69     else if (IsEqualIID (&IID_IMarshal, riid))
70         *ppv = _IFTMarshal_ (This);
71     else {
72         FIXME ("No interface for %s.\n", debugstr_guid (riid));
73         return E_NOINTERFACE;
74     }
75     IUnknown_AddRef ((IUnknown *) * ppv);
76     return S_OK;
77 }
78
79 static ULONG WINAPI IiFTMUnknown_fnAddRef (IUnknown * iface)
80 {
81
82     FTMarshalImpl *This = (FTMarshalImpl *)iface;
83
84     TRACE ("\n");
85     return InterlockedIncrement (&This->ref);
86 }
87
88 static ULONG WINAPI IiFTMUnknown_fnRelease (IUnknown * iface)
89 {
90
91     FTMarshalImpl *This = (FTMarshalImpl *)iface;
92
93     TRACE ("\n");
94     if (InterlockedDecrement (&This->ref))
95         return This->ref;
96     HeapFree (GetProcessHeap (), 0, This);
97     return 0;
98 }
99
100 static const IUnknownVtbl iunkvt =
101 {
102         IiFTMUnknown_fnQueryInterface,
103         IiFTMUnknown_fnAddRef,
104         IiFTMUnknown_fnRelease
105 };
106
107 static HRESULT WINAPI
108 FTMarshalImpl_QueryInterface (LPMARSHAL iface, REFIID riid, LPVOID * ppv)
109 {
110
111     FTMarshalImpl *This = impl_from_IMarshal(iface);
112
113     TRACE ("(%p)->(\n\tIID:\t%s,%p)\n", This, debugstr_guid (riid), ppv);
114     return IUnknown_QueryInterface (This->pUnkOuter, riid, ppv);
115 }
116
117 static ULONG WINAPI
118 FTMarshalImpl_AddRef (LPMARSHAL iface)
119 {
120
121     FTMarshalImpl *This = impl_from_IMarshal(iface);
122
123     TRACE ("\n");
124     return IUnknown_AddRef (This->pUnkOuter);
125 }
126
127 static ULONG WINAPI
128 FTMarshalImpl_Release (LPMARSHAL iface)
129 {
130
131     FTMarshalImpl *This = impl_from_IMarshal(iface);
132
133     TRACE ("\n");
134     return IUnknown_Release (This->pUnkOuter);
135 }
136
137 static HRESULT WINAPI
138 FTMarshalImpl_GetUnmarshalClass (LPMARSHAL iface, REFIID riid, void *pv, DWORD dwDestContext,
139                                                 void *pvDestContext, DWORD mshlflags, CLSID * pCid)
140 {
141     TRACE("(%s, %p, 0x%x, %p, 0x%x, %p)\n", debugstr_guid(riid), pv,
142         dwDestContext, pvDestContext, mshlflags, pCid);
143     if (dwDestContext == MSHCTX_INPROC || dwDestContext == MSHCTX_CROSSCTX)
144         memcpy(pCid, &CLSID_InProcFreeMarshaler, sizeof(CLSID_InProcFreeMarshaler));
145     else
146         memcpy(pCid, &CLSID_DfMarshal, sizeof(CLSID_InProcFreeMarshaler));
147     return S_OK;
148 }
149
150 static HRESULT WINAPI
151 FTMarshalImpl_GetMarshalSizeMax (LPMARSHAL iface, REFIID riid, void *pv, DWORD dwDestContext,
152                                                 void *pvDestContext, DWORD mshlflags, DWORD * pSize)
153 {
154
155     IMarshal *pMarshal = NULL;
156     HRESULT hres;
157
158     TRACE("(%s, %p, 0x%x, %p, 0x%x, %p)\n", debugstr_guid(riid), pv,
159         dwDestContext, pvDestContext, mshlflags, pSize);
160
161     /* if the marshalling happens inside the same process the interface pointer is
162        copied between the apartments */
163     if (dwDestContext == MSHCTX_INPROC || dwDestContext == MSHCTX_CROSSCTX) {
164         *pSize = sizeof (mshlflags) + sizeof (pv) + sizeof (DWORD) + sizeof (GUID);
165         return S_OK;
166     }
167
168     /* use the standard marshaller to handle all other cases */
169     CoGetStandardMarshal (riid, pv, dwDestContext, pvDestContext, mshlflags, &pMarshal);
170     hres = IMarshal_GetMarshalSizeMax (pMarshal, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize);
171     IMarshal_Release (pMarshal);
172     return hres;
173 }
174
175 static HRESULT WINAPI
176 FTMarshalImpl_MarshalInterface (LPMARSHAL iface, IStream * pStm, REFIID riid, void *pv,
177                                DWORD dwDestContext, void *pvDestContext, DWORD mshlflags)
178 {
179
180     IMarshal *pMarshal = NULL;
181     HRESULT hres;
182
183     TRACE("(%p, %s, %p, 0x%x, %p, 0x%x)\n", pStm, debugstr_guid(riid), pv,
184         dwDestContext, pvDestContext, mshlflags);
185
186     /* if the marshalling happens inside the same process the interface pointer is
187        copied between the apartments */
188     if (dwDestContext == MSHCTX_INPROC || dwDestContext == MSHCTX_CROSSCTX) {
189         void *object;
190         DWORD constant = 0;
191         GUID unknown_guid = { 0 };
192
193         hres = IUnknown_QueryInterface((IUnknown *)pv, riid, &object);
194         if (FAILED(hres))
195             return hres;
196
197         /* don't hold a reference to table-weak marshaled interfaces */
198         if (mshlflags & MSHLFLAGS_TABLEWEAK)
199             IUnknown_Release((IUnknown *)object);
200
201         hres = IStream_Write (pStm, &mshlflags, sizeof (mshlflags), NULL);
202         if (hres != S_OK) return STG_E_MEDIUMFULL;
203
204         hres = IStream_Write (pStm, &object, sizeof (object), NULL);
205         if (hres != S_OK) return STG_E_MEDIUMFULL;
206
207         hres = IStream_Write (pStm, &constant, sizeof (constant), NULL);
208         if (hres != S_OK) return STG_E_MEDIUMFULL;
209
210         hres = IStream_Write (pStm, &unknown_guid, sizeof (unknown_guid), NULL);
211         if (hres != S_OK) return STG_E_MEDIUMFULL;
212
213         return S_OK;
214     }
215
216     /* use the standard marshaler to handle all other cases */
217     CoGetStandardMarshal (riid, pv, dwDestContext, pvDestContext, mshlflags, &pMarshal);
218     hres = IMarshal_MarshalInterface (pMarshal, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags);
219     IMarshal_Release (pMarshal);
220     return hres;
221 }
222
223 static HRESULT WINAPI
224 FTMarshalImpl_UnmarshalInterface (LPMARSHAL iface, IStream * pStm, REFIID riid, void **ppv)
225 {
226     DWORD mshlflags;
227     IUnknown *object;
228     DWORD constant;
229     GUID unknown_guid;
230     HRESULT hres;
231
232     TRACE ("(%p, %s, %p)\n", pStm, debugstr_guid(riid), ppv);
233
234     hres = IStream_Read (pStm, &mshlflags, sizeof (mshlflags), NULL);
235     if (hres != S_OK) return STG_E_READFAULT;
236
237     hres = IStream_Read (pStm, &object, sizeof (object), NULL);
238     if (hres != S_OK) return STG_E_READFAULT;
239
240     hres = IStream_Read (pStm, &constant, sizeof (constant), NULL);
241     if (hres != S_OK) return STG_E_READFAULT;
242     if (constant != 0)
243         FIXME("constant is 0x%x instead of 0\n", constant);
244
245     hres = IStream_Read (pStm, &unknown_guid, sizeof (unknown_guid), NULL);
246     if (hres != S_OK) return STG_E_READFAULT;
247
248     hres = IUnknown_QueryInterface(object, riid, ppv);
249     if (!(mshlflags & (MSHLFLAGS_TABLEWEAK|MSHLFLAGS_TABLESTRONG)))
250         IUnknown_Release(object);
251     return hres;
252 }
253
254 static HRESULT WINAPI FTMarshalImpl_ReleaseMarshalData (LPMARSHAL iface, IStream * pStm)
255 {
256     DWORD mshlflags;
257     IUnknown *object;
258     DWORD constant;
259     GUID unknown_guid;
260     HRESULT hres;
261
262     TRACE ("(%p)\n", pStm);
263
264     hres = IStream_Read (pStm, &mshlflags, sizeof (mshlflags), NULL);
265     if (hres != S_OK) return STG_E_READFAULT;
266
267     hres = IStream_Read (pStm, &object, sizeof (object), NULL);
268     if (hres != S_OK) return STG_E_READFAULT;
269
270     hres = IStream_Read (pStm, &constant, sizeof (constant), NULL);
271     if (hres != S_OK) return STG_E_READFAULT;
272     if (constant != 0)
273         FIXME("constant is 0x%x instead of 0\n", constant);
274
275     hres = IStream_Read (pStm, &unknown_guid, sizeof (unknown_guid), NULL);
276     if (hres != S_OK) return STG_E_READFAULT;
277
278     IUnknown_Release(object);
279     return S_OK;
280 }
281
282 static HRESULT WINAPI FTMarshalImpl_DisconnectObject (LPMARSHAL iface, DWORD dwReserved)
283 {
284     TRACE ("()\n");
285     /* nothing to do */
286     return S_OK;
287 }
288
289 static const IMarshalVtbl ftmvtbl =
290 {
291         FTMarshalImpl_QueryInterface,
292         FTMarshalImpl_AddRef,
293         FTMarshalImpl_Release,
294         FTMarshalImpl_GetUnmarshalClass,
295         FTMarshalImpl_GetMarshalSizeMax,
296         FTMarshalImpl_MarshalInterface,
297         FTMarshalImpl_UnmarshalInterface,
298         FTMarshalImpl_ReleaseMarshalData,
299         FTMarshalImpl_DisconnectObject
300 };
301
302 /***********************************************************************
303  *          CoCreateFreeThreadedMarshaler [OLE32.@]
304  *
305  * Creates a free-threaded marshaler.
306  *
307  * PARAMS
308  *  punkOuter    [I] Optional. Outer unknown.
309  *  ppunkMarshal [O] On return, the inner unknown of the created free-threaded marshaler.
310  *
311  * RETURNS
312  *  Success: S_OK
313  *  Failure: E_OUTOFMEMORY if no memory available to create object.
314  *
315  * NOTES
316  *  Objects that ensure their state is maintained consistent when used by
317  *  multiple threads and reference no single-threaded objects are known as
318  *  free-threaded. The free-threaded marshaler enables these objects to be
319  *  efficiently marshaled within the same process, by not creating proxies
320  *  (as they aren't needed for the object to be safely used), whilst still
321  *  allowing the object to be used in inter-process and inter-machine contexts.
322  */
323 HRESULT WINAPI CoCreateFreeThreadedMarshaler (LPUNKNOWN punkOuter, LPUNKNOWN * ppunkMarshal)
324 {
325
326     FTMarshalImpl *ftm;
327
328     TRACE ("(%p %p)\n", punkOuter, ppunkMarshal);
329
330     ftm = HeapAlloc (GetProcessHeap (), 0, sizeof (FTMarshalImpl));
331     if (!ftm)
332         return E_OUTOFMEMORY;
333
334     ftm->lpVtbl = &iunkvt;
335     ftm->lpvtblFTM = &ftmvtbl;
336     ftm->ref = 1;
337     ftm->pUnkOuter = punkOuter ? punkOuter : _IFTMUnknown_(ftm);
338
339     *ppunkMarshal = _IFTMUnknown_ (ftm);
340     return S_OK;
341 }
342
343 static HRESULT WINAPI FTMarshalCF_QueryInterface(LPCLASSFACTORY iface,
344                                                   REFIID riid, LPVOID *ppv)
345 {
346     *ppv = NULL;
347     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory))
348     {
349         *ppv = iface;
350         IUnknown_AddRef(iface);
351         return S_OK;
352     }
353     return E_NOINTERFACE;
354 }
355
356 static ULONG WINAPI FTMarshalCF_AddRef(LPCLASSFACTORY iface)
357 {
358     return 2; /* non-heap based object */
359 }
360
361 static ULONG WINAPI FTMarshalCF_Release(LPCLASSFACTORY iface)
362 {
363     return 1; /* non-heap based object */
364 }
365
366 static HRESULT WINAPI FTMarshalCF_CreateInstance(LPCLASSFACTORY iface,
367     LPUNKNOWN pUnk, REFIID riid, LPVOID *ppv)
368 {
369     IUnknown *pUnknown;
370     HRESULT  hr;
371
372     TRACE("(%p, %s, %p)\n", pUnk, debugstr_guid(riid), ppv);
373
374     *ppv = NULL;
375
376     hr = CoCreateFreeThreadedMarshaler(pUnk, &pUnknown);
377
378     if (SUCCEEDED(hr))
379     {
380         hr = IUnknown_QueryInterface(pUnknown, riid, ppv);
381         IUnknown_Release(pUnknown);
382     }
383
384     return hr;
385 }
386
387 static HRESULT WINAPI FTMarshalCF_LockServer(LPCLASSFACTORY iface, BOOL fLock)
388 {
389     FIXME("(%d), stub!\n",fLock);
390     return S_OK;
391 }
392
393 static const IClassFactoryVtbl FTMarshalCFVtbl =
394 {
395     FTMarshalCF_QueryInterface,
396     FTMarshalCF_AddRef,
397     FTMarshalCF_Release,
398     FTMarshalCF_CreateInstance,
399     FTMarshalCF_LockServer
400 };
401 static const IClassFactoryVtbl *FTMarshalCF = &FTMarshalCFVtbl;
402
403 HRESULT FTMarshalCF_Create(REFIID riid, LPVOID *ppv)
404 {
405     return IClassFactory_QueryInterface((IClassFactory *)&FTMarshalCF, riid, ppv);
406 }