do not check order sequence if option /order was not used
[mono.git] / mcs / class / corlib / System / __ComObject.cs
1 //
2 // System.__ComObject
3 //
4 // Authors:
5 //   Sebastien Pouliot <sebastien@ximian.com>
6 //   Kornél Pál <http://www.kornelpal.hu/>
7 //   Jonathan Chambers <joncham@gmail.com>
8 //
9 // Copyright (C) 2004 Novell (http://www.novell.com)
10 // Copyright (C) 2005 Kornél Pál
11 // Copyright (C) 2006 Jonathan Chambers
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using Mono.Interop;
36 using System.Collections;
37 using System.Runtime.InteropServices;
38 using System.Runtime.CompilerServices;
39 using System.Threading;
40
41 namespace System
42 {
43         // This is a private class that is used as a generic wrapper class
44         // for COM objects that have no specific wrapper class.
45         //
46         // It has no public methods, it's functionality is exposed trough
47         // System.Runtime.InteropServices.Marshal class and can be casted to
48         // any interface that is implemented by the wrapped COM object.
49         //
50         // This class is referenced in .NET Framework SDK Documentation so
51         // many times that obj.GetType().FullName == "System.__ComObject" and
52         // Type.GetType("System.__ComObject") may be used.
53
54         [StructLayout (LayoutKind.Sequential)]
55         internal class __ComObject : MarshalByRefObject
56         {
57 #pragma warning disable 169     
58                 #region Sync with object-internals.h
59                 IntPtr iunknown;
60                 IntPtr hash_table;
61                 SynchronizationContext synchronization_context;
62                 #endregion
63 #pragma warning restore 169
64
65                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
66                 internal static extern __ComObject CreateRCW (Type t);
67
68                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
69                 private extern void ReleaseInterfaces ();
70
71                 ~__ComObject ()
72                 {       
73                         if (synchronization_context != null)
74                                 synchronization_context.Post ((state) => ReleaseInterfaces (), this);
75                         else
76                                 ReleaseInterfaces ();                           
77                 }
78
79                 public __ComObject ()
80                 {
81                         Initialize (GetType ());
82                 }
83
84                 internal __ComObject (Type t) {
85                         Initialize (t);
86                 }
87
88                 internal __ComObject (IntPtr pItf)
89                 {
90                         InitializeApartmentDetails ();
91                         Guid iid = IID_IUnknown;
92                         int hr = Marshal.QueryInterface (pItf, ref iid, out iunknown);
93                         Marshal.ThrowExceptionForHR (hr);
94                 }
95
96                 internal void Initialize (Type t)
97                 {
98                         InitializeApartmentDetails ();
99                         // Guard multiple invocation.
100                         if (iunknown != IntPtr.Zero)
101                                 return;
102
103                         System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (t.TypeHandle);
104                         
105                         ObjectCreationDelegate ocd = ExtensibleClassFactory.GetObjectCreationCallback (t);
106                         if (ocd != null) {
107                                 iunknown = ocd (IntPtr.Zero);
108                                 if (iunknown == IntPtr.Zero)
109                                         throw new COMException (string.Format("ObjectCreationDelegate for type {0} failed to return a valid COM object", t));
110                         }
111                         else {
112                                 int hr = CoCreateInstance (GetCLSID (t), IntPtr.Zero, 0x1 | 0x4 | 0x10, IID_IUnknown, out iunknown);
113                                 Marshal.ThrowExceptionForHR (hr);
114                         }
115                 }
116
117                 private void InitializeApartmentDetails ()
118                 {
119                         // Only synchronization_context if thread is STA.
120                         if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
121                                 return;
122                         
123                         synchronization_context = SynchronizationContext.Current;
124
125                         // Check whether the current context is a plain SynchronizationContext object
126                         // and handle this as if no context was set at all.
127                         if (synchronization_context != null &&
128                                 synchronization_context.GetType () == typeof(SynchronizationContext))
129                                 synchronization_context = null;                 
130                 }
131
132                 private static Guid GetCLSID (Type t)
133                 {
134                         if (t.IsImport)
135                                 return t.GUID;
136
137                         // look at supertypes
138                         Type super = t.BaseType;
139                         while (super != typeof (object)) {
140                                 if (super.IsImport)
141                                         return super.GUID;
142                                 super = super.BaseType;
143                         }
144                         throw new COMException ("Could not find base COM type for type " + t.ToString());
145                 }
146
147                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
148                 internal extern IntPtr GetInterfaceInternal (Type t, bool throwException);
149
150                 internal IntPtr GetInterface (Type t, bool throwException) {
151                         CheckIUnknown ();
152                         return GetInterfaceInternal (t, throwException);
153                 }
154
155                 internal IntPtr GetInterface(Type t)
156                 {
157                         return GetInterface (t, true);
158                 }
159
160                 private void CheckIUnknown ()
161                 {
162                         if (iunknown == IntPtr.Zero)
163                                 throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
164                 }
165
166                 internal IntPtr IUnknown
167                 {
168                         get
169                         {
170                                 if (iunknown == IntPtr.Zero)
171                                         throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
172                                 return iunknown;
173                         }
174                 }
175
176                 internal IntPtr IDispatch
177                 {
178                         get
179                         {
180                                 IntPtr pUnk = GetInterface (typeof (IDispatch));
181                                 if (pUnk == IntPtr.Zero)
182                                         throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
183                                 return pUnk;
184                         }
185                 }
186
187                 internal static Guid IID_IUnknown
188                 {
189                         get
190                         {
191                                 return new Guid("00000000-0000-0000-C000-000000000046");
192                         }
193                 }
194
195                 internal static Guid IID_IDispatch
196                 {
197                         get
198                         {
199                                 return new Guid ("00020400-0000-0000-C000-000000000046");
200                         }
201                 }
202
203                 public override bool Equals (object obj)
204                 {
205                         CheckIUnknown ();
206                         if (obj == null)
207                                 return false;
208
209                         __ComObject co = obj as __ComObject;
210                         if ((object)co == null)
211                                 return false;
212                         return (iunknown == co.IUnknown);
213                 }
214
215                 public override int GetHashCode ()
216                 {
217                         CheckIUnknown ();
218                         // not what MS seems to do, 
219                         // but IUnknown is identity in COM
220                         return iunknown.ToInt32 ();
221                 }
222
223                 [DllImport ("ole32.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, PreserveSig = true)]
224                 static extern int CoCreateInstance (
225                    [In, MarshalAs (UnmanagedType.LPStruct)] Guid rclsid,
226                    IntPtr pUnkOuter,
227                    uint dwClsContext,
228                   [In, MarshalAs (UnmanagedType.LPStruct)] Guid riid,
229                         out IntPtr pUnk);
230         }
231 }