msacm32: Win64 printf format warning fixes.
[wine/wine64.git] / dlls / ole32 / marshal.c
blobcbe4a0f940291503235a4eba9564b7e51101d085
1 /*
2 * Marshalling library
4 * Copyright 2002 Marcus Meissner
5 * Copyright 2004 Mike Hearn, for CodeWeavers
6 * Copyright 2004 Rob Shearman, for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <assert.h>
31 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winuser.h"
36 #include "objbase.h"
37 #include "ole2.h"
38 #include "rpc.h"
39 #include "winerror.h"
40 #include "winreg.h"
41 #include "wtypes.h"
42 #include "wine/unicode.h"
44 #include "compobj_private.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(ole);
50 extern const CLSID CLSID_DfMarshal;
52 /* number of refs given out for normal marshaling */
53 #define NORMALEXTREFS 5
55 /* private flag indicating that the caller does not want to notify the stub
56 * when the proxy disconnects or is destroyed */
57 #define SORFP_NOLIFETIMEMGMT SORF_OXRES1
59 static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt,
60 MSHCTX dest_context, void *dest_context_data,
61 REFIID riid, void **object);
63 /* Marshalling just passes a unique identifier to the remote client,
64 * that makes it possible to find the passed interface again.
66 * So basically we need a set of values that make it unique.
68 * Note that the IUnknown_QI(ob,xiid,&ppv) always returns the SAME ppv value!
70 * A triple is used: OXID (apt id), OID (stub manager id),
71 * IPID (interface ptr/stub id).
73 * OXIDs identify an apartment and are network scoped
74 * OIDs identify a stub manager and are apartment scoped
75 * IPIDs identify an interface stub and are apartment scoped
78 inline static HRESULT get_facbuf_for_iid(REFIID riid, IPSFactoryBuffer **facbuf)
80 HRESULT hr;
81 CLSID clsid;
83 if ((hr = CoGetPSClsid(riid, &clsid)))
84 return hr;
85 return CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL,
86 &IID_IPSFactoryBuffer, (LPVOID*)facbuf);
89 /* marshals an object into a STDOBJREF structure */
90 HRESULT marshal_object(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, MSHLFLAGS mshlflags)
92 struct stub_manager *manager;
93 struct ifstub *ifstub;
94 BOOL tablemarshal;
95 IRpcStubBuffer *stub = NULL;
96 HRESULT hr;
97 IUnknown *iobject = NULL; /* object of type riid */
99 hr = apartment_getoxid(apt, &stdobjref->oxid);
100 if (hr != S_OK)
101 return hr;
103 hr = apartment_createwindowifneeded(apt);
104 if (hr != S_OK)
105 return hr;
107 hr = IUnknown_QueryInterface(object, riid, (void **)&iobject);
108 if (hr != S_OK)
110 ERR("object doesn't expose interface %s, failing with error 0x%08lx\n",
111 debugstr_guid(riid), hr);
112 return E_NOINTERFACE;
115 /* IUnknown doesn't require a stub buffer, because it never goes out on
116 * the wire */
117 if (!IsEqualIID(riid, &IID_IUnknown))
119 IPSFactoryBuffer *psfb;
121 hr = get_facbuf_for_iid(riid, &psfb);
122 if (hr != S_OK)
124 ERR("couldn't get IPSFactory buffer for interface %s\n", debugstr_guid(riid));
125 IUnknown_Release(iobject);
126 return hr;
129 hr = IPSFactoryBuffer_CreateStub(psfb, riid, iobject, &stub);
130 IPSFactoryBuffer_Release(psfb);
131 if (hr != S_OK)
133 ERR("Failed to create an IRpcStubBuffer from IPSFactory for %s\n", debugstr_guid(riid));
134 IUnknown_Release(iobject);
135 return hr;
139 if (mshlflags & MSHLFLAGS_NOPING)
140 stdobjref->flags = SORF_NOPING;
141 else
142 stdobjref->flags = SORF_NULL;
144 if ((manager = get_stub_manager_from_object(apt, object)))
145 TRACE("registering new ifstub on pre-existing manager\n");
146 else
148 TRACE("constructing new stub manager\n");
150 manager = new_stub_manager(apt, object);
151 if (!manager)
153 if (stub) IRpcStubBuffer_Release(stub);
154 IUnknown_Release(iobject);
155 return E_OUTOFMEMORY;
158 stdobjref->oid = manager->oid;
160 tablemarshal = ((mshlflags & MSHLFLAGS_TABLESTRONG) || (mshlflags & MSHLFLAGS_TABLEWEAK));
162 /* make sure ifstub that we are creating is unique */
163 ifstub = stub_manager_find_ifstub(manager, riid, mshlflags);
164 if (!ifstub)
166 ifstub = stub_manager_new_ifstub(manager, stub, iobject, riid, mshlflags);
167 IUnknown_Release(iobject);
168 if (stub) IRpcStubBuffer_Release(stub);
169 if (!ifstub)
171 stub_manager_int_release(manager);
172 /* FIXME: should we do another release to completely destroy the
173 * stub manager? */
174 return E_OUTOFMEMORY;
178 if (!tablemarshal)
180 stdobjref->cPublicRefs = NORMALEXTREFS;
181 stub_manager_ext_addref(manager, stdobjref->cPublicRefs);
183 else
185 stdobjref->cPublicRefs = 0;
186 if (mshlflags & MSHLFLAGS_TABLESTRONG)
187 stub_manager_ext_addref(manager, 1);
190 /* FIXME: check return value */
191 RPC_RegisterInterface(riid);
193 stdobjref->ipid = ifstub->ipid;
195 stub_manager_int_release(manager);
196 return S_OK;
201 /* Client-side identity of the server object */
203 static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk);
204 static void proxy_manager_destroy(struct proxy_manager * This);
205 static HRESULT proxy_manager_find_ifproxy(struct proxy_manager * This, REFIID riid, struct ifproxy ** ifproxy_found);
206 static HRESULT proxy_manager_query_local_interface(struct proxy_manager * This, REFIID riid, void ** ppv);
208 static HRESULT WINAPI ClientIdentity_QueryInterface(IMultiQI * iface, REFIID riid, void ** ppv)
210 HRESULT hr;
211 MULTI_QI mqi;
213 TRACE("%s\n", debugstr_guid(riid));
215 mqi.pIID = riid;
216 hr = IMultiQI_QueryMultipleInterfaces(iface, 1, &mqi);
217 *ppv = (void *)mqi.pItf;
219 return hr;
222 static ULONG WINAPI ClientIdentity_AddRef(IMultiQI * iface)
224 struct proxy_manager * This = (struct proxy_manager *)iface;
225 TRACE("%p - before %ld\n", iface, This->refs);
226 return InterlockedIncrement(&This->refs);
229 static ULONG WINAPI ClientIdentity_Release(IMultiQI * iface)
231 struct proxy_manager * This = (struct proxy_manager *)iface;
232 ULONG refs = InterlockedDecrement(&This->refs);
233 TRACE("%p - after %ld\n", iface, refs);
234 if (!refs)
235 proxy_manager_destroy(This);
236 return refs;
239 static HRESULT WINAPI ClientIdentity_QueryMultipleInterfaces(IMultiQI *iface, ULONG cMQIs, MULTI_QI *pMQIs)
241 struct proxy_manager * This = (struct proxy_manager *)iface;
242 REMQIRESULT *qiresults = NULL;
243 ULONG nonlocal_mqis = 0;
244 ULONG i;
245 ULONG successful_mqis = 0;
246 IID *iids = HeapAlloc(GetProcessHeap(), 0, cMQIs * sizeof(*iids));
247 /* mapping of RemQueryInterface index to QueryMultipleInterfaces index */
248 ULONG *mapping = HeapAlloc(GetProcessHeap(), 0, cMQIs * sizeof(*mapping));
250 TRACE("cMQIs: %ld\n", cMQIs);
252 /* try to get a local interface - this includes already active proxy
253 * interfaces and also interfaces exposed by the proxy manager */
254 for (i = 0; i < cMQIs; i++)
256 TRACE("iid[%ld] = %s\n", i, debugstr_guid(pMQIs[i].pIID));
257 pMQIs[i].hr = proxy_manager_query_local_interface(This, pMQIs[i].pIID, (void **)&pMQIs[i].pItf);
258 if (pMQIs[i].hr == S_OK)
259 successful_mqis++;
260 else
262 iids[nonlocal_mqis] = *pMQIs[i].pIID;
263 mapping[nonlocal_mqis] = i;
264 nonlocal_mqis++;
268 TRACE("%ld interfaces not found locally\n", nonlocal_mqis);
270 /* if we have more than one interface not found locally then we must try
271 * to query the remote object for it */
272 if (nonlocal_mqis != 0)
274 IRemUnknown *remunk;
275 HRESULT hr;
276 IPID *ipid;
278 /* get the ipid of the first entry */
279 /* FIXME: should we implement ClientIdentity on the ifproxies instead
280 * of the proxy_manager so we use the correct ipid here? */
281 ipid = &LIST_ENTRY(list_head(&This->interfaces), struct ifproxy, entry)->stdobjref.ipid;
283 /* get IRemUnknown proxy so we can communicate with the remote object */
284 hr = proxy_manager_get_remunknown(This, &remunk);
286 if (hr == S_OK)
288 hr = IRemUnknown_RemQueryInterface(remunk, ipid, NORMALEXTREFS,
289 nonlocal_mqis, iids, &qiresults);
290 if (FAILED(hr))
291 ERR("IRemUnknown_RemQueryInterface failed with error 0x%08lx\n", hr);
294 /* IRemUnknown_RemQueryInterface can return S_FALSE if only some of
295 * the interfaces were returned */
296 if (SUCCEEDED(hr))
298 /* try to unmarshal each object returned to us */
299 for (i = 0; i < nonlocal_mqis; i++)
301 ULONG index = mapping[i];
302 HRESULT hrobj = qiresults[i].hResult;
303 if (hrobj == S_OK)
304 hrobj = unmarshal_object(&qiresults[i].std, This->parent,
305 This->dest_context,
306 This->dest_context_data,
307 pMQIs[index].pIID,
308 (void **)&pMQIs[index].pItf);
310 if (hrobj == S_OK)
311 successful_mqis++;
312 else
313 ERR("Failed to get pointer to interface %s\n", debugstr_guid(pMQIs[index].pIID));
314 pMQIs[index].hr = hrobj;
318 /* free the memory allocated by the proxy */
319 CoTaskMemFree(qiresults);
322 TRACE("%ld/%ld successfully queried\n", successful_mqis, cMQIs);
324 HeapFree(GetProcessHeap(), 0, iids);
325 HeapFree(GetProcessHeap(), 0, mapping);
327 if (successful_mqis == cMQIs)
328 return S_OK; /* we got all requested interfaces */
329 else if (successful_mqis == 0)
330 return E_NOINTERFACE; /* we didn't get any interfaces */
331 else
332 return S_FALSE; /* we got some interfaces */
335 static const IMultiQIVtbl ClientIdentity_Vtbl =
337 ClientIdentity_QueryInterface,
338 ClientIdentity_AddRef,
339 ClientIdentity_Release,
340 ClientIdentity_QueryMultipleInterfaces
343 static HRESULT WINAPI Proxy_QueryInterface(IMarshal *iface, REFIID riid, void **ppvObject)
345 ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
346 return IMultiQI_QueryInterface((IMultiQI *)&This->lpVtbl, riid, ppvObject);
349 static ULONG WINAPI Proxy_AddRef(IMarshal *iface)
351 ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
352 return IMultiQI_AddRef((IMultiQI *)&This->lpVtbl);
355 /* FIXME: remove these */
356 static HRESULT WINAPI StdMarshalImpl_GetUnmarshalClass(LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, CLSID* pCid);
357 static HRESULT WINAPI StdMarshalImpl_GetMarshalSizeMax(LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, DWORD* pSize);
358 static HRESULT WINAPI StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv);
359 static HRESULT WINAPI StdMarshalImpl_ReleaseMarshalData(LPMARSHAL iface, IStream *pStm);
360 static HRESULT WINAPI StdMarshalImpl_DisconnectObject(LPMARSHAL iface, DWORD dwReserved);
362 static ULONG WINAPI Proxy_Release(IMarshal *iface)
364 ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
365 return IMultiQI_Release((IMultiQI *)&This->lpVtbl);
368 static HRESULT WINAPI Proxy_MarshalInterface(
369 LPMARSHAL iface, IStream *pStm, REFIID riid, void* pv, DWORD dwDestContext,
370 void* pvDestContext, DWORD mshlflags)
372 ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
373 HRESULT hr;
374 struct ifproxy *ifproxy;
376 TRACE("(...,%s,...)\n", debugstr_guid(riid));
378 hr = proxy_manager_find_ifproxy(This, riid, &ifproxy);
379 if (SUCCEEDED(hr))
381 STDOBJREF stdobjref = ifproxy->stdobjref;
382 ULONG cPublicRefs = ifproxy->refs;
383 ULONG cPublicRefsOld;
385 /* optimization - share out proxy's public references if possible
386 * instead of making new proxy do a roundtrip through the server */
389 ULONG cPublicRefsNew;
390 cPublicRefsOld = cPublicRefs;
391 stdobjref.cPublicRefs = cPublicRefs / 2;
392 cPublicRefsNew = cPublicRefs - stdobjref.cPublicRefs;
393 cPublicRefs = InterlockedCompareExchange(
394 (LONG *)&ifproxy->refs, cPublicRefsNew, cPublicRefsOld);
395 } while (cPublicRefs != cPublicRefsOld);
397 if (!stdobjref.cPublicRefs)
399 IRemUnknown *remunk;
400 hr = proxy_manager_get_remunknown(This, &remunk);
401 if (hr == S_OK)
403 HRESULT hrref = S_OK;
404 REMINTERFACEREF rif;
405 rif.ipid = ifproxy->stdobjref.ipid;
406 rif.cPublicRefs = NORMALEXTREFS;
407 rif.cPrivateRefs = 0;
408 hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref);
409 if (hr == S_OK && hrref == S_OK)
410 stdobjref.cPublicRefs = rif.cPublicRefs;
411 else
412 ERR("IRemUnknown_RemAddRef returned with 0x%08lx, hrref = 0x%08lx\n", hr, hrref);
416 if (SUCCEEDED(hr))
418 TRACE("writing stdobjref:\n\tflags = %04lx\n\tcPublicRefs = %ld\n\toxid = %s\n\toid = %s\n\tipid = %s\n",
419 stdobjref.flags, stdobjref.cPublicRefs,
420 wine_dbgstr_longlong(stdobjref.oxid),
421 wine_dbgstr_longlong(stdobjref.oid),
422 debugstr_guid(&stdobjref.ipid));
423 hr = IStream_Write(pStm, &stdobjref, sizeof(stdobjref), NULL);
426 else
428 /* we don't have the interface already unmarshaled so we have to
429 * request the object from the server */
430 IRemUnknown *remunk;
431 IPID *ipid;
432 REMQIRESULT *qiresults = NULL;
433 IID iid = *riid;
435 /* get the ipid of the first entry */
436 /* FIXME: should we implement ClientIdentity on the ifproxies instead
437 * of the proxy_manager so we use the correct ipid here? */
438 ipid = &LIST_ENTRY(list_head(&This->interfaces), struct ifproxy, entry)->stdobjref.ipid;
440 /* get IRemUnknown proxy so we can communicate with the remote object */
441 hr = proxy_manager_get_remunknown(This, &remunk);
443 if (hr == S_OK)
445 hr = IRemUnknown_RemQueryInterface(remunk, ipid, NORMALEXTREFS,
446 1, &iid, &qiresults);
447 if (SUCCEEDED(hr))
449 hr = IStream_Write(pStm, &qiresults->std, sizeof(qiresults->std), NULL);
450 if (FAILED(hr))
452 REMINTERFACEREF rif;
453 rif.ipid = qiresults->std.ipid;
454 rif.cPublicRefs = qiresults->std.cPublicRefs;
455 rif.cPrivateRefs = 0;
456 IRemUnknown_RemRelease(remunk, 1, &rif);
458 CoTaskMemFree(qiresults);
460 else
461 ERR("IRemUnknown_RemQueryInterface failed with error 0x%08lx\n", hr);
465 return hr;
468 static const IMarshalVtbl ProxyMarshal_Vtbl =
470 Proxy_QueryInterface,
471 Proxy_AddRef,
472 Proxy_Release,
473 StdMarshalImpl_GetUnmarshalClass,
474 StdMarshalImpl_GetMarshalSizeMax,
475 Proxy_MarshalInterface,
476 StdMarshalImpl_UnmarshalInterface,
477 StdMarshalImpl_ReleaseMarshalData,
478 StdMarshalImpl_DisconnectObject
481 static HRESULT ifproxy_get_public_ref(struct ifproxy * This)
483 HRESULT hr = S_OK;
485 if (WAIT_OBJECT_0 != WaitForSingleObject(This->parent->remoting_mutex, INFINITE))
487 ERR("Wait failed for ifproxy %p\n", This);
488 return E_UNEXPECTED;
491 if (This->refs == 0)
493 IRemUnknown *remunk = NULL;
495 TRACE("getting public ref for ifproxy %p\n", This);
497 hr = proxy_manager_get_remunknown(This->parent, &remunk);
498 if (hr == S_OK)
500 HRESULT hrref = S_OK;
501 REMINTERFACEREF rif;
502 rif.ipid = This->stdobjref.ipid;
503 rif.cPublicRefs = NORMALEXTREFS;
504 rif.cPrivateRefs = 0;
505 hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref);
506 if (hr == S_OK && hrref == S_OK)
507 This->refs += NORMALEXTREFS;
508 else
509 ERR("IRemUnknown_RemAddRef returned with 0x%08lx, hrref = 0x%08lx\n", hr, hrref);
512 ReleaseMutex(This->parent->remoting_mutex);
514 return hr;
517 static HRESULT ifproxy_release_public_refs(struct ifproxy * This)
519 HRESULT hr = S_OK;
521 if (WAIT_OBJECT_0 != WaitForSingleObject(This->parent->remoting_mutex, INFINITE))
523 ERR("Wait failed for ifproxy %p\n", This);
524 return E_UNEXPECTED;
527 if (This->refs > 0)
529 IRemUnknown *remunk = NULL;
531 TRACE("releasing %ld refs\n", This->refs);
533 hr = proxy_manager_get_remunknown(This->parent, &remunk);
534 if (hr == S_OK)
536 REMINTERFACEREF rif;
537 rif.ipid = This->stdobjref.ipid;
538 rif.cPublicRefs = This->refs;
539 rif.cPrivateRefs = 0;
540 hr = IRemUnknown_RemRelease(remunk, 1, &rif);
541 if (hr == S_OK)
542 This->refs = 0;
543 else if (hr == RPC_E_DISCONNECTED)
544 WARN("couldn't release references because object was "
545 "disconnected: oxid = %s, oid = %s\n",
546 wine_dbgstr_longlong(This->parent->oxid),
547 wine_dbgstr_longlong(This->parent->oid));
548 else
549 ERR("IRemUnknown_RemRelease failed with error 0x%08lx\n", hr);
552 ReleaseMutex(This->parent->remoting_mutex);
554 return hr;
557 /* should be called inside This->parent->cs critical section */
558 static void ifproxy_disconnect(struct ifproxy * This)
560 ifproxy_release_public_refs(This);
561 if (This->proxy) IRpcProxyBuffer_Disconnect(This->proxy);
563 IRpcChannelBuffer_Release(This->chan);
564 This->chan = NULL;
567 /* should be called in This->parent->cs critical section if it is an entry in parent's list */
568 static void ifproxy_destroy(struct ifproxy * This)
570 TRACE("%p\n", This);
572 /* release public references to this object so that the stub can know
573 * when to destroy itself */
574 ifproxy_release_public_refs(This);
576 list_remove(&This->entry);
578 if (This->chan)
580 IRpcChannelBuffer_Release(This->chan);
581 This->chan = NULL;
584 if (This->proxy) IRpcProxyBuffer_Release(This->proxy);
586 HeapFree(GetProcessHeap(), 0, This);
589 static HRESULT proxy_manager_construct(
590 APARTMENT * apt, ULONG sorflags, OXID oxid, OID oid,
591 struct proxy_manager ** proxy_manager)
593 struct proxy_manager * This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
594 if (!This) return E_OUTOFMEMORY;
596 This->remoting_mutex = CreateMutexW(NULL, FALSE, NULL);
597 if (!This->remoting_mutex)
599 HeapFree(GetProcessHeap(), 0, This);
600 return HRESULT_FROM_WIN32(GetLastError());
603 This->lpVtbl = &ClientIdentity_Vtbl;
604 This->lpVtblMarshal = &ProxyMarshal_Vtbl;
606 list_init(&This->entry);
607 list_init(&This->interfaces);
609 InitializeCriticalSection(&This->cs);
610 DEBUG_SET_CRITSEC_NAME(&This->cs, "proxy_manager");
612 /* the apartment the object was unmarshaled into */
613 This->parent = apt;
615 /* the source apartment and id of the object */
616 This->oxid = oxid;
617 This->oid = oid;
619 This->refs = 1;
621 /* the DCOM draft specification states that the SORF_NOPING flag is
622 * proxy manager specific, not ifproxy specific, so this implies that we
623 * should store the STDOBJREF flags here in the proxy manager. */
624 This->sorflags = sorflags;
626 /* we create the IRemUnknown proxy on demand */
627 This->remunk = NULL;
629 /* initialise these values to the weakest values and they will be
630 * overwritten in proxy_manager_set_context */
631 This->dest_context = MSHCTX_INPROC;
632 This->dest_context_data = NULL;
634 EnterCriticalSection(&apt->cs);
635 /* FIXME: we are dependent on the ordering in here to make sure a proxy's
636 * IRemUnknown proxy doesn't get destroyed before the regual proxy does
637 * because we need the IRemUnknown proxy during the destruction of the
638 * regular proxy. Ideally, we should maintain a separate list for the
639 * IRemUnknown proxies that need late destruction */
640 list_add_tail(&apt->proxies, &This->entry);
641 LeaveCriticalSection(&apt->cs);
643 TRACE("%p created for OXID %s, OID %s\n", This,
644 wine_dbgstr_longlong(oxid), wine_dbgstr_longlong(oid));
646 *proxy_manager = This;
647 return S_OK;
650 static inline void proxy_manager_set_context(struct proxy_manager *This, MSHCTX dest_context, void *dest_context_data)
652 MSHCTX old_dest_context = This->dest_context;
653 MSHCTX new_dest_context;
657 new_dest_context = old_dest_context;
658 /* "stronger" values overwrite "weaker" values. stronger values are
659 * ones that disable more optimisations */
660 switch (old_dest_context)
662 case MSHCTX_INPROC:
663 new_dest_context = dest_context;
664 break;
665 case MSHCTX_CROSSCTX:
666 switch (dest_context)
668 case MSHCTX_INPROC:
669 break;
670 default:
671 new_dest_context = dest_context;
673 break;
674 case MSHCTX_LOCAL:
675 switch (dest_context)
677 case MSHCTX_INPROC:
678 case MSHCTX_CROSSCTX:
679 break;
680 default:
681 new_dest_context = dest_context;
683 break;
684 case MSHCTX_NOSHAREDMEM:
685 switch (dest_context)
687 case MSHCTX_DIFFERENTMACHINE:
688 new_dest_context = dest_context;
689 break;
690 default:
691 break;
693 break;
694 default:
695 break;
698 if (old_dest_context == new_dest_context) break;
700 old_dest_context = InterlockedCompareExchange((PLONG)&This->dest_context, new_dest_context, old_dest_context);
701 } while (new_dest_context != old_dest_context);
703 if (dest_context_data)
704 InterlockedExchangePointer(&This->dest_context_data, dest_context_data);
707 static HRESULT proxy_manager_query_local_interface(struct proxy_manager * This, REFIID riid, void ** ppv)
709 HRESULT hr;
710 struct ifproxy * ifproxy;
712 TRACE("%s\n", debugstr_guid(riid));
714 if (IsEqualIID(riid, &IID_IUnknown) ||
715 IsEqualIID(riid, &IID_IMultiQI))
717 *ppv = (void *)&This->lpVtbl;
718 IUnknown_AddRef((IUnknown *)*ppv);
719 return S_OK;
721 if (IsEqualIID(riid, &IID_IMarshal))
723 *ppv = (void *)&This->lpVtblMarshal;
724 IUnknown_AddRef((IUnknown *)*ppv);
725 return S_OK;
727 if (IsEqualIID(riid, &IID_IClientSecurity))
729 FIXME("requesting IClientSecurity, but it is unimplemented\n");
730 *ppv = NULL;
731 return E_NOINTERFACE;
734 hr = proxy_manager_find_ifproxy(This, riid, &ifproxy);
735 if (hr == S_OK)
737 *ppv = ifproxy->iface;
738 IUnknown_AddRef((IUnknown *)*ppv);
739 return S_OK;
742 *ppv = NULL;
743 return E_NOINTERFACE;
746 static HRESULT proxy_manager_create_ifproxy(
747 struct proxy_manager * This, const STDOBJREF *stdobjref, REFIID riid,
748 IRpcChannelBuffer * channel, struct ifproxy ** iif_out)
750 HRESULT hr;
751 IPSFactoryBuffer * psfb;
752 struct ifproxy * ifproxy = HeapAlloc(GetProcessHeap(), 0, sizeof(*ifproxy));
753 if (!ifproxy) return E_OUTOFMEMORY;
755 list_init(&ifproxy->entry);
757 ifproxy->parent = This;
758 ifproxy->stdobjref = *stdobjref;
759 ifproxy->iid = *riid;
760 ifproxy->refs = stdobjref->cPublicRefs;
761 ifproxy->proxy = NULL;
763 assert(channel);
764 ifproxy->chan = channel; /* FIXME: we should take the binding strings and construct the channel in this function */
766 /* the IUnknown interface is special because it does not have a
767 * proxy associated with the ifproxy as we handle IUnknown ourselves */
768 if (IsEqualIID(riid, &IID_IUnknown))
770 ifproxy->iface = (void *)&This->lpVtbl;
771 IMultiQI_AddRef((IMultiQI *)&This->lpVtbl);
772 hr = S_OK;
774 else
776 hr = get_facbuf_for_iid(riid, &psfb);
777 if (hr == S_OK)
779 /* important note: the outer unknown is set to the proxy manager.
780 * This ensures the COM identity rules are not violated, by having a
781 * one-to-one mapping of objects on the proxy side to objects on the
782 * stub side, no matter which interface you view the object through */
783 hr = IPSFactoryBuffer_CreateProxy(psfb, (IUnknown *)&This->lpVtbl, riid,
784 &ifproxy->proxy, &ifproxy->iface);
785 IPSFactoryBuffer_Release(psfb);
786 if (hr != S_OK)
787 ERR("Could not create proxy for interface %s, error 0x%08lx\n",
788 debugstr_guid(riid), hr);
790 else
791 ERR("Could not get IPSFactoryBuffer for interface %s, error 0x%08lx\n",
792 debugstr_guid(riid), hr);
794 if (hr == S_OK)
795 hr = IRpcProxyBuffer_Connect(ifproxy->proxy, ifproxy->chan);
798 /* get at least one external reference to the object to keep it alive */
799 if (hr == S_OK)
800 hr = ifproxy_get_public_ref(ifproxy);
802 if (hr == S_OK)
804 EnterCriticalSection(&This->cs);
805 list_add_tail(&This->interfaces, &ifproxy->entry);
806 LeaveCriticalSection(&This->cs);
808 *iif_out = ifproxy;
809 TRACE("ifproxy %p created for IPID %s, interface %s with %lu public refs\n",
810 ifproxy, debugstr_guid(&stdobjref->ipid), debugstr_guid(riid), stdobjref->cPublicRefs);
812 else
813 ifproxy_destroy(ifproxy);
815 return hr;
818 static HRESULT proxy_manager_find_ifproxy(struct proxy_manager * This, REFIID riid, struct ifproxy ** ifproxy_found)
820 HRESULT hr = E_NOINTERFACE; /* assume not found */
821 struct list * cursor;
823 EnterCriticalSection(&This->cs);
824 LIST_FOR_EACH(cursor, &This->interfaces)
826 struct ifproxy * ifproxy = LIST_ENTRY(cursor, struct ifproxy, entry);
827 if (IsEqualIID(riid, &ifproxy->iid))
829 *ifproxy_found = ifproxy;
830 hr = S_OK;
831 break;
834 LeaveCriticalSection(&This->cs);
836 return hr;
839 static void proxy_manager_disconnect(struct proxy_manager * This)
841 struct list * cursor;
843 TRACE("oxid = %s, oid = %s\n", wine_dbgstr_longlong(This->oxid),
844 wine_dbgstr_longlong(This->oid));
846 EnterCriticalSection(&This->cs);
848 /* SORFP_NOLIFTIMEMGMT proxies (for IRemUnknown) shouldn't be
849 * disconnected - it won't do anything anyway, except cause
850 * problems for other objects that depend on this proxy always
851 * working */
852 if (!(This->sorflags & SORFP_NOLIFETIMEMGMT))
854 LIST_FOR_EACH(cursor, &This->interfaces)
856 struct ifproxy * ifproxy = LIST_ENTRY(cursor, struct ifproxy, entry);
857 ifproxy_disconnect(ifproxy);
861 /* apartment is being destroyed so don't keep a pointer around to it */
862 This->parent = NULL;
864 LeaveCriticalSection(&This->cs);
867 static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk)
869 HRESULT hr = S_OK;
871 /* we don't want to try and unmarshal or use IRemUnknown if we don't want
872 * lifetime management */
873 if (This->sorflags & SORFP_NOLIFETIMEMGMT)
874 return S_FALSE;
876 EnterCriticalSection(&This->cs);
877 if (This->remunk)
878 /* already created - return existing object */
879 *remunk = This->remunk;
880 else if (!This->parent)
881 /* disconnected - we can't create IRemUnknown */
882 hr = S_FALSE;
883 else
885 STDOBJREF stdobjref;
886 /* Don't want IRemUnknown lifetime management as this is IRemUnknown!
887 * We also don't care about whether or not the stub is still alive */
888 stdobjref.flags = SORFP_NOLIFETIMEMGMT | SORF_NOPING;
889 stdobjref.cPublicRefs = 1;
890 /* oxid of destination object */
891 stdobjref.oxid = This->oxid;
892 /* FIXME: what should be used for the oid? The DCOM draft doesn't say */
893 stdobjref.oid = (OID)-1;
894 /* FIXME: this is a hack around not having an OXID resolver yet -
895 * the OXID resolver should give us the IPID of the IRemUnknown
896 * interface */
897 stdobjref.ipid.Data1 = 0xffffffff;
898 stdobjref.ipid.Data2 = 0xffff;
899 stdobjref.ipid.Data3 = 0xffff;
900 assert(sizeof(stdobjref.ipid.Data4) == sizeof(stdobjref.oxid));
901 memcpy(&stdobjref.ipid.Data4, &stdobjref.oxid, sizeof(OXID));
903 /* do the unmarshal */
904 hr = unmarshal_object(&stdobjref, This->parent, This->dest_context,
905 This->dest_context_data, &IID_IRemUnknown,
906 (void**)&This->remunk);
907 if (hr == S_OK)
908 *remunk = This->remunk;
910 LeaveCriticalSection(&This->cs);
912 TRACE("got IRemUnknown* pointer %p, hr = 0x%08lx\n", *remunk, hr);
914 return hr;
917 /* destroys a proxy manager, freeing the memory it used.
918 * Note: this function should not be called from a list iteration in the
919 * apartment, due to the fact that it removes itself from the apartment and
920 * it could add a proxy to IRemUnknown into the apartment. */
921 static void proxy_manager_destroy(struct proxy_manager * This)
923 struct list * cursor;
925 TRACE("oxid = %s, oid = %s\n", wine_dbgstr_longlong(This->oxid),
926 wine_dbgstr_longlong(This->oid));
928 if (This->parent)
930 EnterCriticalSection(&This->parent->cs);
932 /* remove ourself from the list of proxy objects in the apartment */
933 LIST_FOR_EACH(cursor, &This->parent->proxies)
935 if (cursor == &This->entry)
937 list_remove(&This->entry);
938 break;
942 LeaveCriticalSection(&This->parent->cs);
945 /* destroy all of the interface proxies */
946 while ((cursor = list_head(&This->interfaces)))
948 struct ifproxy * ifproxy = LIST_ENTRY(cursor, struct ifproxy, entry);
949 ifproxy_destroy(ifproxy);
952 if (This->remunk) IRemUnknown_Release(This->remunk);
954 DEBUG_CLEAR_CRITSEC_NAME(&This->cs);
955 DeleteCriticalSection(&This->cs);
957 CloseHandle(This->remoting_mutex);
959 HeapFree(GetProcessHeap(), 0, This);
962 /* finds the proxy manager corresponding to a given OXID and OID that has
963 * been unmarshaled in the specified apartment. The caller must release the
964 * reference to the proxy_manager when the object is no longer used. */
965 static BOOL find_proxy_manager(APARTMENT * apt, OXID oxid, OID oid, struct proxy_manager ** proxy_found)
967 BOOL found = FALSE;
968 struct list * cursor;
970 EnterCriticalSection(&apt->cs);
971 LIST_FOR_EACH(cursor, &apt->proxies)
973 struct proxy_manager * proxy = LIST_ENTRY(cursor, struct proxy_manager, entry);
974 if ((oxid == proxy->oxid) && (oid == proxy->oid))
976 *proxy_found = proxy;
977 ClientIdentity_AddRef((IMultiQI *)&proxy->lpVtbl);
978 found = TRUE;
979 break;
982 LeaveCriticalSection(&apt->cs);
983 return found;
986 HRESULT apartment_disconnectproxies(struct apartment *apt)
988 struct list * cursor;
990 LIST_FOR_EACH(cursor, &apt->proxies)
992 struct proxy_manager * proxy = LIST_ENTRY(cursor, struct proxy_manager, entry);
993 proxy_manager_disconnect(proxy);
996 return S_OK;
999 /********************** StdMarshal implementation ****************************/
1000 typedef struct _StdMarshalImpl
1002 const IMarshalVtbl *lpvtbl;
1003 LONG ref;
1005 IID iid;
1006 DWORD dwDestContext;
1007 LPVOID pvDestContext;
1008 DWORD mshlflags;
1009 } StdMarshalImpl;
1011 static HRESULT WINAPI
1012 StdMarshalImpl_QueryInterface(LPMARSHAL iface, REFIID riid, LPVOID *ppv)
1014 *ppv = NULL;
1015 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IMarshal, riid))
1017 *ppv = iface;
1018 IUnknown_AddRef(iface);
1019 return S_OK;
1021 FIXME("No interface for %s.\n", debugstr_guid(riid));
1022 return E_NOINTERFACE;
1025 static ULONG WINAPI
1026 StdMarshalImpl_AddRef(LPMARSHAL iface)
1028 StdMarshalImpl *This = (StdMarshalImpl *)iface;
1029 return InterlockedIncrement(&This->ref);
1032 static ULONG WINAPI
1033 StdMarshalImpl_Release(LPMARSHAL iface)
1035 StdMarshalImpl *This = (StdMarshalImpl *)iface;
1036 ULONG ref = InterlockedDecrement(&This->ref);
1038 if (!ref) HeapFree(GetProcessHeap(),0,This);
1039 return ref;
1042 static HRESULT WINAPI
1043 StdMarshalImpl_GetUnmarshalClass(
1044 LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext,
1045 void* pvDestContext, DWORD mshlflags, CLSID* pCid)
1047 *pCid = CLSID_DfMarshal;
1048 return S_OK;
1051 static HRESULT WINAPI
1052 StdMarshalImpl_GetMarshalSizeMax(
1053 LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext,
1054 void* pvDestContext, DWORD mshlflags, DWORD* pSize)
1056 *pSize = sizeof(STDOBJREF);
1057 return S_OK;
1060 static HRESULT WINAPI
1061 StdMarshalImpl_MarshalInterface(
1062 LPMARSHAL iface, IStream *pStm,REFIID riid, void* pv, DWORD dwDestContext,
1063 void* pvDestContext, DWORD mshlflags)
1065 STDOBJREF stdobjref;
1066 ULONG res;
1067 HRESULT hres;
1068 APARTMENT *apt = COM_CurrentApt();
1070 TRACE("(...,%s,...)\n", debugstr_guid(riid));
1072 if (!apt)
1074 ERR("Apartment not initialized\n");
1075 return CO_E_NOTINITIALIZED;
1078 /* make sure this apartment can be reached from other threads / processes */
1079 RPC_StartRemoting(apt);
1081 hres = marshal_object(apt, &stdobjref, riid, (IUnknown *)pv, mshlflags);
1082 if (hres)
1084 ERR("Failed to create ifstub, hres=0x%lx\n", hres);
1085 return hres;
1088 hres = IStream_Write(pStm, &stdobjref, sizeof(stdobjref), &res);
1089 if (hres) return hres;
1091 return S_OK;
1094 /* helper for StdMarshalImpl_UnmarshalInterface - does the unmarshaling with
1095 * no questions asked about the rules surrounding same-apartment unmarshals
1096 * and table marshaling */
1097 static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt,
1098 MSHCTX dest_context, void *dest_context_data,
1099 REFIID riid, void **object)
1101 struct proxy_manager *proxy_manager = NULL;
1102 HRESULT hr = S_OK;
1104 assert(apt);
1106 TRACE("stdobjref:\n\tflags = %04lx\n\tcPublicRefs = %ld\n\toxid = %s\n\toid = %s\n\tipid = %s\n",
1107 stdobjref->flags, stdobjref->cPublicRefs,
1108 wine_dbgstr_longlong(stdobjref->oxid),
1109 wine_dbgstr_longlong(stdobjref->oid),
1110 debugstr_guid(&stdobjref->ipid));
1112 /* create an a new proxy manager if one doesn't already exist for the
1113 * object */
1114 if (!find_proxy_manager(apt, stdobjref->oxid, stdobjref->oid, &proxy_manager))
1116 hr = proxy_manager_construct(apt, stdobjref->flags,
1117 stdobjref->oxid, stdobjref->oid,
1118 &proxy_manager);
1120 else
1121 TRACE("proxy manager already created, using\n");
1123 if (hr == S_OK)
1125 struct ifproxy * ifproxy;
1127 proxy_manager_set_context(proxy_manager, dest_context, dest_context_data);
1129 hr = proxy_manager_find_ifproxy(proxy_manager, riid, &ifproxy);
1130 if (hr == E_NOINTERFACE)
1132 IRpcChannelBuffer *chanbuf;
1133 hr = RPC_CreateClientChannel(&stdobjref->oxid, &stdobjref->ipid,
1134 proxy_manager->dest_context,
1135 proxy_manager->dest_context_data,
1136 &chanbuf);
1137 if (hr == S_OK)
1138 hr = proxy_manager_create_ifproxy(proxy_manager, stdobjref,
1139 riid, chanbuf, &ifproxy);
1141 else
1142 IUnknown_AddRef((IUnknown *)ifproxy->iface);
1144 if (hr == S_OK)
1145 *object = ifproxy->iface;
1148 /* release our reference to the proxy manager - the client/apartment
1149 * will hold on to the remaining reference for us */
1150 if (proxy_manager) ClientIdentity_Release((IMultiQI*)&proxy_manager->lpVtbl);
1152 return hr;
1155 static HRESULT WINAPI
1156 StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv)
1158 StdMarshalImpl *This = (StdMarshalImpl *)iface;
1159 struct stub_manager *stubmgr;
1160 STDOBJREF stdobjref;
1161 ULONG res;
1162 HRESULT hres;
1163 APARTMENT *apt = COM_CurrentApt();
1164 APARTMENT *stub_apt;
1165 OXID oxid;
1167 TRACE("(...,%s,....)\n", debugstr_guid(riid));
1169 /* we need an apartment to unmarshal into */
1170 if (!apt)
1172 ERR("Apartment not initialized\n");
1173 return CO_E_NOTINITIALIZED;
1176 /* read STDOBJREF from wire */
1177 hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res);
1178 if (hres) return STG_E_READFAULT;
1180 hres = apartment_getoxid(apt, &oxid);
1181 if (hres) return hres;
1183 /* check if we're marshalling back to ourselves */
1184 if ((oxid == stdobjref.oxid) && (stubmgr = get_stub_manager(apt, stdobjref.oid)))
1186 TRACE("Unmarshalling object marshalled in same apartment for iid %s, "
1187 "returning original object %p\n", debugstr_guid(riid), stubmgr->object);
1189 hres = IUnknown_QueryInterface(stubmgr->object, riid, ppv);
1191 /* unref the ifstub. FIXME: only do this on success? */
1192 if (!stub_manager_is_table_marshaled(stubmgr, &stdobjref.ipid))
1193 stub_manager_ext_release(stubmgr, stdobjref.cPublicRefs, TRUE);
1195 stub_manager_int_release(stubmgr);
1196 return hres;
1199 /* notify stub manager about unmarshal if process-local object.
1200 * note: if the oxid is not found then we and native will quite happily
1201 * ignore table marshaling and normal marshaling rules regarding number of
1202 * unmarshals, etc, but if you abuse these rules then your proxy could end
1203 * up returning RPC_E_DISCONNECTED. */
1204 if ((stub_apt = apartment_findfromoxid(stdobjref.oxid, TRUE)))
1206 if ((stubmgr = get_stub_manager(stub_apt, stdobjref.oid)))
1208 if (!stub_manager_notify_unmarshal(stubmgr, &stdobjref.ipid))
1209 hres = CO_E_OBJNOTCONNECTED;
1211 stub_manager_int_release(stubmgr);
1213 else
1215 WARN("Couldn't find object for OXID %s, OID %s, assuming disconnected\n",
1216 wine_dbgstr_longlong(stdobjref.oxid),
1217 wine_dbgstr_longlong(stdobjref.oid));
1218 hres = CO_E_OBJNOTCONNECTED;
1221 apartment_release(stub_apt);
1223 else
1224 TRACE("Treating unmarshal from OXID %s as inter-process\n",
1225 wine_dbgstr_longlong(stdobjref.oxid));
1227 if (hres == S_OK)
1228 hres = unmarshal_object(&stdobjref, apt, This->dwDestContext,
1229 This->pvDestContext, riid, ppv);
1231 if (hres) WARN("Failed with error 0x%08lx\n", hres);
1232 else TRACE("Successfully created proxy %p\n", *ppv);
1234 return hres;
1237 static HRESULT WINAPI
1238 StdMarshalImpl_ReleaseMarshalData(LPMARSHAL iface, IStream *pStm)
1240 STDOBJREF stdobjref;
1241 ULONG res;
1242 HRESULT hres;
1243 struct stub_manager *stubmgr;
1244 APARTMENT *apt;
1246 TRACE("iface=%p, pStm=%p\n", iface, pStm);
1248 hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res);
1249 if (hres) return STG_E_READFAULT;
1251 TRACE("oxid = %s, oid = %s, ipid = %s\n",
1252 wine_dbgstr_longlong(stdobjref.oxid),
1253 wine_dbgstr_longlong(stdobjref.oid),
1254 wine_dbgstr_guid(&stdobjref.ipid));
1256 if (!(apt = apartment_findfromoxid(stdobjref.oxid, TRUE)))
1258 WARN("Could not map OXID %s to apartment object\n",
1259 wine_dbgstr_longlong(stdobjref.oxid));
1260 return RPC_E_INVALID_OBJREF;
1263 if (!(stubmgr = get_stub_manager(apt, stdobjref.oid)))
1265 ERR("could not map object ID to stub manager, oxid=%s, oid=%s\n",
1266 wine_dbgstr_longlong(stdobjref.oxid), wine_dbgstr_longlong(stdobjref.oid));
1267 return RPC_E_INVALID_OBJREF;
1270 stub_manager_release_marshal_data(stubmgr, stdobjref.cPublicRefs, &stdobjref.ipid);
1272 stub_manager_int_release(stubmgr);
1273 apartment_release(apt);
1275 return S_OK;
1278 static HRESULT WINAPI
1279 StdMarshalImpl_DisconnectObject(LPMARSHAL iface, DWORD dwReserved)
1281 FIXME("(), stub!\n");
1282 return S_OK;
1285 static const IMarshalVtbl VT_StdMarshal =
1287 StdMarshalImpl_QueryInterface,
1288 StdMarshalImpl_AddRef,
1289 StdMarshalImpl_Release,
1290 StdMarshalImpl_GetUnmarshalClass,
1291 StdMarshalImpl_GetMarshalSizeMax,
1292 StdMarshalImpl_MarshalInterface,
1293 StdMarshalImpl_UnmarshalInterface,
1294 StdMarshalImpl_ReleaseMarshalData,
1295 StdMarshalImpl_DisconnectObject
1298 static HRESULT StdMarshalImpl_Construct(REFIID riid, void** ppvObject)
1300 StdMarshalImpl * pStdMarshal =
1301 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StdMarshalImpl));
1302 if (!pStdMarshal)
1303 return E_OUTOFMEMORY;
1304 pStdMarshal->lpvtbl = &VT_StdMarshal;
1305 pStdMarshal->ref = 0;
1306 return IMarshal_QueryInterface((IMarshal*)pStdMarshal, riid, ppvObject);
1309 /***********************************************************************
1310 * CoGetStandardMarshal [OLE32.@]
1312 * Gets or creates a standard marshal object.
1314 * PARAMS
1315 * riid [I] Interface identifier of the pUnk object.
1316 * pUnk [I] Optional. Object to get the marshal object for.
1317 * dwDestContext [I] Destination. Used to enable or disable optimizations.
1318 * pvDestContext [I] Reserved. Must be NULL.
1319 * mshlflags [I] Flags affecting the marshaling process.
1320 * ppMarshal [O] Address where marshal object will be stored.
1322 * RETURNS
1323 * Success: S_OK.
1324 * Failure: HRESULT code.
1326 * NOTES
1328 * The function retrieves the IMarshal object associated with an object if
1329 * that object is currently an active stub, otherwise a new marshal object is
1330 * created.
1332 HRESULT WINAPI CoGetStandardMarshal(REFIID riid, IUnknown *pUnk,
1333 DWORD dwDestContext, LPVOID pvDestContext,
1334 DWORD mshlflags, LPMARSHAL *ppMarshal)
1336 StdMarshalImpl *dm;
1338 if (pUnk == NULL)
1340 FIXME("(%s,NULL,%lx,%p,%lx,%p), unimplemented yet.\n",
1341 debugstr_guid(riid),dwDestContext,pvDestContext,mshlflags,ppMarshal);
1342 return E_NOTIMPL;
1344 TRACE("(%s,%p,%lx,%p,%lx,%p)\n",
1345 debugstr_guid(riid),pUnk,dwDestContext,pvDestContext,mshlflags,ppMarshal);
1346 *ppMarshal = HeapAlloc(GetProcessHeap(),0,sizeof(StdMarshalImpl));
1347 dm = (StdMarshalImpl*) *ppMarshal;
1348 if (!dm) return E_FAIL;
1349 dm->lpvtbl = &VT_StdMarshal;
1350 dm->ref = 1;
1352 dm->iid = *riid;
1353 dm->dwDestContext = dwDestContext;
1354 dm->pvDestContext = pvDestContext;
1355 dm->mshlflags = mshlflags;
1356 return S_OK;
1359 /***********************************************************************
1360 * get_marshaler [internal]
1362 * Retrieves an IMarshal interface for an object.
1364 static HRESULT get_marshaler(REFIID riid, IUnknown *pUnk, DWORD dwDestContext,
1365 void *pvDestContext, DWORD mshlFlags,
1366 LPMARSHAL *pMarshal)
1368 HRESULT hr;
1370 if (!pUnk)
1371 return E_POINTER;
1372 hr = IUnknown_QueryInterface(pUnk, &IID_IMarshal, (LPVOID*)pMarshal);
1373 if (hr)
1374 hr = CoGetStandardMarshal(riid, pUnk, dwDestContext, pvDestContext,
1375 mshlFlags, pMarshal);
1376 return hr;
1379 /***********************************************************************
1380 * get_unmarshaler_from_stream [internal]
1382 * Creates an IMarshal* object according to the data marshaled to the stream.
1383 * The function leaves the stream pointer at the start of the data written
1384 * to the stream by the IMarshal* object.
1386 static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal, IID *iid)
1388 HRESULT hr;
1389 ULONG res;
1390 OBJREF objref;
1392 /* read common OBJREF header */
1393 hr = IStream_Read(stream, &objref, FIELD_OFFSET(OBJREF, u_objref), &res);
1394 if (hr || (res != FIELD_OFFSET(OBJREF, u_objref)))
1396 ERR("Failed to read common OBJREF header, 0x%08lx\n", hr);
1397 return STG_E_READFAULT;
1400 /* sanity check on header */
1401 if (objref.signature != OBJREF_SIGNATURE)
1403 ERR("Bad OBJREF signature 0x%08lx\n", objref.signature);
1404 return RPC_E_INVALID_OBJREF;
1407 if (iid) *iid = objref.iid;
1409 /* FIXME: handler marshaling */
1410 if (objref.flags & OBJREF_STANDARD)
1412 TRACE("Using standard unmarshaling\n");
1413 hr = StdMarshalImpl_Construct(&IID_IMarshal, (LPVOID*)marshal);
1415 else if (objref.flags & OBJREF_CUSTOM)
1417 ULONG custom_header_size = FIELD_OFFSET(OBJREF, u_objref.u_custom.pData) -
1418 FIELD_OFFSET(OBJREF, u_objref.u_custom);
1419 TRACE("Using custom unmarshaling\n");
1420 /* read constant sized OR_CUSTOM data from stream */
1421 hr = IStream_Read(stream, &objref.u_objref.u_custom,
1422 custom_header_size, &res);
1423 if (hr || (res != custom_header_size))
1425 ERR("Failed to read OR_CUSTOM header, 0x%08lx\n", hr);
1426 return STG_E_READFAULT;
1428 /* now create the marshaler specified in the stream */
1429 hr = CoCreateInstance(&objref.u_objref.u_custom.clsid, NULL,
1430 CLSCTX_INPROC_SERVER, &IID_IMarshal,
1431 (LPVOID*)marshal);
1433 else
1435 FIXME("Invalid or unimplemented marshaling type specified: %lx\n",
1436 objref.flags);
1437 return RPC_E_INVALID_OBJREF;
1440 if (hr)
1441 ERR("Failed to create marshal, 0x%08lx\n", hr);
1443 return hr;
1446 /***********************************************************************
1447 * CoGetMarshalSizeMax [OLE32.@]
1449 * Gets the maximum amount of data that will be needed by a marshal.
1451 * PARAMS
1452 * pulSize [O] Address where maximum marshal size will be stored.
1453 * riid [I] Identifier of the interface to marshal.
1454 * pUnk [I] Pointer to the object to marshal.
1455 * dwDestContext [I] Destination. Used to enable or disable optimizations.
1456 * pvDestContext [I] Reserved. Must be NULL.
1457 * mshlFlags [I] Flags that affect the marshaling. See CoMarshalInterface().
1459 * RETURNS
1460 * Success: S_OK.
1461 * Failure: HRESULT code.
1463 * SEE ALSO
1464 * CoMarshalInterface().
1466 HRESULT WINAPI CoGetMarshalSizeMax(ULONG *pulSize, REFIID riid, IUnknown *pUnk,
1467 DWORD dwDestContext, void *pvDestContext,
1468 DWORD mshlFlags)
1470 HRESULT hr;
1471 LPMARSHAL pMarshal;
1472 CLSID marshaler_clsid;
1474 hr = get_marshaler(riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &pMarshal);
1475 if (hr)
1476 return hr;
1478 hr = IMarshal_GetUnmarshalClass(pMarshal, riid, pUnk, dwDestContext,
1479 pvDestContext, mshlFlags, &marshaler_clsid);
1480 if (hr)
1482 ERR("IMarshal::GetUnmarshalClass failed, 0x%08lx\n", hr);
1483 IMarshal_Release(pMarshal);
1484 return hr;
1487 hr = IMarshal_GetMarshalSizeMax(pMarshal, riid, pUnk, dwDestContext,
1488 pvDestContext, mshlFlags, pulSize);
1489 if (IsEqualCLSID(&marshaler_clsid, &CLSID_DfMarshal))
1490 /* add on the size of the common header */
1491 *pulSize += FIELD_OFFSET(OBJREF, u_objref);
1492 else
1493 /* custom marshaling: add on the size of the whole OBJREF structure
1494 * like native does */
1495 *pulSize += sizeof(OBJREF);
1497 IMarshal_Release(pMarshal);
1498 return hr;
1502 static void dump_MSHLFLAGS(MSHLFLAGS flags)
1504 if (flags & MSHLFLAGS_TABLESTRONG)
1505 TRACE(" MSHLFLAGS_TABLESTRONG");
1506 if (flags & MSHLFLAGS_TABLEWEAK)
1507 TRACE(" MSHLFLAGS_TABLEWEAK");
1508 if (!(flags & (MSHLFLAGS_TABLESTRONG|MSHLFLAGS_TABLEWEAK)))
1509 TRACE(" MSHLFLAGS_NORMAL");
1510 if (flags & MSHLFLAGS_NOPING)
1511 TRACE(" MSHLFLAGS_NOPING");
1514 /***********************************************************************
1515 * CoMarshalInterface [OLE32.@]
1517 * Marshals an interface into a stream so that the object can then be
1518 * unmarshaled from another COM apartment and used remotely.
1520 * PARAMS
1521 * pStream [I] Stream the object will be marshaled into.
1522 * riid [I] Identifier of the interface to marshal.
1523 * pUnk [I] Pointer to the object to marshal.
1524 * dwDestContext [I] Destination. Used to enable or disable optimizations.
1525 * pvDestContext [I] Reserved. Must be NULL.
1526 * mshlFlags [I] Flags that affect the marshaling. See notes.
1528 * RETURNS
1529 * Success: S_OK.
1530 * Failure: HRESULT code.
1532 * NOTES
1534 * The mshlFlags parameter can take one or more of the following flags:
1535 *| MSHLFLAGS_NORMAL - Unmarshal once, releases stub on last proxy release.
1536 *| MSHLFLAGS_TABLESTRONG - Unmarshal many, release when CoReleaseMarshalData() called.
1537 *| MSHLFLAGS_TABLEWEAK - Unmarshal many, releases stub on last proxy release.
1538 *| MSHLFLAGS_NOPING - No automatic garbage collection (and so reduces network traffic).
1540 * If a marshaled object is not unmarshaled, then CoReleaseMarshalData() must
1541 * be called in order to release the resources used in the marshaling.
1543 * SEE ALSO
1544 * CoUnmarshalInterface(), CoReleaseMarshalData().
1546 HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk,
1547 DWORD dwDestContext, void *pvDestContext,
1548 DWORD mshlFlags)
1550 HRESULT hr;
1551 CLSID marshaler_clsid;
1552 OBJREF objref;
1553 LPMARSHAL pMarshal;
1555 TRACE("(%p, %s, %p, %lx, %p,", pStream, debugstr_guid(riid), pUnk,
1556 dwDestContext, pvDestContext);
1557 dump_MSHLFLAGS(mshlFlags);
1558 TRACE(")\n");
1560 if (pUnk == NULL)
1561 return E_INVALIDARG;
1563 objref.signature = OBJREF_SIGNATURE;
1564 objref.iid = *riid;
1566 /* get the marshaler for the specified interface */
1567 hr = get_marshaler(riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &pMarshal);
1568 if (hr)
1570 ERR("Failed to get marshaller, 0x%08lx\n", hr);
1571 return hr;
1574 hr = IMarshal_GetUnmarshalClass(pMarshal, riid, pUnk, dwDestContext,
1575 pvDestContext, mshlFlags, &marshaler_clsid);
1576 if (hr)
1578 ERR("IMarshal::GetUnmarshalClass failed, 0x%08lx\n", hr);
1579 goto cleanup;
1582 /* FIXME: implement handler marshaling too */
1583 if (IsEqualCLSID(&marshaler_clsid, &CLSID_DfMarshal))
1585 TRACE("Using standard marshaling\n");
1586 objref.flags = OBJREF_STANDARD;
1588 /* write the common OBJREF header to the stream */
1589 hr = IStream_Write(pStream, &objref, FIELD_OFFSET(OBJREF, u_objref), NULL);
1590 if (hr)
1592 ERR("Failed to write OBJREF header to stream, 0x%08lx\n", hr);
1593 goto cleanup;
1596 else
1598 TRACE("Using custom marshaling\n");
1599 objref.flags = OBJREF_CUSTOM;
1600 objref.u_objref.u_custom.clsid = marshaler_clsid;
1601 objref.u_objref.u_custom.cbExtension = 0;
1602 objref.u_objref.u_custom.size = 0;
1603 hr = IMarshal_GetMarshalSizeMax(pMarshal, riid, pUnk, dwDestContext,
1604 pvDestContext, mshlFlags,
1605 &objref.u_objref.u_custom.size);
1606 if (hr)
1608 ERR("Failed to get max size of marshal data, error 0x%08lx\n", hr);
1609 goto cleanup;
1611 /* write constant sized common header and OR_CUSTOM data into stream */
1612 hr = IStream_Write(pStream, &objref,
1613 FIELD_OFFSET(OBJREF, u_objref.u_custom.pData), NULL);
1614 if (hr)
1616 ERR("Failed to write OR_CUSTOM header to stream with 0x%08lx\n", hr);
1617 goto cleanup;
1621 TRACE("Calling IMarshal::MarshalInterace\n");
1622 /* call helper object to do the actual marshaling */
1623 hr = IMarshal_MarshalInterface(pMarshal, pStream, riid, pUnk, dwDestContext,
1624 pvDestContext, mshlFlags);
1626 if (hr)
1628 ERR("Failed to marshal the interface %s, %lx\n", debugstr_guid(riid), hr);
1629 goto cleanup;
1632 cleanup:
1633 IMarshal_Release(pMarshal);
1635 TRACE("completed with hr 0x%08lx\n", hr);
1637 return hr;
1640 /***********************************************************************
1641 * CoUnmarshalInterface [OLE32.@]
1643 * Unmarshals an object from a stream by creating a proxy to the remote
1644 * object, if necessary.
1646 * PARAMS
1648 * pStream [I] Stream containing the marshaled object.
1649 * riid [I] Interface identifier of the object to create a proxy to.
1650 * ppv [O] Address where proxy will be stored.
1652 * RETURNS
1654 * Success: S_OK.
1655 * Failure: HRESULT code.
1657 * SEE ALSO
1658 * CoMarshalInterface().
1660 HRESULT WINAPI CoUnmarshalInterface(IStream *pStream, REFIID riid, LPVOID *ppv)
1662 HRESULT hr;
1663 LPMARSHAL pMarshal;
1664 IID iid;
1665 IUnknown *object;
1667 TRACE("(%p, %s, %p)\n", pStream, debugstr_guid(riid), ppv);
1669 hr = get_unmarshaler_from_stream(pStream, &pMarshal, &iid);
1670 if (hr != S_OK)
1671 return hr;
1673 /* call the helper object to do the actual unmarshaling */
1674 hr = IMarshal_UnmarshalInterface(pMarshal, pStream, &iid, (LPVOID*)&object);
1675 if (hr)
1676 ERR("IMarshal::UnmarshalInterface failed, 0x%08lx\n", hr);
1678 /* IID_NULL means use the interface ID of the marshaled object */
1679 if (!IsEqualIID(riid, &IID_NULL))
1680 iid = *riid;
1682 if (hr == S_OK)
1684 if (!IsEqualIID(riid, &iid))
1686 TRACE("requested interface != marshalled interface, additional QI needed\n");
1687 hr = IUnknown_QueryInterface(object, &iid, ppv);
1688 if (hr)
1689 ERR("Couldn't query for interface %s, hr = 0x%08lx\n",
1690 debugstr_guid(riid), hr);
1691 IUnknown_Release(object);
1693 else
1695 *ppv = object;
1699 IMarshal_Release(pMarshal);
1701 TRACE("completed with hr 0x%lx\n", hr);
1703 return hr;
1706 /***********************************************************************
1707 * CoReleaseMarshalData [OLE32.@]
1709 * Releases resources associated with an object that has been marshaled into
1710 * a stream.
1712 * PARAMS
1714 * pStream [I] The stream that the object has been marshaled into.
1716 * RETURNS
1717 * Success: S_OK.
1718 * Failure: HRESULT error code.
1720 * NOTES
1722 * Call this function to release resources associated with a normal or
1723 * table-weak marshal that will not be unmarshaled, and all table-strong
1724 * marshals when they are no longer needed.
1726 * SEE ALSO
1727 * CoMarshalInterface(), CoUnmarshalInterface().
1729 HRESULT WINAPI CoReleaseMarshalData(IStream *pStream)
1731 HRESULT hr;
1732 LPMARSHAL pMarshal;
1734 TRACE("(%p)\n", pStream);
1736 hr = get_unmarshaler_from_stream(pStream, &pMarshal, NULL);
1737 if (hr != S_OK)
1738 return hr;
1740 /* call the helper object to do the releasing of marshal data */
1741 hr = IMarshal_ReleaseMarshalData(pMarshal, pStream);
1742 if (hr)
1743 ERR("IMarshal::ReleaseMarshalData failed with error 0x%08lx\n", hr);
1745 IMarshal_Release(pMarshal);
1746 return hr;
1750 /***********************************************************************
1751 * CoMarshalInterThreadInterfaceInStream [OLE32.@]
1753 * Marshal an interface across threads in the same process.
1755 * PARAMS
1756 * riid [I] Identifier of the interface to be marshalled.
1757 * pUnk [I] Pointer to IUnknown-derived interface that will be marshalled.
1758 * ppStm [O] Pointer to IStream object that is created and then used to store the marshalled inteface.
1760 * RETURNS
1761 * Success: S_OK
1762 * Failure: E_OUTOFMEMORY and other COM error codes
1764 * SEE ALSO
1765 * CoMarshalInterface(), CoUnmarshalInterface() and CoGetInterfaceAndReleaseStream()
1767 HRESULT WINAPI CoMarshalInterThreadInterfaceInStream(
1768 REFIID riid, LPUNKNOWN pUnk, LPSTREAM * ppStm)
1770 ULARGE_INTEGER xpos;
1771 LARGE_INTEGER seekto;
1772 HRESULT hres;
1774 TRACE("(%s, %p, %p)\n",debugstr_guid(riid), pUnk, ppStm);
1776 hres = CreateStreamOnHGlobal(NULL, TRUE, ppStm);
1777 if (FAILED(hres)) return hres;
1778 hres = CoMarshalInterface(*ppStm, riid, pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
1780 if (SUCCEEDED(hres))
1782 memset(&seekto, 0, sizeof(seekto));
1783 IStream_Seek(*ppStm, seekto, SEEK_SET, &xpos);
1785 else
1787 IStream_Release(*ppStm);
1788 *ppStm = NULL;
1791 return hres;
1794 /***********************************************************************
1795 * CoGetInterfaceAndReleaseStream [OLE32.@]
1797 * Unmarshalls an inteface from a stream and then releases the stream.
1799 * PARAMS
1800 * pStm [I] Stream that contains the marshalled inteface.
1801 * riid [I] Interface identifier of the object to unmarshall.
1802 * ppv [O] Address of pointer where the requested interface object will be stored.
1804 * RETURNS
1805 * Success: S_OK
1806 * Failure: A COM error code
1808 * SEE ALSO
1809 * CoMarshalInterThreadInterfaceInStream() and CoUnmarshalInteface()
1811 HRESULT WINAPI CoGetInterfaceAndReleaseStream(LPSTREAM pStm, REFIID riid,
1812 LPVOID *ppv)
1814 HRESULT hres;
1816 TRACE("(%p, %s, %p)\n", pStm, debugstr_guid(riid), ppv);
1818 hres = CoUnmarshalInterface(pStm, riid, ppv);
1819 IStream_Release(pStm);
1820 return hres;
1823 static HRESULT WINAPI StdMarshalCF_QueryInterface(LPCLASSFACTORY iface,
1824 REFIID riid, LPVOID *ppv)
1826 *ppv = NULL;
1827 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory))
1829 *ppv = (LPVOID)iface;
1830 return S_OK;
1832 return E_NOINTERFACE;
1835 static ULONG WINAPI StdMarshalCF_AddRef(LPCLASSFACTORY iface)
1837 return 2; /* non-heap based object */
1840 static ULONG WINAPI StdMarshalCF_Release(LPCLASSFACTORY iface)
1842 return 1; /* non-heap based object */
1845 static HRESULT WINAPI StdMarshalCF_CreateInstance(LPCLASSFACTORY iface,
1846 LPUNKNOWN pUnk, REFIID riid, LPVOID *ppv)
1848 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMarshal))
1849 return StdMarshalImpl_Construct(riid, ppv);
1851 FIXME("(%s), not supported.\n",debugstr_guid(riid));
1852 return E_NOINTERFACE;
1855 static HRESULT WINAPI StdMarshalCF_LockServer(LPCLASSFACTORY iface, BOOL fLock)
1857 FIXME("(%d), stub!\n",fLock);
1858 return S_OK;
1861 static const IClassFactoryVtbl StdMarshalCFVtbl =
1863 StdMarshalCF_QueryInterface,
1864 StdMarshalCF_AddRef,
1865 StdMarshalCF_Release,
1866 StdMarshalCF_CreateInstance,
1867 StdMarshalCF_LockServer
1869 static const IClassFactoryVtbl *StdMarshalCF = &StdMarshalCFVtbl;
1871 HRESULT MARSHAL_GetStandardMarshalCF(LPVOID *ppv)
1873 *ppv = &StdMarshalCF;
1874 return S_OK;
1877 /***********************************************************************
1878 * CoMarshalHresult [OLE32.@]
1880 * Marshals an HRESULT value into a stream.
1882 * PARAMS
1883 * pStm [I] Stream that hresult will be marshalled into.
1884 * hresult [I] HRESULT to be marshalled.
1886 * RETURNS
1887 * Success: S_OK
1888 * Failure: A COM error code
1890 * SEE ALSO
1891 * CoUnmarshalHresult().
1893 HRESULT WINAPI CoMarshalHresult(LPSTREAM pStm, HRESULT hresult)
1895 return IStream_Write(pStm, &hresult, sizeof(hresult), NULL);
1898 /***********************************************************************
1899 * CoUnmarshalHresult [OLE32.@]
1901 * Unmarshals an HRESULT value from a stream.
1903 * PARAMS
1904 * pStm [I] Stream that hresult will be unmarshalled from.
1905 * phresult [I] Pointer to HRESULT where the value will be unmarshalled to.
1907 * RETURNS
1908 * Success: S_OK
1909 * Failure: A COM error code
1911 * SEE ALSO
1912 * CoMarshalHresult().
1914 HRESULT WINAPI CoUnmarshalHresult(LPSTREAM pStm, HRESULT * phresult)
1916 return IStream_Read(pStm, phresult, sizeof(*phresult), NULL);