ole32: Implement table-weak marshaling for the free-threaded marshaler.
[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     FIXME ("(), stub!\n");
142     return S_OK;
143 }
144
145 static HRESULT WINAPI
146 FTMarshalImpl_GetMarshalSizeMax (LPMARSHAL iface, REFIID riid, void *pv, DWORD dwDestContext,
147                                                 void *pvDestContext, DWORD mshlflags, DWORD * pSize)
148 {
149
150     IMarshal *pMarshal = NULL;
151     HRESULT hres;
152
153     FTMarshalImpl *This = impl_from_IMarshal(iface);
154
155     FIXME ("(), stub!\n");
156
157     /* if the marshalling happens inside the same process the interface pointer is
158        copied between the apartments */
159     if (dwDestContext == MSHCTX_INPROC || dwDestContext == MSHCTX_CROSSCTX) {
160         *pSize = sizeof (This);
161         return S_OK;
162     }
163
164     /* use the standard marshaller to handle all other cases */
165     CoGetStandardMarshal (riid, pv, dwDestContext, pvDestContext, mshlflags, &pMarshal);
166     hres = IMarshal_GetMarshalSizeMax (pMarshal, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize);
167     IMarshal_Release (pMarshal);
168     return hres;
169 }
170
171 static HRESULT WINAPI
172 FTMarshalImpl_MarshalInterface (LPMARSHAL iface, IStream * pStm, REFIID riid, void *pv,
173                                DWORD dwDestContext, void *pvDestContext, DWORD mshlflags)
174 {
175
176     IMarshal *pMarshal = NULL;
177     HRESULT hres;
178     DWORD magic = 0x57dfd54d /* MEOW */;
179
180     TRACE("(%p, %s, %p, 0x%lx, %p, 0x%lx)\n", pStm, debugstr_guid(riid), pv,
181         dwDestContext, pvDestContext, mshlflags);
182
183     /* if the marshalling happens inside the same process the interface pointer is
184        copied between the apartments */
185     if (dwDestContext == MSHCTX_INPROC || dwDestContext == MSHCTX_CROSSCTX) {
186         void *object;
187         DWORD constant = 0;
188         GUID unknown_guid = { 0 };
189
190         hres = IUnknown_QueryInterface((IUnknown *)pv, riid, &object);
191         if (FAILED(hres))
192             return hres;
193
194         /* don't hold a reference to table-weak marshaled interfaces */
195         if (mshlflags & MSHLFLAGS_TABLEWEAK)
196             IUnknown_Release((IUnknown *)object);
197
198         hres = IStream_Write (pStm, &mshlflags, sizeof (mshlflags), NULL);
199         if (hres != S_OK) return STG_E_MEDIUMFULL;
200
201         hres = IStream_Write (pStm, &object, sizeof (object), NULL);
202         if (hres != S_OK) return STG_E_MEDIUMFULL;
203
204         hres = IStream_Write (pStm, &constant, sizeof (constant), NULL);
205         if (hres != S_OK) return STG_E_MEDIUMFULL;
206
207         hres = IStream_Write (pStm, &unknown_guid, sizeof (unknown_guid), NULL);
208         if (hres != S_OK) return STG_E_MEDIUMFULL;
209
210         return S_OK;
211     }
212
213     /* FIXME: this isn't exactly corret. it looks like the standard marshaler
214      * for native writes all of the OBJREF data into the stream, so we should
215      * really rely on it to write this constant for us. however, we need a
216      * constant to differentiate the outofproc data from the inproc data */
217     hres = IStream_Write (pStm, &magic, sizeof (magic), NULL);
218     if (hres != S_OK) return STG_E_MEDIUMFULL;
219
220     /* use the standard marshaler to handle all other cases */
221     CoGetStandardMarshal (riid, pv, dwDestContext, pvDestContext, mshlflags, &pMarshal);
222     hres = IMarshal_MarshalInterface (pMarshal, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags);
223     IMarshal_Release (pMarshal);
224     return hres;
225 }
226
227 static HRESULT WINAPI
228 FTMarshalImpl_UnmarshalInterface (LPMARSHAL iface, IStream * pStm, REFIID riid, void **ppv)
229 {
230     DWORD mshlflags;
231     HRESULT hres;
232
233     TRACE ("(%p, %s, %p)\n", pStm, debugstr_guid(riid), ppv);
234
235     hres = IStream_Read (pStm, &mshlflags, sizeof (mshlflags), NULL);
236     if (hres != S_OK) return STG_E_READFAULT;
237
238     if (mshlflags == 0x57dfd54d /* MEOW */) {
239         IMarshal *pMarshal;
240
241         hres = CoCreateInstance (&CLSID_DfMarshal, NULL, CLSCTX_INPROC, &IID_IMarshal, (void **)&pMarshal);
242         if (FAILED(hres)) return hres;
243
244         hres = IMarshal_UnmarshalInterface (pMarshal, pStm, riid, ppv);
245         IMarshal_Release (pMarshal);
246         return hres;
247     }
248     else {
249         IUnknown *object;
250         DWORD constant;
251         GUID unknown_guid;
252
253         hres = IStream_Read (pStm, &object, sizeof (object), NULL);
254         if (hres != S_OK) return STG_E_READFAULT;
255
256         hres = IStream_Read (pStm, &constant, sizeof (constant), NULL);
257         if (hres != S_OK) return STG_E_READFAULT;
258         if (constant != 0)
259             FIXME("constant is 0x%lx instead of 0\n", constant);
260
261         hres = IStream_Read (pStm, &unknown_guid, sizeof (unknown_guid), NULL);
262         if (hres != S_OK) return STG_E_READFAULT;
263
264         hres = IUnknown_QueryInterface(object, riid, ppv);
265         if (!(mshlflags & MSHLFLAGS_TABLEWEAK))
266             IUnknown_Release(object);
267         return hres;
268     }
269 }
270
271 static HRESULT WINAPI FTMarshalImpl_ReleaseMarshalData (LPMARSHAL iface, IStream * pStm)
272 {
273     FIXME ("(), stub!\n");
274     return S_OK;
275 }
276
277 static HRESULT WINAPI FTMarshalImpl_DisconnectObject (LPMARSHAL iface, DWORD dwReserved)
278 {
279     FIXME ("(), stub!\n");
280     return S_OK;
281 }
282
283 static const IMarshalVtbl ftmvtbl =
284 {
285         FTMarshalImpl_QueryInterface,
286         FTMarshalImpl_AddRef,
287         FTMarshalImpl_Release,
288         FTMarshalImpl_GetUnmarshalClass,
289         FTMarshalImpl_GetMarshalSizeMax,
290         FTMarshalImpl_MarshalInterface,
291         FTMarshalImpl_UnmarshalInterface,
292         FTMarshalImpl_ReleaseMarshalData,
293         FTMarshalImpl_DisconnectObject
294 };
295
296 /***********************************************************************
297  *          CoCreateFreeThreadedMarshaler [OLE32.@]
298  *
299  */
300 HRESULT WINAPI CoCreateFreeThreadedMarshaler (LPUNKNOWN punkOuter, LPUNKNOWN * ppunkMarshal)
301 {
302
303     FTMarshalImpl *ftm;
304
305     TRACE ("(%p %p)\n", punkOuter, ppunkMarshal);
306
307     ftm = HeapAlloc (GetProcessHeap (), 0, sizeof (FTMarshalImpl));
308     if (!ftm)
309         return E_OUTOFMEMORY;
310
311     ftm->lpVtbl = &iunkvt;
312     ftm->lpvtblFTM = &ftmvtbl;
313     ftm->ref = 1;
314     ftm->pUnkOuter = punkOuter ? punkOuter : _IFTMUnknown_(ftm);
315
316     *ppunkMarshal = _IFTMUnknown_ (ftm);
317     return S_OK;
318 }