summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/__ComObject.cs
blob: 86800a51f89197dc5b4429fb0f884c9de07c70da (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
179
180
181
182
183
184
185
186
187
188
189
190
// 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.

/*============================================================
**
**
**
** __ComObject is the root class for all COM wrappers.  This class
** defines only the basics. This class is used for wrapping COM objects
** accessed from COM+
**
** 
===========================================================*/
namespace System
{
    using System;
    using System.Collections;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Runtime.InteropServices.WindowsRuntime;
    using System.Runtime.CompilerServices;
    using System.Reflection;

    internal class __ComObject : MarshalByRefObject
    {
        private Hashtable m_ObjectToDataMap;

        /*============================================================
        ** default constructor
        ** can't instantiate this directly
        =============================================================*/
        protected __ComObject ()
        {
        }

        //====================================================================
        // Overrides ToString() to make sure we call to IStringable if the 
        // COM object implements it in the case of weakly typed RCWs
        //====================================================================
        public override string ToString()
        {
            //
            // Only do the IStringable cast when running under AppX for better compat
            // Otherwise we could do a IStringable cast in classic apps which could introduce
            // a thread transition which would lead to deadlock
            //
            if (AppDomain.IsAppXModel())
            {
                // Check whether the type implements IStringable.
                IStringable stringableType = this as IStringable;
                if (stringableType != null)
                {
                    return stringableType.ToString();
                }                   
            }
                
            return base.ToString();
        }

        //====================================================================
        // This method retrieves the data associated with the specified
        // key if any such data exists for the current __ComObject.
        //====================================================================
        internal Object GetData(Object key)
        {
            Object data = null;

            // Synchronize access to the map.
            lock(this)
            {
                // If the map hasn't been allocated, then there can be no data for the specified key.
                if (m_ObjectToDataMap != null)
                {
                    // Look up the data in the map.
                    data = m_ObjectToDataMap[key];
                }
            }

            return data;
        }
        
        //====================================================================
        // This method sets the data for the specified key on the current 
        // __ComObject.
        //====================================================================
        internal bool SetData(Object key, Object data)
        {
            bool bAdded = false;

            // Synchronize access to the map.
            lock(this)
            {
                // If the map hasn't been allocated yet, allocate it.
                if (m_ObjectToDataMap == null)
                    m_ObjectToDataMap = new Hashtable();

                // If there isn't already data in the map then add it.
                if (m_ObjectToDataMap[key] == null)
                {
                    m_ObjectToDataMap[key] = data;
                    bAdded = true;
                }
            }

            return bAdded;
        }

        //====================================================================
        // This method is called from within the EE and releases all the 
        // cached data for the __ComObject.
        //====================================================================
        internal void ReleaseAllData()
        {
            // Synchronize access to the map.
            lock(this)
            {

                // If the map hasn't been allocated, then there is nothing to do.
                if (m_ObjectToDataMap != null)
                {
                    foreach (Object o in m_ObjectToDataMap.Values)
                    {
                        // Note: the value could be an object[]
                        // We are fine for now as object[] doesn't implement IDisposable nor derive from __ComObject
                        
                        // If the object implements IDisposable, then call Dispose on it.
                        IDisposable DisposableObj = o as IDisposable;
                        if (DisposableObj != null)
                            DisposableObj.Dispose();

                        // If the object is a derived from __ComObject, then call Marshal.ReleaseComObject on it.
                        __ComObject ComObj = o as __ComObject;
                        if (ComObj != null)
                            Marshal.ReleaseComObject(ComObj);
                    }

                    // Set the map to null to indicate it has been cleaned up.
                    m_ObjectToDataMap = null;
                }
            }
        }

        //====================================================================
        // This method is called from within the EE and is used to handle
        // calls on methods of event interfaces.
        //====================================================================
        internal Object GetEventProvider(RuntimeType t)
        {
            // Check to see if we already have a cached event provider for this type.
            Object EvProvider = GetData(t);

            // If we don't then we need to create one.
            if (EvProvider == null)
                EvProvider = CreateEventProvider(t);

            return EvProvider;
        }

        internal int ReleaseSelf()
        {
            return Marshal.InternalReleaseComObject(this);
        }

        internal void FinalReleaseSelf()
        {
            Marshal.InternalFinalReleaseComObject(this);
        }

        private Object CreateEventProvider(RuntimeType t)
        {
            // Create the event provider for the specified type.
            Object EvProvider = Activator.CreateInstance(t, Activator.ConstructorDefault | BindingFlags.NonPublic, null, new Object[]{this}, null);

            // Attempt to cache the wrapper on the object.
            if (!SetData(t, EvProvider))
            {
                // Dispose the event provider if it implements IDisposable.
                IDisposable DisposableEvProv = EvProvider as IDisposable;
                if (DisposableEvProv != null)
                    DisposableEvProv.Dispose();

                // Another thead already cached the wrapper so use that one instead.
                EvProvider = GetData(t);
            }

            return EvProvider;
        }
    }
}