Added parser template and made AVISplitter use it.
[wine] / dlls / ole32 / stubmanager.c
1 /*
2  * A stub manager is an object that controls interface stubs. It is
3  * identified by an OID (object identifier) and acts as the network
4  * identity of the object. There can be many stub managers in a
5  * process or apartment.
6  *
7  * Copyright 2002 Marcus Meissner
8  * Copyright 2004 Mike Hearn for CodeWeavers
9  * Copyright 2004 Robert Shearman (for CodeWeavers)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #define COBJMACROS
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
29
30 #include <assert.h>
31 #include <stdarg.h>
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winuser.h"
36 #include "objbase.h"
37 #include "ole2.h"
38 #include "ole2ver.h"
39 #include "rpc.h"
40 #include "wine/debug.h"
41 #include "compobj_private.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(ole);
44
45 static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *ifstub);
46 static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const IPID *ipid);
47
48 /* creates a new stub manager and adds it into the apartment. caller must
49  * release stub manager when it is no longer required. the apartment and
50  * external refs together take one implicit ref */
51 struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object)
52 {
53     struct stub_manager *sm;
54
55     assert( apt );
56     
57     sm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct stub_manager));
58     if (!sm) return NULL;
59
60     list_init(&sm->ifstubs);
61     InitializeCriticalSection(&sm->lock);
62     IUnknown_AddRef(object);
63     sm->object = object;
64     sm->apt    = apt;
65
66     /* start off with 2 references because the stub is in the apartment
67      * and the caller will also hold a reference */
68     sm->refs   = 2;
69
70     /* yes, that's right, this starts at zero. that's zero EXTERNAL
71      * refs, ie nobody has unmarshalled anything yet. we can't have
72      * negative refs because the stub manager cannot be explicitly
73      * killed, it has to die by somebody unmarshalling then releasing
74      * the marshalled ifptr.
75      */
76     sm->extrefs = 0;
77     
78     EnterCriticalSection(&apt->cs);
79     sm->oid    = apt->oidc++;
80     list_add_head(&apt->stubmgrs, &sm->entry);
81     LeaveCriticalSection(&apt->cs);
82
83     TRACE("Created new stub manager (oid=%s) at %p for object with IUnknown %p\n", wine_dbgstr_longlong(sm->oid), sm, object);
84     
85     return sm;
86 }
87
88 /* m->apt->cs must be held on entry to this function */
89 static void stub_manager_delete(struct stub_manager *m)
90 {
91     struct list *cursor;
92
93     TRACE("destroying %p (oid=%s)\n", m, wine_dbgstr_longlong(m->oid));
94
95     list_remove(&m->entry);
96
97     /* release every ifstub */
98     while ((cursor = list_head(&m->ifstubs)))
99     {
100         struct ifstub *ifstub = LIST_ENTRY(cursor, struct ifstub, entry);
101         stub_manager_delete_ifstub(m, ifstub);
102     }
103
104     IUnknown_Release(m->object);
105
106     DeleteCriticalSection(&m->lock);
107
108     HeapFree(GetProcessHeap(), 0, m);
109 }
110
111 /* gets the stub manager associated with an object - caller must have
112  * a reference to the apartment while a reference to the stub manager is held.
113  * it must also call release on the stub manager when it is no longer needed */
114 struct stub_manager *get_stub_manager_from_object(APARTMENT *apt, void *object)
115 {
116     struct stub_manager *result = NULL;
117     struct list         *cursor;
118
119     EnterCriticalSection(&apt->cs);
120     LIST_FOR_EACH( cursor, &apt->stubmgrs )
121     {
122         struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
123
124         if (m->object == object)
125         {
126             result = m;
127             stub_manager_int_addref(result);
128             break;
129         }
130     }
131     LeaveCriticalSection(&apt->cs);
132
133     if (result)
134         TRACE("found %p for object %p\n", result, object);
135     else
136         TRACE("not found for object %p\n", object);
137
138     return result;    
139 }
140
141 /* gets the stub manager associated with an object id - caller must have
142  * a reference to the apartment while a reference to the stub manager is held.
143  * it must also call release on the stub manager when it is no longer needed */
144 struct stub_manager *get_stub_manager(APARTMENT *apt, OID oid)
145 {
146     struct stub_manager *result = NULL;
147     struct list         *cursor;
148
149     EnterCriticalSection(&apt->cs);
150     LIST_FOR_EACH( cursor, &apt->stubmgrs )
151     {
152         struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
153
154         if (m->oid == oid)
155         {
156             result = m;
157             stub_manager_int_addref(result);
158             break;
159         }
160     }
161     LeaveCriticalSection(&apt->cs);
162
163     if (result)
164         TRACE("found %p for oid %s\n", result, wine_dbgstr_longlong(oid));
165     else
166         TRACE("not found for oid %s\n", wine_dbgstr_longlong(oid));
167
168     return result;
169 }
170
171 /* increments the internal refcount */
172 ULONG stub_manager_int_addref(struct stub_manager *This)
173 {
174     ULONG refs;
175
176     EnterCriticalSection(&This->apt->cs);
177     refs = ++This->refs;
178     LeaveCriticalSection(&This->apt->cs);
179
180     TRACE("before %ld\n", refs - 1);
181
182     return refs;
183 }
184
185 /* decrements the internal refcount */
186 ULONG stub_manager_int_release(struct stub_manager *This)
187 {
188     ULONG refs;
189     APARTMENT *apt = This->apt;
190
191     EnterCriticalSection(&apt->cs);
192     refs = --This->refs;
193
194     TRACE("after %ld\n", refs);
195
196     if (!refs)
197         stub_manager_delete(This);
198     LeaveCriticalSection(&apt->cs);
199
200     return refs;
201 }
202
203 /* add some external references (ie from a client that unmarshaled an ifptr) */
204 ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs)
205 {
206     ULONG rc = InterlockedExchangeAdd(&m->extrefs, refs) + refs;
207
208     TRACE("added %lu refs to %p (oid %s), rc is now %lu\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
209
210     return rc;
211 }
212
213 /* remove some external references */
214 ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs)
215 {
216     ULONG rc = InterlockedExchangeAdd(&m->extrefs, -refs) - refs;
217     
218     TRACE("removed %lu refs from %p (oid %s), rc is now %lu\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
219
220     if (rc == 0)
221         stub_manager_int_release(m);
222
223     return rc;
224 }
225
226 static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const IPID *ipid)
227 {
228     struct list    *cursor;
229     struct ifstub  *result = NULL;
230     
231     EnterCriticalSection(&m->lock);
232     LIST_FOR_EACH( cursor, &m->ifstubs )
233     {
234         struct ifstub *ifstub = LIST_ENTRY( cursor, struct ifstub, entry );
235
236         if (IsEqualGUID(ipid, &ifstub->ipid))
237         {
238             result = ifstub;
239             break;
240         }
241     }
242     LeaveCriticalSection(&m->lock);
243
244     return result;
245 }
246
247 /* gets the stub manager associated with an ipid - caller must have
248  * a reference to the apartment while a reference to the stub manager is held.
249  * it must also call release on the stub manager when it is no longer needed */
250 static struct stub_manager *get_stub_manager_from_ipid(APARTMENT *apt, const IPID *ipid)
251 {
252     struct stub_manager *result = NULL;
253     struct list         *cursor;
254
255     EnterCriticalSection(&apt->cs);
256     LIST_FOR_EACH( cursor, &apt->stubmgrs )
257     {
258         struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
259
260         if (stub_manager_ipid_to_ifstub(m, ipid))
261         {
262             result = m;
263             stub_manager_int_addref(result);
264             break;
265         }
266     }
267     LeaveCriticalSection(&apt->cs);
268
269     if (result)
270         TRACE("found %p for ipid %s\n", result, debugstr_guid(ipid));
271     else
272         ERR("not found for ipid %s\n", debugstr_guid(ipid));
273
274     return result;
275 }
276
277 HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret)
278 {
279     /* FIXME: hack for IRemUnknown */
280     if (ipid->Data2 == 0xffff)
281         *stub_apt = COM_ApartmentFromOXID(*(OXID *)ipid->Data4, TRUE);
282     else
283         *stub_apt = COM_ApartmentFromTID(ipid->Data2);
284     if (!*stub_apt)
285     {
286         ERR("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2);
287         return RPC_E_INVALID_OBJECT;
288     }
289     *stubmgr_ret = get_stub_manager_from_ipid(*stub_apt, ipid);
290     if (!*stubmgr_ret)
291     {
292         COM_ApartmentRelease(*stub_apt);
293         *stub_apt = NULL;
294         return RPC_E_INVALID_OBJECT;
295     }
296     return S_OK;
297 }
298
299 IRpcStubBuffer *ipid_to_stubbuffer(const IPID *ipid)
300 {
301     IRpcStubBuffer *ret = NULL;
302     APARTMENT *apt;
303     struct stub_manager *stubmgr;
304     struct ifstub *ifstub;
305     HRESULT hr;
306
307     hr = ipid_to_stub_manager(ipid, &apt, &stubmgr);
308     if (hr != S_OK) return NULL;
309
310     ifstub = stub_manager_ipid_to_ifstub(stubmgr, ipid);
311     if (ifstub)
312         ret = ifstub->stubbuffer;
313
314     stub_manager_int_release(stubmgr);
315
316     COM_ApartmentRelease(apt);
317
318     return ret;
319 }
320
321 /* generates an ipid in the following format (similar to native version):
322  * Data1 = apartment-local ipid counter
323  * Data2 = apartment creator thread ID
324  * Data3 = process ID
325  * Data4 = random value
326  */
327 static inline HRESULT generate_ipid(struct stub_manager *m, IPID *ipid)
328 {
329     HRESULT hr;
330     hr = UuidCreate(ipid);
331     if (FAILED(hr))
332     {
333         ERR("couldn't create IPID for stub manager %p\n", m);
334         UuidCreateNil(ipid);
335         return hr;
336     }
337
338     ipid->Data1 = InterlockedIncrement(&m->apt->ipidc);
339     ipid->Data2 = (USHORT)m->apt->tid;
340     ipid->Data3 = (USHORT)GetCurrentProcessId();
341     return S_OK;
342 }
343
344 /* registers a new interface stub COM object with the stub manager and returns registration record */
345 struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid, BOOL tablemarshal)
346 {
347     struct ifstub *stub;
348
349     TRACE("oid=%s, stubbuffer=%p, iptr=%p, iid=%s, tablemarshal=%s\n",
350           wine_dbgstr_longlong(m->oid), sb, iptr, debugstr_guid(iid), tablemarshal ? "TRUE" : "FALSE");
351
352     stub = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ifstub));
353     if (!stub) return NULL;
354
355     stub->stubbuffer = sb;
356     IUnknown_AddRef(sb);
357
358     /* no need to ref this, same object as sb */
359     stub->iface = iptr;
360
361     if (tablemarshal)
362         stub->state = IFSTUB_STATE_TABLE_MARSHALED;
363     else
364         stub->state = IFSTUB_STATE_NORMAL_MARSHALED;
365
366     stub->iid = *iid;
367
368     /* FIXME: hack for IRemUnknown because we don't notify SCM of our IPID
369      * yet, so we need to use a well-known one */
370     if (IsEqualIID(iid, &IID_IRemUnknown))
371     {
372         stub->ipid.Data1 = 0xffffffff;
373         stub->ipid.Data2 = 0xffff;
374         stub->ipid.Data3 = 0xffff;
375         assert(sizeof(stub->ipid.Data4) == sizeof(m->apt->oxid));
376         memcpy(&stub->ipid.Data4, &m->apt->oxid, sizeof(OXID));
377     }
378     else
379         generate_ipid(m, &stub->ipid);
380
381     EnterCriticalSection(&m->lock);
382     list_add_head(&m->ifstubs, &stub->entry);
383     LeaveCriticalSection(&m->lock);
384
385     TRACE("ifstub %p created with ipid %s\n", stub, debugstr_guid(&stub->ipid));
386
387     return stub;
388 }
389
390 static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *ifstub)
391 {
392     TRACE("m=%p, m->oid=%s, ipid=%s\n", m, wine_dbgstr_longlong(m->oid), debugstr_guid(&ifstub->ipid));
393     
394     list_remove(&ifstub->entry);
395         
396     IUnknown_Release(ifstub->stubbuffer);
397     IUnknown_Release(ifstub->iface);
398
399     HeapFree(GetProcessHeap(), 0, ifstub);
400 }
401
402 /* returns TRUE if it is possible to unmarshal, FALSE otherwise. */
403 BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid)
404 {
405     struct ifstub *ifstub;
406     BOOL ret;
407
408     ifstub = stub_manager_ipid_to_ifstub(m, ipid);
409     if (!ifstub)
410     {
411         WARN("Can't find ifstub for OID %s, IPID %s\n",
412             wine_dbgstr_longlong(m->oid), wine_dbgstr_guid(ipid));
413         return FALSE;
414     }
415
416     EnterCriticalSection(&m->lock);
417
418     switch (ifstub->state)
419     {
420     case IFSTUB_STATE_TABLE_MARSHALED:
421         ret = TRUE;
422         break;
423     case IFSTUB_STATE_NORMAL_MARSHALED:
424         ifstub->state = IFSTUB_STATE_NORMAL_UNMARSHALED;
425         ret = TRUE;
426         break;
427     default:
428         WARN("object OID %s, IPID %s already unmarshaled\n",
429             wine_dbgstr_longlong(m->oid), wine_dbgstr_guid(ipid));
430         ret = FALSE;
431         break;
432     }
433
434     LeaveCriticalSection(&m->lock);
435
436     return ret;
437 }
438
439 /* is an ifstub table marshaled? */
440 BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid)
441 {
442     struct ifstub *ifstub;
443     BOOL ret;
444
445     ifstub = stub_manager_ipid_to_ifstub(m, ipid);
446     if (!ifstub)
447     {
448         WARN("Can't find ifstub for OID %s, IPID %s\n",
449             wine_dbgstr_longlong(m->oid), wine_dbgstr_guid(ipid));
450         return FALSE;
451     }
452
453     EnterCriticalSection(&m->lock);
454     ret = (ifstub->state == IFSTUB_STATE_TABLE_MARSHALED);
455     LeaveCriticalSection(&m->lock);
456
457     return ret;
458 }
459
460
461 /*****************************************************************************
462  *
463  * IRemUnknown implementation
464  *
465  *
466  * Note: this object is not related to the lifetime of a stub_manager, but it
467  * interacts with stub managers.
468  */
469
470 const IID IID_IRemUnknown = { 0x00000131, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
471
472 typedef struct rem_unknown
473 {
474     const IRemUnknownVtbl *lpVtbl;
475     ULONG refs;
476 } RemUnknown;
477
478 static const IRemUnknownVtbl RemUnknown_Vtbl;
479
480
481 /* construct an IRemUnknown object with one outstanding reference */
482 static HRESULT RemUnknown_Construct(IRemUnknown **ppRemUnknown)
483 {
484     RemUnknown *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
485
486     if (!This) return E_OUTOFMEMORY;
487
488     This->lpVtbl = &RemUnknown_Vtbl;
489     This->refs = 1;
490
491     *ppRemUnknown = (IRemUnknown *)This;
492     return S_OK;
493 }
494
495 static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, void **ppv)
496 {
497     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
498
499     if (IsEqualIID(riid, &IID_IUnknown) ||
500         IsEqualIID(riid, &IID_IRemUnknown))
501     {
502         *ppv = (LPVOID)iface;
503         IRemUnknown_AddRef(iface);
504         return S_OK;
505     }
506
507     FIXME("No interface for iid %s\n", debugstr_guid(riid));
508
509     *ppv = NULL;
510     return E_NOINTERFACE;
511 }
512
513 static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface)
514 {
515     ULONG refs;
516     RemUnknown *This = (RemUnknown *)iface;
517
518     refs = InterlockedIncrement(&This->refs);
519
520     TRACE("%p before: %ld\n", iface, refs-1);
521     return refs;
522 }
523
524 static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface)
525 {
526     ULONG refs;
527     RemUnknown *This = (RemUnknown *)iface;
528
529     refs = InterlockedDecrement(&This->refs);
530     if (!refs)
531         HeapFree(GetProcessHeap(), 0, This);
532
533     TRACE("%p after: %ld\n", iface, refs);
534     return refs;
535 }
536
537 static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface,
538     REFIPID ripid, ULONG cRefs, USHORT cIids, IID *iids /* [size_is(cIids)] */,
539     REMQIRESULT **ppQIResults /* [size_is(,cIids)] */)
540 {
541     HRESULT hr;
542     USHORT i;
543     USHORT successful_qis = 0;
544     APARTMENT *apt;
545     struct stub_manager *stubmgr;
546
547     TRACE("(%p)->(%s, %ld, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
548
549     hr = ipid_to_stub_manager(ripid, &apt, &stubmgr);
550     if (hr != S_OK) return hr;
551
552     *ppQIResults = CoTaskMemAlloc(sizeof(REMQIRESULT) * cIids);
553
554     for (i = 0; i < cIids; i++)
555     {
556         HRESULT hrobj = register_ifstub(apt, &(*ppQIResults)[i].std, &iids[i],
557                                         stubmgr->object, MSHLFLAGS_NORMAL);
558         if (hrobj == S_OK)
559             successful_qis++;
560         (*ppQIResults)[i].hResult = hrobj;
561     }
562
563     stub_manager_int_release(stubmgr);
564     COM_ApartmentRelease(apt);
565
566     if (successful_qis == cIids)
567         return S_OK; /* we got all requested interfaces */
568     else if (successful_qis == 0)
569         return E_NOINTERFACE; /* we didn't get any interfaces */
570     else
571         return S_FALSE; /* we got some interfaces */
572 }
573
574 static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface,
575     USHORT cInterfaceRefs,
576     REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */,
577     HRESULT *pResults /* [size_is(cInterfaceRefs)] */)
578 {
579     HRESULT hr = S_OK;
580     USHORT i;
581
582     TRACE("(%p)->(%d, %p, %p)\n", iface, cInterfaceRefs, InterfaceRefs, pResults);
583
584     for (i = 0; i < cInterfaceRefs; i++)
585     {
586         APARTMENT *apt;
587         struct stub_manager *stubmgr;
588
589         pResults[i] = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
590         if (pResults[i] != S_OK)
591         {
592             hr = S_FALSE;
593             continue;
594         }
595
596         stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs);
597         if (InterfaceRefs[i].cPrivateRefs)
598             FIXME("Adding %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
599
600         stub_manager_int_release(stubmgr);
601         COM_ApartmentRelease(apt);
602     }
603
604     return hr;
605 }
606
607 static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface,
608     USHORT cInterfaceRefs,
609     REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */)
610 {
611     HRESULT hr = S_OK;
612     USHORT i;
613
614     TRACE("(%p)->(%d, %p)\n", iface, cInterfaceRefs, InterfaceRefs);
615
616     for (i = 0; i < cInterfaceRefs; i++)
617     {
618         APARTMENT *apt;
619         struct stub_manager *stubmgr;
620
621         hr = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
622         if (hr != S_OK)
623         {
624             hr = E_INVALIDARG;
625             /* FIXME: we should undo any changes already made in this function */
626             break;
627         }
628
629         stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs);
630         if (InterfaceRefs[i].cPrivateRefs)
631             FIXME("Releasing %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
632
633         stub_manager_int_release(stubmgr);
634         COM_ApartmentRelease(apt);
635     }
636
637     return hr;
638 }
639
640 static const IRemUnknownVtbl RemUnknown_Vtbl =
641 {
642     RemUnknown_QueryInterface,
643     RemUnknown_AddRef,
644     RemUnknown_Release,
645     RemUnknown_RemQueryInterface,
646     RemUnknown_RemAddRef,
647     RemUnknown_RemRelease
648 };
649
650 /* starts the IRemUnknown listener for the current apartment */
651 HRESULT start_apartment_remote_unknown()
652 {
653     IRemUnknown *pRemUnknown;
654     HRESULT hr = S_OK;
655     APARTMENT *apt = COM_CurrentApt();
656
657     EnterCriticalSection(&apt->cs);
658     if (!apt->remunk_exported)
659     {
660         /* create the IRemUnknown object */
661         hr = RemUnknown_Construct(&pRemUnknown);
662         if (hr == S_OK)
663         {
664             STDOBJREF stdobjref; /* dummy - not used */
665             /* register it with the stub manager */
666             hr = register_ifstub(COM_CurrentApt(), &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL);
667             /* release our reference to the object as the stub manager will manage the life cycle for us */
668             IRemUnknown_Release(pRemUnknown);
669             if (hr == S_OK)
670                 apt->remunk_exported = TRUE;
671         }
672     }
673     LeaveCriticalSection(&apt->cs);
674     return hr;
675 }