1 /*
2 * Marshalling library
3 *
4 * Copyright 2002 Marcus Meissner
5 * Copyright 2004 Mike Hearn, for CodeWeavers
6 * Copyright 2004 Rob Shearman, for CodeWeavers
7 *
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.
12 *
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.
17 *
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
21 */
22
23 #include <stdarg.h>
24 #include <string.h>
25 #include <assert.h>
26
27 #define COBJMACROS
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "objbase.h"
33 #include "ole2.h"
34 #include "winerror.h"
35 #include "wine/unicode.h"
36
37 #include "compobj_private.h"
38
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(ole);
42
43 extern const CLSID CLSID_DfMarshal;
44
45 /* number of refs given out for normal marshaling */
46 #define NORMALEXTREFS 5
47
48
49 /* private flag indicating that the object was marshaled as table-weak */
50 #define SORFP_TABLEWEAK SORF_OXRES1
51 /* private flag indicating that the caller does not want to notify the stub
52 * when the proxy disconnects or is destroyed */
53 #define SORFP_NOLIFETIMEMGMT SORF_OXRES2
54
55 static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt,
56 MSHCTX dest_context, void *dest_context_data,
57 REFIID riid, const OXID_INFO *oxid_info,
58 void **object);
59
60 /* Marshalling just passes a unique identifier to the remote client,
61 * that makes it possible to find the passed interface again.
62 *
63 * So basically we need a set of values that make it unique.
64 *
65 * Note that the IUnknown_QI(ob,xiid,&ppv) always returns the SAME ppv value!
66 *
67 * A triple is used: OXID (apt id), OID (stub manager id),
68 * IPID (interface ptr/stub id).
69 *
70 * OXIDs identify an apartment and are network scoped
71 * OIDs identify a stub manager and are apartment scoped
72 * IPIDs identify an interface stub and are apartment scoped
73 */
74
75 static inline HRESULT get_facbuf_for_iid(REFIID riid, IPSFactoryBuffer **facbuf)
76 {
77 HRESULT hr;
78 CLSID clsid;
79
80 if ((hr = CoGetPSClsid(riid, &clsid)))
81 return hr;
82 return CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER | WINE_CLSCTX_DONT_HOST,
83 NULL, &IID_IPSFactoryBuffer, (LPVOID*)facbuf);
84 }
85
86 /* marshals an object into a STDOBJREF structure */
87 HRESULT marshal_object(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, MSHLFLAGS mshlflags)
88 {
89 struct stub_manager *manager;
90 struct ifstub *ifstub;
91 BOOL tablemarshal;
92 IRpcStubBuffer *stub = NULL;
93 HRESULT hr;
94 IUnknown *iobject = NULL; /* object of type riid */
95
96 hr = apartment_getoxid(apt, &stdobjref->oxid);
97 if (hr != S_OK)
98 return hr;
99
100 hr = apartment_createwindowifneeded(apt);
101 if (hr != S_OK)
102 return hr;
103
104 hr = IUnknown_QueryInterface(object, riid, (void **)&iobject);
105 if (hr != S_OK)
106 {
107 ERR("object doesn't expose interface %s, failing with error 0x%08x\n",
108 debugstr_guid(riid), hr);
109 return E_NOINTERFACE;
110 }
111
112 /* IUnknown doesn't require a stub buffer, because it never goes out on
113 * the wire */
114 if (!IsEqualIID(riid, &IID_IUnknown))
115 {
116 IPSFactoryBuffer *psfb;
117
118 hr = get_facbuf_for_iid(riid, &psfb);
119 if (hr != S_OK)
120 {
121 ERR("couldn't get IPSFactory buffer for interface %s\n", debugstr_guid(riid));
122 IUnknown_Release(iobject);
123 return hr;
124 }
125
126 hr = IPSFactoryBuffer_CreateStub(psfb, riid, iobject, &stub);
127 IPSFactoryBuffer_Release(psfb);
128 if (hr != S_OK)
129 {
130 ERR("Failed to create an IRpcStubBuffer from IPSFactory for %s with error 0x%08x\n",
131 debugstr_guid(riid), hr);
132 IUnknown_Release(iobject);
133 return hr;
134 }
135 }
136
137 stdobjref->flags = SORF_NULL;
138 if (mshlflags & MSHLFLAGS_TABLEWEAK)
139 stdobjref->flags |= SORFP_TABLEWEAK;
140 if (mshlflags & MSHLFLAGS_NOPING)
141 stdobjref->flags |= SORF_NOPING;
142
143 if ((manager = get_stub_manager_from_object(apt, object)))
144 TRACE("registering new ifstub on pre-existing manager\n");
145 else
146 {
147 TRACE("constructing new stub manager\n");
148
149 manager = new_stub_manager(apt, object);
150 if (!manager)
151 {
152 if (stub) IRpcStubBuffer_Release(stub);
153 IUnknown_Release(iobject);
154 return E_OUTOFMEMORY;
155 }
156 }
157 stdobjref->oid = manager->oid;
158
159 tablemarshal = ((mshlflags & MSHLFLAGS_TABLESTRONG) || (mshlflags & MSHLFLAGS_TABLEWEAK));
160
161 /* make sure ifstub that we are creating is unique */
162 ifstub = stub_manager_find_ifstub(manager, riid, mshlflags);
163 if (!ifstub)
164 ifstub = stub_manager_new_ifstub(manager, stub, iobject, riid, mshlflags);
165
166 if (stub) IRpcStubBuffer_Release(stub);
167 IUnknown_Release(iobject);
168
169 if (!ifstub)
170 {
171 stub_manager_int_release(manager);
172 /* destroy the stub manager if it has no ifstubs by releasing
173 * zero external references */
174 stub_manager_ext_release(manager, 0, FALSE, TRUE);
175 return E_OUTOFMEMORY;
176 }
177
178 if (!tablemarshal)
179 {
180 stdobjref->cPublicRefs = NORMALEXTREFS;
181 stub_manager_ext_addref(manager, stdobjref->cPublicRefs, FALSE);
182 }
183 else
184 {
185 stdobjref->cPublicRefs = 0;
186 if (mshlflags & MSHLFLAGS_TABLESTRONG)
187 stub_manager_ext_addref(manager, 1, FALSE);
188 else
189 stub_manager_ext_addref(manager, 0, TRUE);
190 }
191
192 /* FIXME: check return value */
193 RPC_RegisterInterface(riid);
194
195 stdobjref->ipid = ifstub->ipid;
196
197 stub_manager_int_release(manager);
198 return S_OK;
199 }
200
201
202
203 /* Client-side identity of the server object */
204
205 static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk);
206 static void proxy_manager_destroy(struct proxy_manager * This);
207 static HRESULT proxy_manager_find_ifproxy(struct proxy_manager * This, REFIID riid, struct ifproxy ** ifproxy_found);
208 static HRESULT proxy_manager_query_local_interface(struct proxy_manager * This, REFIID riid, void ** ppv);
209
210 static HRESULT WINAPI ClientIdentity_QueryInterface(IMultiQI * iface, REFIID riid, void ** ppv)
211 {
212 HRESULT hr;
213 MULTI_QI mqi;
214
215 TRACE("%s\n", debugstr_guid(riid));
216
217 mqi.pIID = riid;
218 hr = IMultiQI_QueryMultipleInterfaces(iface, 1, &mqi);
219 *ppv = (void *)mqi.pItf;
220
221 return hr;
222 }
223
224 static ULONG WINAPI ClientIdentity_AddRef(IMultiQI * iface)
225 {
226 struct proxy_manager * This = (struct proxy_manager *)iface;
227 TRACE("%p - before %d\n", iface, This->refs);
228 return InterlockedIncrement(&This->refs);
229 }
230
231 static ULONG WINAPI ClientIdentity_Release(IMultiQI * iface)
232 {
233 struct proxy_manager * This = (struct proxy_manager *)iface;
234 ULONG refs = InterlockedDecrement(&This->refs);
235 TRACE("%p - after %d\n", iface, refs);
236 if (!refs)
237 proxy_manager_destroy(This);
238 return refs;
239 }
240
241 static HRESULT WINAPI ClientIdentity_QueryMultipleInterfaces(IMultiQI *iface, ULONG cMQIs, MULTI_QI *pMQIs)
242 {
243 struct proxy_manager * This = (struct proxy_manager *)iface;
244 REMQIRESULT *qiresults = NULL;
245 ULONG nonlocal_mqis = 0;
246 ULONG i;
247 ULONG successful_mqis = 0;
248 IID *iids = HeapAlloc(GetProcessHeap(), 0, cMQIs * sizeof(*iids));
249 /* mapping of RemQueryInterface index to QueryMultipleInterfaces index */
250 ULONG *mapping = HeapAlloc(GetProcessHeap(), 0, cMQIs * sizeof(*mapping));
251
252 TRACE("cMQIs: %d\n", cMQIs);
253
254 /* try to get a local interface - this includes already active proxy
255 * interfaces and also interfaces exposed by the proxy manager */
256 for (i = 0; i < cMQIs; i++)
257 {
258 TRACE("iid[%d] = %s\n", i, debugstr_guid(pMQIs[i].pIID));
259 pMQIs[i].hr = proxy_manager_query_local_interface(This, pMQIs[i].pIID, (void **)&pMQIs[i].pItf);
260 if (pMQIs[i].hr == S_OK)
261 successful_mqis++;
262 else
263 {
264 iids[nonlocal_mqis] = *pMQIs[i].pIID;
265 mapping[nonlocal_mqis] = i;
266 nonlocal_mqis++;
267 }
268 }
269
270 TRACE("%d interfaces not found locally\n", nonlocal_mqis);
271
272 /* if we have more than one interface not found locally then we must try
273 * to query the remote object for it */
274 if (nonlocal_mqis != 0)
275 {
276 IRemUnknown *remunk;
277 HRESULT hr;
278 IPID *ipid;
279
280 /* get the ipid of the first entry */
281 /* FIXME: should we implement ClientIdentity on the ifproxies instead
282 * of the proxy_manager so we use the correct ipid here? */
283 ipid = &LIST_ENTRY(list_head(&This->interfaces), struct ifproxy, entry)->stdobjref.ipid;
284
285 /* get IRemUnknown proxy so we can communicate with the remote object */
286 hr = proxy_manager_get_remunknown(This, &remunk);
287
288 if (SUCCEEDED(hr))
289 {
290 hr = IRemUnknown_RemQueryInterface(remunk, ipid, NORMALEXTREFS,
291 nonlocal_mqis, iids, &qiresults);
292 IRemUnknown_Release(remunk);
293 if (FAILED(hr))
294 ERR("IRemUnknown_RemQueryInterface failed with error 0x%08x\n", hr);
295 }
296
297 /* IRemUnknown_RemQueryInterface can return S_FALSE if only some of
298 * the interfaces were returned */
299 if (SUCCEEDED(hr))
300 {
301 /* try to unmarshal each object returned to us */
302 for (i = 0; i < nonlocal_mqis; i++)
303 {
304 ULONG index = mapping[i];
305 HRESULT hrobj = qiresults[i].hResult;
306 if (hrobj == S_OK)
307 hrobj = unmarshal_object(&qiresults[i].std, COM_CurrentApt(),
308 This->dest_context,
309 This->dest_context_data,
310 pMQIs[index].pIID, &This->oxid_info,
311 (void **)&pMQIs[index].pItf);
312
313 if (hrobj == S_OK)
314 successful_mqis++;
315 else
316 ERR("Failed to get pointer to interface %s\n", debugstr_guid(pMQIs[index].pIID));
317 pMQIs[index].hr = hrobj;
318 }
319 }
320
321 /* free the memory allocated by the proxy */
322 CoTaskMemFree(qiresults);
323 }
324
325 TRACE("%d/%d successfully queried\n", successful_mqis, cMQIs);
326
327 HeapFree(GetProcessHeap(), 0, iids);
328 HeapFree(GetProcessHeap(), 0, mapping);
329
330 if (successful_mqis == cMQIs)
331 return S_OK; /* we got all requested interfaces */
332 else if (successful_mqis == 0)
333 return E_NOINTERFACE; /* we didn't get any interfaces */
334 else
335 return S_FALSE; /* we got some interfaces */
336 }
337
338 static const IMultiQIVtbl ClientIdentity_Vtbl =
339 {
340 ClientIdentity_QueryInterface,
341 ClientIdentity_AddRef,
342 ClientIdentity_Release,
343 ClientIdentity_QueryMultipleInterfaces
344 };
345
346 /* FIXME: remove these */
347 static HRESULT WINAPI StdMarshalImpl_GetUnmarshalClass(LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, CLSID* pCid);
348 static HRESULT WINAPI StdMarshalImpl_GetMarshalSizeMax(LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, DWORD* pSize);
349 static HRESULT WINAPI StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv);
350 static HRESULT WINAPI StdMarshalImpl_ReleaseMarshalData(LPMARSHAL iface, IStream *pStm);
351 static HRESULT WINAPI StdMarshalImpl_DisconnectObject(LPMARSHAL iface, DWORD dwReserved);
352
353 static HRESULT WINAPI Proxy_QueryInterface(IMarshal *iface, REFIID riid, void **ppvObject)
354 {
355 ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
356 return IMultiQI_QueryInterface((IMultiQI *)&This->lpVtbl, riid, ppvObject);
357 }
358
359 static ULONG WINAPI Proxy_AddRef(IMarshal *iface)
360 {
361 ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
362 return IMultiQI_AddRef((IMultiQI *)&This->lpVtbl);
363 }
364
365 static ULONG WINAPI Proxy_Release(IMarshal *iface)
366 {
367 ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
368 return IMultiQI_Release((IMultiQI *)&This->lpVtbl);
369 }
370
371 static HRESULT WINAPI Proxy_MarshalInterface(
372 LPMARSHAL iface, IStream *pStm, REFIID riid, void* pv, DWORD dwDestContext,
373 void* pvDestContext, DWORD mshlflags)
374 {
375 ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
376 HRESULT hr;
377 struct ifproxy *ifproxy;
378
379 TRACE("(...,%s,...)\n", debugstr_guid(riid));
380
381 hr = proxy_manager_find_ifproxy(This, riid, &ifproxy);
382 if (SUCCEEDED(hr))
383 {
384 STDOBJREF stdobjref = ifproxy->stdobjref;
385
386 stdobjref.cPublicRefs = 0;
387
388 if ((mshlflags != MSHLFLAGS_TABLEWEAK) &&
389 (mshlflags != MSHLFLAGS_TABLESTRONG))
390 {
391 ULONG cPublicRefs = ifproxy->refs;
392 ULONG cPublicRefsOld;
393 /* optimization - share out proxy's public references if possible
394 * instead of making new proxy do a roundtrip through the server */
395 do
396 {
397 ULONG cPublicRefsNew;
398 cPublicRefsOld = cPublicRefs;
399 stdobjref.cPublicRefs = cPublicRefs / 2;
400 cPublicRefsNew = cPublicRefs - stdobjref.cPublicRefs;
401 cPublicRefs = InterlockedCompareExchange(
402 (LONG *)&ifproxy->refs, cPublicRefsNew, cPublicRefsOld);
403 } while (cPublicRefs != cPublicRefsOld);
404 }
405
406 /* normal and table-strong marshaling need at least one reference */
407 if (!stdobjref.cPublicRefs && (mshlflags != MSHLFLAGS_TABLEWEAK))
408 {
409 IRemUnknown *remunk;
410 hr = proxy_manager_get_remunknown(This, &remunk);
411 if (hr == S_OK)
412 {
413 HRESULT hrref = S_OK;
414 REMINTERFACEREF rif;
415 rif.ipid = ifproxy->stdobjref.ipid;
416 rif.cPublicRefs = (mshlflags == MSHLFLAGS_TABLESTRONG) ? 1 : NORMALEXTREFS;
417 rif.cPrivateRefs = 0;
418 hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref);
419 IRemUnknown_Release(remunk);
420 if (hr == S_OK && hrref == S_OK)
421 {
422 /* table-strong marshaling doesn't give the refs to the
423 * client that unmarshals the STDOBJREF */
424 if (mshlflags != MSHLFLAGS_TABLESTRONG)
425 stdobjref.cPublicRefs = rif.cPublicRefs;
426 }
427 else
428 ERR("IRemUnknown_RemAddRef returned with 0x%08x, hrref = 0x%08x\n", hr, hrref);
429 }
430 }
431
432 if (SUCCEEDED(hr))
433 {
434 TRACE("writing stdobjref:\n\tflags = %04lx\n\tcPublicRefs = %ld\n\toxid = %s\n\toid = %s\n\tipid = %s\n",
435 stdobjref.flags, stdobjref.cPublicRefs,
436 wine_dbgstr_longlong(stdobjref.oxid),
437 wine_dbgstr_longlong(stdobjref.oid),
438 debugstr_guid(&stdobjref.ipid));
439 hr = IStream_Write(pStm, &stdobjref, sizeof(stdobjref), NULL);
440 }
441 }
442 else
443 {
444 /* we don't have the interface already unmarshaled so we have to
445 * request the object from the server */
446 IRemUnknown *remunk;
447 IPID *ipid;
448 REMQIRESULT *qiresults = NULL;
449 IID iid = *riid;
450
451 /* get the ipid of the first entry */
452 /* FIXME: should we implement ClientIdentity on the ifproxies instead
453 * of the proxy_manager so we use the correct ipid here? */
454 ipid = &LIST_ENTRY(list_head(&This->interfaces), struct ifproxy, entry)->stdobjref.ipid;
455
456 /* get IRemUnknown proxy so we can communicate with the remote object */
457 hr = proxy_manager_get_remunknown(This, &remunk);
458
459 if (hr == S_OK)
460 {
461 hr = IRemUnknown_RemQueryInterface(remunk, ipid, NORMALEXTREFS,
462 1, &iid, &qiresults);
463 if (SUCCEEDED(hr))
464 {
465 hr = IStream_Write(pStm, &qiresults->std, sizeof(qiresults->std), NULL);
466 if (FAILED(hr))
467 {
468 REMINTERFACEREF rif;
469 rif.ipid = qiresults->std.ipid;
470 rif.cPublicRefs = qiresults->std.cPublicRefs;
471 rif.cPrivateRefs = 0;
472 IRemUnknown_RemRelease(remunk, 1, &rif);
473 }
474 CoTaskMemFree(qiresults);
475 }
476 else
477 ERR("IRemUnknown_RemQueryInterface failed with error 0x%08x\n", hr);
478 IRemUnknown_Release(remunk);
479 }
480 }
481
482 return hr;
483 }
484
485 static const IMarshalVtbl ProxyMarshal_Vtbl =
486 {
487 Proxy_QueryInterface,
488 Proxy_AddRef,
489 Proxy_Release,
490 StdMarshalImpl_GetUnmarshalClass,
491 StdMarshalImpl_GetMarshalSizeMax,
492 Proxy_MarshalInterface,
493 StdMarshalImpl_UnmarshalInterface,
494 StdMarshalImpl_ReleaseMarshalData,
495 StdMarshalImpl_DisconnectObject
496 };
497
498 static HRESULT WINAPI ProxyCliSec_QueryInterface(IClientSecurity *iface, REFIID riid, void **ppvObject)
499 {
500 ICOM_THIS_MULTI(struct proxy_manager, lpVtblCliSec, iface);
501 return IMultiQI_QueryInterface((IMultiQI *)&This->lpVtbl, riid, ppvObject);
502 }
503
504 static ULONG WINAPI ProxyCliSec_AddRef(IClientSecurity *iface)
505 {
506 ICOM_THIS_MULTI(struct proxy_manager, lpVtblCliSec, iface);
507 return IMultiQI_AddRef((IMultiQI *)&This->lpVtbl);
508 }
509
510 static ULONG WINAPI ProxyCliSec_Release(IClientSecurity *iface)
511 {
512 ICOM_THIS_MULTI(struct proxy_manager, lpVtblCliSec, iface);
513 return IMultiQI_Release((IMultiQI *)&This->lpVtbl);
514 }
515
516 static HRESULT WINAPI ProxyCliSec_QueryBlanket(IClientSecurity *iface,
517 IUnknown *pProxy,
518 DWORD *pAuthnSvc,
519 DWORD *pAuthzSvc,
520 OLECHAR **ppServerPrincName,
521 DWORD *pAuthnLevel,
522 DWORD *pImpLevel,
523 void **pAuthInfo,
524 DWORD *pCapabilities)
525 {
526 FIXME("(%p, %p, %p, %p, %p, %p, %p, %p): stub\n", pProxy, pAuthnSvc,
527 pAuthzSvc, ppServerPrincName, pAuthnLevel, pImpLevel, pAuthInfo,
528 pCapabilities);
529
530 if (pAuthnSvc)
531 *pAuthnSvc = 0;
532 if (pAuthzSvc)
533 *pAuthzSvc = 0;
534 if (ppServerPrincName)
535 *ppServerPrincName = NULL;
536 if (pAuthnLevel)
537 *pAuthnLevel = RPC_C_AUTHN_LEVEL_DEFAULT;
538 if (pImpLevel)
539 *pImpLevel = RPC_C_IMP_LEVEL_DEFAULT;
540 if (pAuthInfo)
541 *pAuthInfo = NULL;
542 if (pCapabilities)
543 *pCapabilities = EOAC_NONE;
544
545 return E_NOTIMPL;
546 }
547
548 static HRESULT WINAPI ProxyCliSec_SetBlanket(IClientSecurity *iface,
549 IUnknown *pProxy, DWORD AuthnSvc,
550 DWORD AuthzSvc,
551 OLECHAR *pServerPrincName,
552 DWORD AuthnLevel, DWORD ImpLevel,
553 void *pAuthInfo,
554 DWORD Capabilities)
555 {
556 FIXME("(%p, %d, %d, %s, %d, %d, %p, 0x%x): stub\n", pProxy, AuthnSvc,
557 AuthzSvc, debugstr_w(pServerPrincName), AuthnLevel, ImpLevel,
558 pAuthInfo, Capabilities);
559 return E_NOTIMPL;
560 }
561
562 static HRESULT WINAPI ProxyCliSec_CopyProxy(IClientSecurity *iface,
563 IUnknown *pProxy, IUnknown **ppCopy)
564 {
565 FIXME("(%p, %p): stub\n", pProxy, ppCopy);
566 *ppCopy = NULL;
567 return E_NOTIMPL;
568 }
569
570 static const IClientSecurityVtbl ProxyCliSec_Vtbl =
571 {
572 ProxyCliSec_QueryInterface,
573 ProxyCliSec_AddRef,
574 ProxyCliSec_Release,
575 ProxyCliSec_QueryBlanket,
576 ProxyCliSec_SetBlanket,
577 ProxyCliSec_CopyProxy
578 };
579
580 static HRESULT ifproxy_get_public_ref(struct ifproxy * This)
581 {
582 HRESULT hr = S_OK;
583
584 if (WAIT_OBJECT_0 != WaitForSingleObject(This->parent->remoting_mutex, INFINITE))
585 {
586 ERR("Wait failed for ifproxy %p\n", This);
587 return E_UNEXPECTED;
588 }
589
590 if (This->refs == 0)
591 {
592 IRemUnknown *remunk = NULL;
593
594 TRACE("getting public ref for ifproxy %p\n", This);
595
596 hr = proxy_manager_get_remunknown(This->parent, &remunk);
597 if (hr == S_OK)
598 {
599 HRESULT hrref = S_OK;
600 REMINTERFACEREF rif;
601 rif.ipid = This->stdobjref.ipid;
602 rif.cPublicRefs = NORMALEXTREFS;
603 rif.cPrivateRefs = 0;
604 hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref);
605 IRemUnknown_Release(remunk);
606 if (hr == S_OK && hrref == S_OK)
607 InterlockedExchangeAdd((LONG *)&This->refs, NORMALEXTREFS);
608 else
609 ERR("IRemUnknown_RemAddRef returned with 0x%08x, hrref = 0x%08x\n", hr, hrref);
610 }
611 }
612 ReleaseMutex(This->parent->remoting_mutex);
613
614 return hr;
615 }
616
617 static HRESULT ifproxy_release_public_refs(struct ifproxy * This)
618 {
619 HRESULT hr = S_OK;
620 LONG public_refs;
621
622 if (WAIT_OBJECT_0 != WaitForSingleObject(This->parent->remoting_mutex, INFINITE))
623 {
624 ERR("Wait failed for ifproxy %p\n", This);
625 return E_UNEXPECTED;
626 }
627
628 public_refs = This->refs;
629 if (public_refs > 0)
630 {
631 IRemUnknown *remunk = NULL;
632
633 TRACE("releasing %d refs\n", public_refs);
634
635 hr = proxy_manager_get_remunknown(This->parent, &remunk);
636 if (hr == S_OK)
637 {
638 REMINTERFACEREF rif;
639 rif.ipid = This->stdobjref.ipid;
640 rif.cPublicRefs = public_refs;
641 rif.cPrivateRefs = 0;
642 hr = IRemUnknown_RemRelease(remunk, 1, &rif);
643 IRemUnknown_Release(remunk);
644 if (hr == S_OK)
645 InterlockedExchangeAdd((LONG *)&This->refs, -public_refs);
646 else if (hr == RPC_E_DISCONNECTED)
647 WARN("couldn't release references because object was "
648 "disconnected: oxid = %s, oid = %s\n",
649 wine_dbgstr_longlong(This->parent->oxid),
650 wine_dbgstr_longlong(This->parent->oid));
651 else
652 ERR("IRemUnknown_RemRelease failed with error 0x%08x\n", hr);
653 }
654 }
655 ReleaseMutex(This->parent->remoting_mutex);
656
657 return hr;
658 }
659
660 /* should be called inside This->parent->cs critical section */
661 static void ifproxy_disconnect(struct ifproxy * This)
662 {
663 ifproxy_release_public_refs(This);
664 if (This->proxy) IRpcProxyBuffer_Disconnect(This->proxy);
665
666 IRpcChannelBuffer_Release(This->chan);
667 This->chan = NULL;
668 }
669
670 /* should be called in This->parent->cs critical section if it is an entry in parent's list */
671 static void ifproxy_destroy(struct ifproxy * This)
672 {
673 TRACE("%p\n", This);
674
675 /* release public references to this object so that the stub can know
676 * when to destroy itself */
677 ifproxy_release_public_refs(This);
678
679 list_remove(&This->entry);
680
681 if (This->chan)
682 {
683 IRpcChannelBuffer_Release(This->chan);
684 This->chan = NULL;
685 }
686
687 if (This->proxy) IRpcProxyBuffer_Release(This->proxy);
688
689 HeapFree(GetProcessHeap(), 0, This);
690 }
691
692 static HRESULT proxy_manager_construct(
693 APARTMENT * apt, ULONG sorflags, OXID oxid, OID oid,
694 const OXID_INFO *oxid_info, struct proxy_manager ** proxy_manager)
695 {
696 struct proxy_manager * This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
697 if (!This) return E_OUTOFMEMORY;
698
699 This->remoting_mutex = CreateMutexW(NULL, FALSE, NULL);
700 if (!This->remoting_mutex)
701 {
702 HeapFree(GetProcessHeap(), 0, This);
703 return HRESULT_FROM_WIN32(GetLastError());
704 }
705
706 if (oxid_info)
707 {
708 This->oxid_info.dwPid = oxid_info->dwPid;
709 This->oxid_info.dwTid = oxid_info->dwTid;
710 This->oxid_info.ipidRemUnknown = oxid_info->ipidRemUnknown;
711 This->oxid_info.dwAuthnHint = oxid_info->dwAuthnHint;
712 This->oxid_info.psa = NULL /* FIXME: copy from oxid_info */;
713 }
714 else
715 {
716 HRESULT hr = RPC_ResolveOxid(oxid, &This->oxid_info);
717 if (FAILED(hr))
718 {
719 CloseHandle(This->remoting_mutex);
720 HeapFree(GetProcessHeap(), 0, This);
721 return hr;
722 }
723 }
724
725 This->lpVtbl = &ClientIdentity_Vtbl;
726 This->lpVtblMarshal = &ProxyMarshal_Vtbl;
727 This->lpVtblCliSec = &ProxyCliSec_Vtbl;
728
729 list_init(&This->entry);
730 list_init(&This->interfaces);
731
732 InitializeCriticalSection(&This->cs);
733 DEBUG_SET_CRITSEC_NAME(&This->cs, "proxy_manager");
734
735 /* the apartment the object was unmarshaled into */
736 This->parent = apt;
737
738 /* the source apartment and id of the object */
739 This->oxid = oxid;
740 This->oid = oid;
741
742 This->refs = 1;
743
744 /* the DCOM draft specification states that the SORF_NOPING flag is
745 * proxy manager specific, not ifproxy specific, so this implies that we
746 * should store the STDOBJREF flags here in the proxy manager. */
747 This->sorflags = sorflags;
748
749 /* we create the IRemUnknown proxy on demand */
750 This->remunk = NULL;
751
752 /* initialise these values to the weakest values and they will be
753 * overwritten in proxy_manager_set_context */
754 This->dest_context = MSHCTX_INPROC;
755 This->dest_context_data = NULL;
756
757 EnterCriticalSection(&apt->cs);
758 /* FIXME: we are dependent on the ordering in here to make sure a proxy's
759 * IRemUnknown proxy doesn't get destroyed before the regual proxy does
760 * because we need the IRemUnknown proxy during the destruction of the
761 * regular proxy. Ideally, we should maintain a separate list for the
762 * IRemUnknown proxies that need late destruction */
763 list_add_tail(&apt->proxies, &This->entry);
764 LeaveCriticalSection(&apt->cs);
765
766 TRACE("%p created for OXID %s, OID %s\n", This,
767 wine_dbgstr_longlong(oxid), wine_dbgstr_longlong(oid));
768
769 *proxy_manager = This;
770 return S_OK;
771 }
772
773 static inline void proxy_manager_set_context(struct proxy_manager *This, MSHCTX dest_context, void *dest_context_data)
774 {
775 MSHCTX old_dest_context = This->dest_context;
776 MSHCTX new_dest_context;
777
778 do
779 {
780 new_dest_context = old_dest_context;
781 /* "stronger" values overwrite "weaker" values. stronger values are
782 * ones that disable more optimisations */
783 switch (old_dest_context)
784 {
785 case MSHCTX_INPROC:
786 new_dest_context = dest_context;
787 break;
788 case MSHCTX_CROSSCTX:
789 switch (dest_context)
790 {
791 case MSHCTX_INPROC:
792 break;
793 default:
794 new_dest_context = dest_context;
795 }
796 break;
797 case MSHCTX_LOCAL:
798 switch (dest_context)
799 {
800 case MSHCTX_INPROC:
801 case MSHCTX_CROSSCTX:
802 break;
803 default:
804 new_dest_context = dest_context;
805 }
806 break;
807 case MSHCTX_NOSHAREDMEM:
808 switch (dest_context)
809 {
810 case MSHCTX_DIFFERENTMACHINE:
811 new_dest_context = dest_context;
812 break;
813 default:
814 break;
815 }
816 break;
817 default:
818 break;
819 }
820
821 if (old_dest_context == new_dest_context) break;
822
823 old_dest_context = InterlockedCompareExchange((PLONG)&This->dest_context, new_dest_context, old_dest_context);
824 } while (new_dest_context != old_dest_context);
825
826 if (dest_context_data)
827 InterlockedExchangePointer(&This->dest_context_data, dest_context_data);
828 }
829
830 static HRESULT proxy_manager_query_local_interface(struct proxy_manager * This, REFIID riid, void ** ppv)
831 {
832 HRESULT hr;
833 struct ifproxy * ifproxy;
834
835 TRACE("%s\n", debugstr_guid(riid));
836
837 if (IsEqualIID(riid, &IID_IUnknown) ||
838 IsEqualIID(riid, &IID_IMultiQI))
839 {
840 *ppv = (void *)&This->lpVtbl;
841 IUnknown_AddRef((IUnknown *)*ppv);
842 return S_OK;
843 }
844 if (IsEqualIID(riid, &IID_IMarshal))
845 {
846 *ppv = (void *)&This->lpVtblMarshal;
847 IUnknown_AddRef((IUnknown *)*ppv);
848 return S_OK;
849 }
850 if (IsEqualIID(riid, &IID_IClientSecurity))
851 {
852 *ppv = (void *)&This->lpVtblCliSec;
853 IUnknown_AddRef((IUnknown *)*ppv);
854 return S_OK;
855 }
856
857 hr = proxy_manager_find_ifproxy(This, riid, &ifproxy);
858 if (hr == S_OK)
859 {
860 *ppv = ifproxy->iface;
861 IUnknown_AddRef((IUnknown *)*ppv);
862 return S_OK;
863 }
864
865 *ppv = NULL;
866 return E_NOINTERFACE;
867 }
868
869 static HRESULT proxy_manager_create_ifproxy(
870 struct proxy_manager * This, const STDOBJREF *stdobjref, REFIID riid,
871 IRpcChannelBuffer * channel, struct ifproxy ** iif_out)
872 {
873 HRESULT hr;
874 IPSFactoryBuffer * psfb;
875 struct ifproxy * ifproxy = HeapAlloc(GetProcessHeap(), 0, sizeof(*ifproxy));
876 if (!ifproxy) return E_OUTOFMEMORY;
877
878 list_init(&ifproxy->entry);
879
880 ifproxy->parent = This;
881 ifproxy->stdobjref = *stdobjref;
882 ifproxy->iid = *riid;
883 ifproxy->refs = 0;
884 ifproxy->proxy = NULL;
885
886 assert(channel);
887 ifproxy->chan = channel; /* FIXME: we should take the binding strings and construct the channel in this function */
888
889 /* the IUnknown interface is special because it does not have a
890 * proxy associated with the ifproxy as we handle IUnknown ourselves */
891 if (IsEqualIID(riid, &IID_IUnknown))
892 {
893 ifproxy->iface = (void *)&This->lpVtbl;
894 IMultiQI_AddRef((IMultiQI *)&This->lpVtbl);
895 hr = S_OK;
896 }
897 else
898 {
899 hr = get_facbuf_for_iid(riid, &psfb);
900 if (hr == S_OK)
901 {
902 /* important note: the outer unknown is set to the proxy manager.
903 * This ensures the COM identity rules are not violated, by having a
904 * one-to-one mapping of objects on the proxy side to objects on the
905 * stub side, no matter which interface you view the object through */
906 hr = IPSFactoryBuffer_CreateProxy(psfb, (IUnknown *)&This->lpVtbl, riid,
907 &ifproxy->proxy, &ifproxy->iface);
908 IPSFactoryBuffer_Release(psfb);
909 if (hr != S_OK)
910 ERR("Could not create proxy for interface %s, error 0x%08x\n",
911 debugstr_guid(riid), hr);
912 }
913 else
914 ERR("Could not get IPSFactoryBuffer for interface %s, error 0x%08x\n",
915 debugstr_guid(riid), hr);
916
917 if (hr == S_OK)
918 hr = IRpcProxyBuffer_Connect(ifproxy->proxy, ifproxy->chan);
919 }
920
921 if (hr == S_OK)