summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Runtime/InteropServices/ComEventsSink.cs
blob: f2b22e3ceb91ec5092a5e924ecf4a862a6c980fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


/*============================================================
**
**
** Purpose: part of ComEventHelpers APIs which allow binding 
** managed delegates to COM's connection point based events.
**
**/

namespace System.Runtime.InteropServices {
    using System;
    using System.Diagnostics;

    // see code:ComEventsHelper#ComEventsArchitecture
    internal class ComEventsSink : ICustomQueryInterface
    {
#region private fields

        private Guid _iidSourceItf;
        private ComTypes.IConnectionPoint _connectionPoint;
        private int _cookie;
        private ComEventsMethod _methods;
        private ComEventsSink _next;

#endregion  

        
#region ctor

        internal ComEventsSink(object rcw, Guid iid) {
            _iidSourceItf = iid;
            this.Advise(rcw);
        } 

#endregion

        
#region static members

        internal static ComEventsSink Find(ComEventsSink sinks, ref Guid iid) {

            ComEventsSink sink = sinks;
            while (sink != null && sink._iidSourceItf != iid) {
                sink = sink._next;
            }

            return sink;
        }

        internal static ComEventsSink Add(ComEventsSink sinks, ComEventsSink sink) {
            sink._next = sinks;
            return sink;
        }

        internal static ComEventsSink RemoveAll(ComEventsSink sinks) {
            while (sinks != null) {
                sinks.Unadvise();
                sinks = sinks._next;
            }

            return null;
        }

        internal static ComEventsSink Remove(ComEventsSink sinks, ComEventsSink sink) {
            BCLDebug.Assert(sinks != null, "removing event sink from empty sinks collection");
            BCLDebug.Assert(sink != null, "specify event sink is null");

            if (sink == sinks) {
                sinks = sinks._next;
            } else {
                ComEventsSink current = sinks;
                while (current != null && current._next != sink)
                    current = current._next;

                if (current != null) {
                    current._next = sink._next;
                }
            }

            sink.Unadvise();

            return sinks;
        } 

#endregion


#region public methods

        public ComEventsMethod RemoveMethod(ComEventsMethod method) {
            _methods = ComEventsMethod.Remove(_methods, method);
            return _methods;
        }

        public ComEventsMethod FindMethod(int dispid) {
            return ComEventsMethod.Find(_methods, dispid);
        }

        public ComEventsMethod AddMethod(int dispid) {
            ComEventsMethod method = new ComEventsMethod(dispid);
            _methods = ComEventsMethod.Add(_methods, method);
            return method;
        } 

#endregion

        static Guid IID_IManagedObject = new Guid("{C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4}");

        CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) {
            ppv = IntPtr.Zero;
            if (iid == this._iidSourceItf || iid == typeof(NativeMethods.IDispatch).GUID) {
                ppv = Marshal.GetComInterfaceForObject(this, typeof(NativeMethods.IDispatch), CustomQueryInterfaceMode.Ignore);
                return CustomQueryInterfaceResult.Handled;
            }
            else if (iid == IID_IManagedObject)
            {
                return CustomQueryInterfaceResult.Failed;
            }

            return CustomQueryInterfaceResult.NotHandled;
        }

#region private methods


        private void Advise(object rcw) {
            BCLDebug.Assert(_connectionPoint == null, "comevent sink is already advised");

            ComTypes.IConnectionPointContainer cpc = (ComTypes.IConnectionPointContainer)rcw;
            ComTypes.IConnectionPoint cp;
            cpc.FindConnectionPoint(ref _iidSourceItf, out cp);

            object sinkObject = this;

            cp.Advise(sinkObject, out _cookie);

            _connectionPoint = cp;
        }

        private void Unadvise() {
            BCLDebug.Assert(_connectionPoint != null, "can not unadvise from empty connection point");

            try {
                _connectionPoint.Unadvise(_cookie);
                Marshal.ReleaseComObject(_connectionPoint);
            } catch (System.Exception) {
                // swallow all exceptions on unadvise
                // the host may not be available at this point
            } finally {
                _connectionPoint = null;
            }

        } 

#endregion
    };
}