summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Runtime/InteropServices/ComEventsMethod.cs
blob: c2f56b0580d442a50bb0359d1004758cc0017d34 (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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// 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.
**
**/
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Reflection;


namespace System.Runtime.InteropServices {

    // see code:ComEventsHelper#ComEventsArchitecture
    internal class ComEventsMethod {

        // This delegate wrapper class handles dynamic invocation of delegates. The reason for the wrapper's
        // existence is that under certain circumstances we need to coerce arguments to types expected by the
        // delegates signature. Normally, reflection (Delegate.DynamicInvoke) handles types coercion
        // correctly but one known case is when the expected signature is 'ref Enum' - in this case
        // reflection by design does not do the coercion. Since we need to be compatible with COM interop
        // handling of this scenario - we are pre-processing delegate's signature by looking for 'ref enums'
        // and cache the types required for such coercion.
        internal class DelegateWrapper {
            private Delegate _d;

            private bool _once = false;
            private int _expectedParamsCount;
            private Type[] _cachedTargetTypes;

            public DelegateWrapper(Delegate d) {
                _d = d;
            }

            public Delegate Delegate {
                get { return _d; }
                set { _d = value; }
            }
        }

        #region private fields

        /// <summary>
        /// Invoking ComEventsMethod means invoking a multi-cast delegate attached to it.
        /// Since multicast delegate's built-in chaining supports only chaining instances of the same type,
        /// we need to complement this design by using an explicit linked list data structure.
        /// </summary>
        private DelegateWrapper [] _delegateWrappers;

        private int _dispid;
        private ComEventsMethod _next;

        #endregion

        
        #region ctor
        
        internal ComEventsMethod(int dispid) {
            _delegateWrappers = null;
            _dispid = dispid;
        }
        
        #endregion

        
        #region static internal methods
        
        internal static ComEventsMethod Find(ComEventsMethod methods, int dispid) {
            while (methods != null && methods._dispid != dispid) {
                methods = methods._next;
            }
            return methods;
        }

        internal static ComEventsMethod Add(ComEventsMethod methods, ComEventsMethod method) {
            method._next = methods;
            return method;
        }

        internal static ComEventsMethod Remove(ComEventsMethod methods, ComEventsMethod method) {
            if (methods == method) {
                methods = methods._next;
            } else {
                ComEventsMethod current = methods;
                while (current != null && current._next != method)
                    current = current._next;
                if (current != null)
                    current._next = method._next;
            }

            return methods;
        }

#endregion
#region public properties / methods

        internal bool Empty {
            get { return _delegateWrappers == null || _delegateWrappers.Length == 0; }
        }

        internal void AddDelegate(Delegate d) {
            int count = 0;
            if (_delegateWrappers != null) {
                count = _delegateWrappers.Length;
            }

            for (int i = 0; i < count; i++) {
                if (_delegateWrappers[i].Delegate.GetType() == d.GetType()) {
                    _delegateWrappers[i].Delegate = Delegate.Combine(_delegateWrappers[i].Delegate, d);
                    return;
                }
            }

            DelegateWrapper [] newDelegateWrappers = new DelegateWrapper[count + 1];
            if (count > 0) {
                _delegateWrappers.CopyTo(newDelegateWrappers, 0);
            }

            DelegateWrapper wrapper = new DelegateWrapper(d);
            newDelegateWrappers[count] = wrapper;

            _delegateWrappers = newDelegateWrappers;
        }

        internal void RemoveDelegate(Delegate d) {

            int count = _delegateWrappers.Length;
            int removeIdx = -1;

            for (int i = 0; i < count; i++) {
                if (_delegateWrappers[i].Delegate.GetType() == d.GetType()) {
                    removeIdx = i;
                    break;
                }
            }

            if (removeIdx < 0)
                return;

            Delegate newDelegate = Delegate.Remove(_delegateWrappers[removeIdx].Delegate, d);
            if (newDelegate != null) {
                _delegateWrappers[removeIdx].Delegate = newDelegate;
                return;
            }

            // now remove the found entry from the _delegates array

            if (count == 1) {
                _delegateWrappers = null;
                return;
            }

            DelegateWrapper [] newDelegateWrappers = new DelegateWrapper[count - 1];
            int j = 0;
            while (j < removeIdx) {
                newDelegateWrappers[j] = _delegateWrappers[j];
                j++;
            }
            while (j < count-1) {
                newDelegateWrappers[j] = _delegateWrappers[j + 1];
                j++;
            }

            _delegateWrappers = newDelegateWrappers;
        }

        #endregion
    }
}