Port CustomMarshalers.dll to C# and .NET Core (#21343)
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / src / System / Runtime / InteropServices / ComEventsSink.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5
6 /*============================================================
7 **
8 **
9 ** Purpose: part of ComEventHelpers APIs which allow binding 
10 ** managed delegates to COM's connection point based events.
11 **
12 **/
13
14 using System;
15 using System.Diagnostics;
16
17 namespace System.Runtime.InteropServices
18 {
19     // see code:ComEventsHelper#ComEventsArchitecture
20     internal class ComEventsSink : ICustomQueryInterface
21     {
22         #region private fields
23
24         private Guid _iidSourceItf;
25         private ComTypes.IConnectionPoint _connectionPoint;
26         private int _cookie;
27         private ComEventsMethod _methods;
28         private ComEventsSink _next;
29
30         #endregion
31
32
33         #region ctor
34
35         internal ComEventsSink(object rcw, Guid iid)
36         {
37             _iidSourceItf = iid;
38             this.Advise(rcw);
39         }
40
41         #endregion
42
43
44         #region static members
45
46         internal static ComEventsSink Find(ComEventsSink sinks, ref Guid iid)
47         {
48             ComEventsSink sink = sinks;
49             while (sink != null && sink._iidSourceItf != iid)
50             {
51                 sink = sink._next;
52             }
53
54             return sink;
55         }
56
57         internal static ComEventsSink Add(ComEventsSink sinks, ComEventsSink sink)
58         {
59             sink._next = sinks;
60             return sink;
61         }
62
63         internal static ComEventsSink RemoveAll(ComEventsSink sinks)
64         {
65             while (sinks != null)
66             {
67                 sinks.Unadvise();
68                 sinks = sinks._next;
69             }
70
71             return null;
72         }
73
74         internal static ComEventsSink Remove(ComEventsSink sinks, ComEventsSink sink)
75         {
76             Debug.Assert(sinks != null, "removing event sink from empty sinks collection");
77             Debug.Assert(sink != null, "specify event sink is null");
78
79             if (sink == sinks)
80             {
81                 sinks = sinks._next;
82             }
83             else
84             {
85                 ComEventsSink current = sinks;
86                 while (current != null && current._next != sink)
87                     current = current._next;
88
89                 if (current != null)
90                 {
91                     current._next = sink._next;
92                 }
93             }
94
95             sink.Unadvise();
96
97             return sinks;
98         }
99
100         #endregion
101
102
103         #region public methods
104
105         public ComEventsMethod RemoveMethod(ComEventsMethod method)
106         {
107             _methods = ComEventsMethod.Remove(_methods, method);
108             return _methods;
109         }
110
111         public ComEventsMethod FindMethod(int dispid)
112         {
113             return ComEventsMethod.Find(_methods, dispid);
114         }
115
116         public ComEventsMethod AddMethod(int dispid)
117         {
118             ComEventsMethod method = new ComEventsMethod(dispid);
119             _methods = ComEventsMethod.Add(_methods, method);
120             return method;
121         }
122
123         #endregion
124
125         private static Guid IID_IManagedObject = new Guid("{C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4}");
126
127         CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv)
128         {
129             ppv = IntPtr.Zero;
130             if (iid == _iidSourceItf || iid == typeof(IDispatch).GUID)
131             {
132                 ppv = Marshal.GetComInterfaceForObject(this, typeof(IDispatch), CustomQueryInterfaceMode.Ignore);
133                 return CustomQueryInterfaceResult.Handled;
134             }
135             else if (iid == IID_IManagedObject)
136             {
137                 return CustomQueryInterfaceResult.Failed;
138             }
139
140             return CustomQueryInterfaceResult.NotHandled;
141         }
142
143         #region private methods
144
145
146         private void Advise(object rcw)
147         {
148             Debug.Assert(_connectionPoint == null, "comevent sink is already advised");
149
150             ComTypes.IConnectionPointContainer cpc = (ComTypes.IConnectionPointContainer)rcw;
151             ComTypes.IConnectionPoint cp;
152             cpc.FindConnectionPoint(ref _iidSourceItf, out cp);
153
154             object sinkObject = this;
155
156             cp.Advise(sinkObject, out _cookie);
157
158             _connectionPoint = cp;
159         }
160
161         private void Unadvise()
162         {
163             Debug.Assert(_connectionPoint != null, "can not unadvise from empty connection point");
164
165             try
166             {
167                 _connectionPoint.Unadvise(_cookie);
168                 Marshal.ReleaseComObject(_connectionPoint);
169             }
170             catch (System.Exception)
171             {
172                 // swallow all exceptions on unadvise
173                 // the host may not be available at this point
174             }
175             finally
176             {
177                 _connectionPoint = null;
178             }
179         }
180
181         #endregion
182     };
183 }