rpcrt4: Add tests for a complex array of pointers.
[wine] / dlls / oleaut32 / connpt.c
1 /*
2  * Implementation of a generic ConnectionPoint object.
3  *
4  * Copyright 2000 Huw D M Davies for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * NOTES:
21  * See one exported function here is CreateConnectionPoint, see
22  * comments just above that function for information.
23  */
24
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <string.h>
28
29 #define COBJMACROS
30
31 #include "winerror.h"
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36 #include "ole2.h"
37 #include "olectl.h"
38 #include "connpt.h"
39
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(ole);
43
44 #define MAXSINKS 10
45
46 /************************************************************************
47  * Implementation of IConnectionPoint
48  */
49 typedef struct ConnectionPointImpl {
50
51   const IConnectionPointVtbl *lpvtbl;
52
53   /* IUnknown of our main object*/
54   IUnknown *Obj;
55
56   /* Reference count */
57   LONG ref;
58
59   /* IID of sink interface */
60   IID iid;
61
62   /* Array of sink IUnknowns */
63   IUnknown **sinks;
64   DWORD maxSinks;
65
66   DWORD nSinks;
67 } ConnectionPointImpl;
68
69 static const IConnectionPointVtbl ConnectionPointImpl_VTable;
70
71
72 /************************************************************************
73  * Implementation of IEnumConnections
74  */
75 typedef struct EnumConnectionsImpl {
76
77   const IEnumConnectionsVtbl *lpvtbl;
78
79   LONG ref;
80
81   /* IUnknown of ConnectionPoint, used for ref counting */
82   IUnknown *pUnk;
83
84   /* Connection Data */
85   CONNECTDATA *pCD;
86   DWORD nConns;
87
88   /* Next connection to enumerate from */
89   DWORD nCur;
90
91 } EnumConnectionsImpl;
92
93 static EnumConnectionsImpl *EnumConnectionsImpl_Construct(IUnknown *pUnk,
94                                                           DWORD nSinks,
95                                                           CONNECTDATA *pCD);
96
97
98 /************************************************************************
99  * ConnectionPointImpl_Construct
100  */
101 static ConnectionPointImpl *ConnectionPointImpl_Construct(IUnknown *pUnk,
102                                                           REFIID riid)
103 {
104   ConnectionPointImpl *Obj;
105
106   Obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*Obj));
107   Obj->lpvtbl = &ConnectionPointImpl_VTable;
108   Obj->Obj = pUnk;
109   Obj->ref = 1;
110   Obj->iid =  *riid;
111   Obj->maxSinks = MAXSINKS;
112   Obj->sinks = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
113                          sizeof(IUnknown*) * MAXSINKS);
114   Obj->nSinks = 0;
115   return Obj;
116 }
117
118 /************************************************************************
119  * ConnectionPointImpl_Destroy
120  */
121 static void ConnectionPointImpl_Destroy(ConnectionPointImpl *Obj)
122 {
123   DWORD i;
124   for(i = 0; i < Obj->maxSinks; i++) {
125     if(Obj->sinks[i]) {
126       IUnknown_Release(Obj->sinks[i]);
127       Obj->sinks[i] = NULL;
128     }
129   }
130   HeapFree(GetProcessHeap(), 0, Obj->sinks);
131   HeapFree(GetProcessHeap(), 0, Obj);
132   return;
133 }
134
135 static ULONG WINAPI ConnectionPointImpl_AddRef(IConnectionPoint* iface);
136 /************************************************************************
137  * ConnectionPointImpl_QueryInterface (IUnknown)
138  *
139  * See Windows documentation for more details on IUnknown methods.
140  */
141 static HRESULT WINAPI ConnectionPointImpl_QueryInterface(
142   IConnectionPoint*  iface,
143   REFIID  riid,
144   void**  ppvObject)
145 {
146   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
147   TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppvObject);
148
149   /*
150    * Perform a sanity check on the parameters.
151    */
152   if ( (This==0) || (ppvObject==0) )
153     return E_INVALIDARG;
154
155   /*
156    * Initialize the return parameter.
157    */
158   *ppvObject = 0;
159
160   /*
161    * Compare the riid with the interface IDs implemented by this object.
162    */
163   if (IsEqualIID(&IID_IUnknown, riid))
164     *ppvObject = This;
165   else if (IsEqualIID(&IID_IConnectionPoint, riid))
166     *ppvObject = This;
167
168   /*
169    * Check that we obtained an interface.
170    */
171   if ((*ppvObject)==0)
172   {
173     FIXME("() : asking for un supported interface %s\n",debugstr_guid(riid));
174     return E_NOINTERFACE;
175   }
176
177   /*
178    * Query Interface always increases the reference count by one when it is
179    * successful
180    */
181   ConnectionPointImpl_AddRef((IConnectionPoint*)This);
182
183   return S_OK;
184 }
185
186
187 /************************************************************************
188  * ConnectionPointImpl_AddRef (IUnknown)
189  *
190  * See Windows documentation for more details on IUnknown methods.
191  */
192 static ULONG WINAPI ConnectionPointImpl_AddRef(IConnectionPoint* iface)
193 {
194   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
195   ULONG refCount = InterlockedIncrement(&This->ref);
196
197   TRACE("(%p)->(ref before=%d)\n", This, refCount - 1);
198
199   return refCount;
200 }
201
202 /************************************************************************
203  * ConnectionPointImpl_Release (IUnknown)
204  *
205  * See Windows documentation for more details on IUnknown methods.
206  */
207 static ULONG WINAPI ConnectionPointImpl_Release(
208       IConnectionPoint* iface)
209 {
210   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
211   ULONG refCount = InterlockedDecrement(&This->ref);
212
213   TRACE("(%p)->(ref before=%d)\n", This, refCount + 1);
214
215   /*
216    * If the reference count goes down to 0, perform suicide.
217    */
218   if (!refCount) ConnectionPointImpl_Destroy(This);
219
220   return refCount;
221 }
222
223 /************************************************************************
224  * ConnectionPointImpl_GetConnectionInterface (IConnectionPoint)
225  *
226  */
227 static HRESULT WINAPI ConnectionPointImpl_GetConnectionInterface(
228                                                IConnectionPoint *iface,
229                                                IID              *piid)
230 {
231   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
232   TRACE("(%p)->(%p) returning %s\n", This, piid, debugstr_guid(&(This->iid)));
233   *piid = This->iid;
234   return S_OK;
235 }
236
237 /************************************************************************
238  * ConnectionPointImpl_GetConnectionPointContainer (IConnectionPoint)
239  *
240  */
241 static HRESULT WINAPI ConnectionPointImpl_GetConnectionPointContainer(
242                                       IConnectionPoint           *iface,
243                                       IConnectionPointContainer  **ppCPC)
244 {
245   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
246   TRACE("(%p)->(%p)\n", This, ppCPC);
247
248   return IUnknown_QueryInterface(This->Obj,
249                                  &IID_IConnectionPointContainer,
250                                  (LPVOID)ppCPC);
251 }
252
253 /************************************************************************
254  * ConnectionPointImpl_Advise (IConnectionPoint)
255  *
256  */
257 static HRESULT WINAPI ConnectionPointImpl_Advise(IConnectionPoint *iface,
258                                                  IUnknown *lpUnk,
259                                                  DWORD *pdwCookie)
260 {
261   DWORD i;
262   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
263   IUnknown *lpSink;
264   TRACE("(%p)->(%p, %p)\n", This, lpUnk, pdwCookie);
265
266   *pdwCookie = 0;
267   if(FAILED(IUnknown_QueryInterface(lpUnk, &This->iid, (LPVOID)&lpSink)))
268     return CONNECT_E_CANNOTCONNECT;
269
270   for(i = 0; i < This->maxSinks; i++) {
271     if(This->sinks[i] == NULL)
272       break;
273   }
274   if(i == This->maxSinks) {
275     This->maxSinks += MAXSINKS;
276     This->sinks = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->sinks,
277                               This->maxSinks * sizeof(IUnknown *));
278   }
279   This->sinks[i] = lpSink;
280   This->nSinks++;
281   *pdwCookie = i + 1;
282   return S_OK;
283 }
284
285
286 /************************************************************************
287  * ConnectionPointImpl_Unadvise (IConnectionPoint)
288  *
289  */
290 static HRESULT WINAPI ConnectionPointImpl_Unadvise(IConnectionPoint *iface,
291                                                    DWORD dwCookie)
292 {
293   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
294   TRACE("(%p)->(%d)\n", This, dwCookie);
295
296   if(dwCookie == 0 || dwCookie > This->maxSinks) return E_INVALIDARG;
297
298   if(This->sinks[dwCookie-1] == NULL) return CONNECT_E_NOCONNECTION;
299
300   IUnknown_Release(This->sinks[dwCookie-1]);
301   This->sinks[dwCookie-1] = NULL;
302   This->nSinks--;
303   return S_OK;
304 }
305
306 /************************************************************************
307  * ConnectionPointImpl_EnumConnections (IConnectionPoint)
308  *
309  */
310 static HRESULT WINAPI ConnectionPointImpl_EnumConnections(
311                                                     IConnectionPoint *iface,
312                                                     LPENUMCONNECTIONS *ppEnum)
313 {
314   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
315   CONNECTDATA *pCD;
316   DWORD i, nextslot;
317   EnumConnectionsImpl *EnumObj;
318   HRESULT hr;
319
320   TRACE("(%p)->(%p)\n", This, ppEnum);
321
322   *ppEnum = NULL;
323
324   if(This->nSinks == 0) return OLE_E_NOCONNECTION;
325
326   pCD = HeapAlloc(GetProcessHeap(), 0, sizeof(CONNECTDATA) * This->nSinks);
327
328   for(i = 0, nextslot = 0; i < This->maxSinks; i++) {
329     if(This->sinks[i] != NULL) {
330       pCD[nextslot].pUnk = This->sinks[i];
331       pCD[nextslot].dwCookie = i + 1;
332       nextslot++;
333     }
334   }
335   assert(nextslot == This->nSinks);
336
337   /* Bump the ref count of this object up by one.  It gets Released in
338      IEnumConnections_Release */
339   IUnknown_AddRef((IUnknown*)This);
340
341   EnumObj = EnumConnectionsImpl_Construct((IUnknown*)This, This->nSinks, pCD);
342   hr = IEnumConnections_QueryInterface((IEnumConnections*)EnumObj,
343                                   &IID_IEnumConnections, (LPVOID)ppEnum);
344   IEnumConnections_Release((IEnumConnections*)EnumObj);
345
346   HeapFree(GetProcessHeap(), 0, pCD);
347   return hr;
348 }
349
350 static const IConnectionPointVtbl ConnectionPointImpl_VTable =
351 {
352   ConnectionPointImpl_QueryInterface,
353   ConnectionPointImpl_AddRef,
354   ConnectionPointImpl_Release,
355   ConnectionPointImpl_GetConnectionInterface,
356   ConnectionPointImpl_GetConnectionPointContainer,
357   ConnectionPointImpl_Advise,
358   ConnectionPointImpl_Unadvise,
359   ConnectionPointImpl_EnumConnections
360 };
361
362
363 static const IEnumConnectionsVtbl EnumConnectionsImpl_VTable;
364 static ULONG WINAPI EnumConnectionsImpl_AddRef(IEnumConnections* iface);
365
366 /************************************************************************
367  * EnumConnectionsImpl_Construct
368  */
369 static EnumConnectionsImpl *EnumConnectionsImpl_Construct(IUnknown *pUnk,
370                                                           DWORD nSinks,
371                                                           CONNECTDATA *pCD)
372 {
373   EnumConnectionsImpl *Obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*Obj));
374   DWORD i;
375
376   Obj->lpvtbl = &EnumConnectionsImpl_VTable;
377   Obj->ref = 1;
378   Obj->pUnk = pUnk;
379   Obj->pCD = HeapAlloc(GetProcessHeap(), 0, nSinks * sizeof(CONNECTDATA));
380   Obj->nConns = nSinks;
381   Obj->nCur = 0;
382
383   for(i = 0; i < nSinks; i++) {
384     Obj->pCD[i] = pCD[i];
385     IUnknown_AddRef(Obj->pCD[i].pUnk);
386   }
387   return Obj;
388 }
389
390 /************************************************************************
391  * EnumConnectionsImpl_Destroy
392  */
393 static void EnumConnectionsImpl_Destroy(EnumConnectionsImpl *Obj)
394 {
395   DWORD i;
396
397   for(i = 0; i < Obj->nConns; i++)
398     IUnknown_Release(Obj->pCD[i].pUnk);
399
400   HeapFree(GetProcessHeap(), 0, Obj->pCD);
401   HeapFree(GetProcessHeap(), 0, Obj);
402   return;
403 }
404
405 /************************************************************************
406  * EnumConnectionsImpl_QueryInterface (IUnknown)
407  *
408  * See Windows documentation for more details on IUnknown methods.
409  */
410 static HRESULT WINAPI EnumConnectionsImpl_QueryInterface(
411   IEnumConnections*  iface,
412   REFIID  riid,
413   void**  ppvObject)
414 {
415   ConnectionPointImpl *This = (ConnectionPointImpl *)iface;
416   TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppvObject);
417
418   /*
419    * Perform a sanity check on the parameters.
420    */
421   if ( (This==0) || (ppvObject==0) )
422     return E_INVALIDARG;
423
424   /*
425    * Initialize the return parameter.
426    */
427   *ppvObject = 0;
428
429   /*
430    * Compare the riid with the interface IDs implemented by this object.
431    */
432   if (IsEqualIID(&IID_IUnknown, riid))
433     *ppvObject = This;
434   else if (IsEqualIID(&IID_IEnumConnections, riid))
435     *ppvObject = This;
436
437   /*
438    * Check that we obtained an interface.
439    */
440   if ((*ppvObject)==0)
441   {
442     FIXME("() : asking for un supported interface %s\n",debugstr_guid(riid));
443     return E_NOINTERFACE;
444   }
445
446   /*
447    * Query Interface always increases the reference count by one when it is
448    * successful
449    */
450   EnumConnectionsImpl_AddRef((IEnumConnections*)This);
451
452   return S_OK;
453 }
454
455
456 /************************************************************************
457  * EnumConnectionsImpl_AddRef (IUnknown)
458  *
459  * See Windows documentation for more details on IUnknown methods.
460  */
461 static ULONG WINAPI EnumConnectionsImpl_AddRef(IEnumConnections* iface)
462 {
463   EnumConnectionsImpl *This = (EnumConnectionsImpl *)iface;
464   ULONG refCount = InterlockedIncrement(&This->ref);
465
466   TRACE("(%p)->(ref before=%d)\n", This, refCount - 1);
467
468   IUnknown_AddRef(This->pUnk);
469   return refCount;
470 }
471
472 /************************************************************************
473  * EnumConnectionsImpl_Release (IUnknown)
474  *
475  * See Windows documentation for more details on IUnknown methods.
476  */
477 static ULONG WINAPI EnumConnectionsImpl_Release(IEnumConnections* iface)
478 {
479   EnumConnectionsImpl *This = (EnumConnectionsImpl *)iface;
480   ULONG refCount = InterlockedDecrement(&This->ref);
481
482   TRACE("(%p)->(ref before=%d)\n", This, refCount + 1);
483
484   IUnknown_Release(This->pUnk);
485
486   /*
487    * If the reference count goes down to 0, perform suicide.
488    */
489   if (!refCount) EnumConnectionsImpl_Destroy(This);
490
491   return refCount;
492 }
493
494 /************************************************************************
495  * EnumConnectionsImpl_Next (IEnumConnections)
496  *
497  */
498 static HRESULT WINAPI EnumConnectionsImpl_Next(IEnumConnections* iface,
499                                                ULONG cConn, LPCONNECTDATA pCD,
500                                                ULONG *pEnum)
501 {
502   EnumConnectionsImpl *This = (EnumConnectionsImpl *)iface;
503   DWORD nRet = 0;
504   TRACE("(%p)->(%d, %p, %p)\n", This, cConn, pCD, pEnum);
505
506   if(pEnum == NULL) {
507     if(cConn != 1)
508       return E_POINTER;
509   } else
510     *pEnum = 0;
511
512   if(This->nCur >= This->nConns)
513     return S_FALSE;
514
515   while(This->nCur < This->nConns && cConn) {
516     *pCD++ = This->pCD[This->nCur];
517     IUnknown_AddRef(This->pCD[This->nCur].pUnk);
518     This->nCur++;
519     cConn--;
520     nRet++;
521   }
522
523   if(pEnum)
524     *pEnum = nRet;
525
526   return S_OK;
527 }
528
529
530 /************************************************************************
531  * EnumConnectionsImpl_Skip (IEnumConnections)
532  *
533  */
534 static HRESULT WINAPI EnumConnectionsImpl_Skip(IEnumConnections* iface,
535                                                ULONG cSkip)
536 {
537   EnumConnectionsImpl *This = (EnumConnectionsImpl *)iface;
538   TRACE("(%p)->(%d)\n", This, cSkip);
539
540   if(This->nCur + cSkip >= This->nConns)
541     return S_FALSE;
542
543   This->nCur += cSkip;
544
545   return S_OK;
546 }
547
548
549 /************************************************************************
550  * EnumConnectionsImpl_Reset (IEnumConnections)
551  *
552  */
553 static HRESULT WINAPI EnumConnectionsImpl_Reset(IEnumConnections* iface)
554 {
555   EnumConnectionsImpl *This = (EnumConnectionsImpl *)iface;
556   TRACE("(%p)\n", This);
557
558   This->nCur = 0;
559
560   return S_OK;
561 }
562
563
564 /************************************************************************
565  * EnumConnectionsImpl_Clone (IEnumConnections)
566  *
567  */
568 static HRESULT WINAPI EnumConnectionsImpl_Clone(IEnumConnections* iface,
569                                                 LPENUMCONNECTIONS *ppEnum)
570 {
571   EnumConnectionsImpl *This = (EnumConnectionsImpl *)iface;
572   EnumConnectionsImpl *newObj;
573   TRACE("(%p)->(%p)\n", This, ppEnum);
574
575   newObj = EnumConnectionsImpl_Construct(This->pUnk, This->nConns, This->pCD);
576   newObj->nCur = This->nCur;
577   *ppEnum = (LPENUMCONNECTIONS)newObj;
578   IUnknown_AddRef(This->pUnk);
579   return S_OK;
580 }
581
582 static const IEnumConnectionsVtbl EnumConnectionsImpl_VTable =
583 {
584   EnumConnectionsImpl_QueryInterface,
585   EnumConnectionsImpl_AddRef,
586   EnumConnectionsImpl_Release,
587   EnumConnectionsImpl_Next,
588   EnumConnectionsImpl_Skip,
589   EnumConnectionsImpl_Reset,
590   EnumConnectionsImpl_Clone
591 };
592
593 /************************************************************************
594  *
595  *  The exported function to create the connection point.
596  *  NB not a windows API
597  *
598  * PARAMS
599  * pUnk [in] IUnknown of object to which the ConnectionPoint is associated.
600  *           Needed to access IConnectionPointContainer.
601  *
602  * riid [in] IID of sink interface that this ConnectionPoint manages
603  *
604  * pCP [out] returns IConnectionPoint
605  *
606  */
607 HRESULT CreateConnectionPoint(IUnknown *pUnk, REFIID riid,
608                               IConnectionPoint **pCP)
609 {
610   ConnectionPointImpl *Obj;
611   HRESULT hr;
612
613   Obj = ConnectionPointImpl_Construct(pUnk, riid);
614   if(!Obj) return E_OUTOFMEMORY;
615
616   hr = IConnectionPoint_QueryInterface((IConnectionPoint *)Obj,
617                                        &IID_IConnectionPoint, (LPVOID)pCP);
618   IConnectionPoint_Release((IConnectionPoint *)Obj);
619   return hr;
620 }