2007-07-06 Jonathan Chambers <joncham@gmail.com>
[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
40 namespace System
41 {
42         // This is a private class that is used as a generic wrapper class
43         // for COM objects that have no specific wrapper class.
44         //
45         // It has no public methods, it's functionality is exposed trough
46         // System.Runtime.InteropServices.Marshal class and can be casted to
47         // any interface that is implemented by the wrapped COM object.
48         //
49         // This class is referenced in .NET Framework SDK Documentation so
50         // many times that obj.GetType().FullName == "System.__ComObject" and
51         // Type.GetType("System.__ComObject") may be used.
52
53         internal class __ComObject : MarshalByRefObject
54         {
55                 #region Sync with object-internals.h
56                 IntPtr iunknown;
57                 IntPtr hash_table;
58                 #endregion
59
60                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
61                 internal static extern __ComObject CreateRCW (Type t);
62
63                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
64                 private extern void ReleaseInterfaces ();
65
66                 ~__ComObject ()
67                 {
68                         ReleaseInterfaces ();
69                 }
70
71                 public __ComObject ()
72                 {
73                         Type t = GetType ();
74                         ObjectCreationDelegate ocd = ExtensibleClassFactory.GetObjectCreationCallback (t);
75                         if (ocd != null) {
76                                 iunknown = ocd (IntPtr.Zero);
77                                 if (iunknown == IntPtr.Zero)
78                                         throw new COMException (string.Format("ObjectCreationDelegate for type {0} failed to return a valid COM object", t));
79                         } else {
80                                 int hr = CoCreateInstance (GetCLSID (t), IntPtr.Zero, 0x1 | 0x4 | 0x10, IID_IUnknown, out iunknown);
81                                 Marshal.ThrowExceptionForHR (hr);
82                         }
83                 }
84
85                 internal __ComObject (Type t) {
86                         ObjectCreationDelegate ocd = ExtensibleClassFactory.GetObjectCreationCallback (t);
87                         if (ocd != null) {
88                                 iunknown = ocd (IntPtr.Zero);
89                                 if (iunknown == IntPtr.Zero)
90                                         throw new COMException (string.Format("ObjectCreationDelegate for type {0} failed to return a valid COM object", t));
91                         }
92                         else {
93                                 int hr = CoCreateInstance (GetCLSID (t), IntPtr.Zero, 0x1 | 0x4 | 0x10, IID_IUnknown, out iunknown);
94                                 Marshal.ThrowExceptionForHR (hr);
95                         }
96                 }
97
98                 internal __ComObject (IntPtr pItf)
99                 {
100                         Guid iid = IID_IUnknown;
101                         int hr = Marshal.QueryInterface (pItf, ref iid, out iunknown);
102                         Marshal.ThrowExceptionForHR (hr);
103                 }
104
105                 private static Guid GetCLSID (Type t)
106                 {
107                         if (t.IsImport)
108                                 return t.GUID;
109
110                         // look at supertypes
111                         Type super = t.BaseType;
112                         while (super != typeof (object)) {
113                                 if (super.IsImport)
114                                         return super.GUID;
115                                 super = super.BaseType;
116                         }
117                         throw new COMException ("Could not find base COM type for type " + t.ToString());
118                 }
119
120                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
121                 internal extern IntPtr GetInterfaceInternal (Type t, bool throwException);
122
123                 internal IntPtr GetInterface (Type t, bool throwException) {
124                         CheckIUnknown ();
125                         return GetInterfaceInternal (t, throwException);
126                 }
127
128                 internal IntPtr GetInterface(Type t)
129                 {
130                         return GetInterface (t, true);
131                 }
132
133                 private void CheckIUnknown ()
134                 {
135                         if (iunknown == IntPtr.Zero)
136                                 throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
137                 }
138
139                 internal IntPtr IUnknown
140                 {
141                         get
142                         {
143                                 if (iunknown == IntPtr.Zero)
144                                         throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
145                                 return iunknown;
146                         }
147                 }
148
149                 internal IntPtr IDispatch
150                 {
151                         get
152                         {
153                                 IntPtr pUnk = GetInterface (typeof (IDispatch));
154                                 if (pUnk == IntPtr.Zero)
155                                         throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
156                                 return pUnk;
157                         }
158                 }
159
160                 internal static Guid IID_IUnknown
161                 {
162                         get
163                         {
164                                 return new Guid("00000000-0000-0000-C000-000000000046");
165                         }
166                 }
167
168                 internal static Guid IID_IDispatch
169                 {
170                         get
171                         {
172                                 return new Guid ("00020400-0000-0000-C000-000000000046");
173                         }
174                 }
175
176                 public override bool Equals (object obj)
177                 {
178                         CheckIUnknown ();
179                         if (obj == null)
180                                 return false;
181
182                         __ComObject co = obj as __ComObject;
183                         if ((object)co == null)
184                                 return false;
185                         return (iunknown == co.IUnknown);
186                 }
187
188                 public override int GetHashCode ()
189                 {
190                         CheckIUnknown ();
191                         // not what MS seems to do, 
192                         // but IUnknown is identity in COM
193                         return iunknown.ToInt32 ();
194                 }
195
196                 [DllImport ("ole32.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, PreserveSig = true)]
197                 static extern int CoCreateInstance (
198                    [In, MarshalAs (UnmanagedType.LPStruct)] Guid rclsid,
199                    IntPtr pUnkOuter,
200                    uint dwClsContext,
201                   [In, MarshalAs (UnmanagedType.LPStruct)] Guid riid,
202                         out IntPtr pUnk);
203         }
204 }