2007-07-06 Jonathan Chambers <joncham@gmail.com>
[mono.git] / mcs / class / corlib / System / __ComObject.cs
index 1b01c99e1b98694e52e7c8735af1ea14de564104..acfd093db968365cbec830b1e400fd07465fda5d 100644 (file)
@@ -1,10 +1,16 @@
 //
 // System.__ComObject
 //
-// Author:
-//     Sebastien Pouliot  <sebastien@ximian.com>
+// Authors:
+//   Sebastien Pouliot <sebastien@ximian.com>
+//   Kornél Pál <http://www.kornelpal.hu/>
+//   Jonathan Chambers <joncham@gmail.com>
 //
 // Copyright (C) 2004 Novell (http://www.novell.com)
+// Copyright (C) 2005 Kornél Pál
+// Copyright (C) 2006 Jonathan Chambers
+//
+
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // distribute, sublicense, and/or sell copies of the Software, and to
 // permit persons to whom the Software is furnished to do so, subject to
 // the following conditions:
-// 
+//
 // The above copyright notice and this permission notice shall be
 // included in all copies or substantial portions of the Software.
-// 
+//
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-namespace System {
+using Mono.Interop;
+using System.Collections;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+       // This is a private class that is used as a generic wrapper class
+       // for COM objects that have no specific wrapper class.
+       //
+       // It has no public methods, it's functionality is exposed trough
+       // System.Runtime.InteropServices.Marshal class and can be casted to
+       // any interface that is implemented by the wrapped COM object.
+       //
+       // This class is referenced in .NET Framework SDK Documentation so
+       // many times that obj.GetType().FullName == "System.__ComObject" and
+       // Type.GetType("System.__ComObject") may be used.
+
+       internal class __ComObject : MarshalByRefObject
+       {
+               #region Sync with object-internals.h
+               IntPtr iunknown;
+               IntPtr hash_table;
+               #endregion
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal static extern __ComObject CreateRCW (Type t);
 
-       [CLSCompliant (false)]
-       [MonoTODO ("Is there someone in there ?")]
-       public class __ComObject : MarshalByRefObject {
-       
-               private __ComObject ()
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern void ReleaseInterfaces ();
+
+               ~__ComObject ()
+               {
+                       ReleaseInterfaces ();
+               }
+
+               public __ComObject ()
                {
+                       Type t = GetType ();
+                       ObjectCreationDelegate ocd = ExtensibleClassFactory.GetObjectCreationCallback (t);
+                       if (ocd != null) {
+                               iunknown = ocd (IntPtr.Zero);
+                               if (iunknown == IntPtr.Zero)
+                                       throw new COMException (string.Format("ObjectCreationDelegate for type {0} failed to return a valid COM object", t));
+                       } else {
+                               int hr = CoCreateInstance (GetCLSID (t), IntPtr.Zero, 0x1 | 0x4 | 0x10, IID_IUnknown, out iunknown);
+                               Marshal.ThrowExceptionForHR (hr);
+                       }
+               }
+
+               internal __ComObject (Type t) {
+                       ObjectCreationDelegate ocd = ExtensibleClassFactory.GetObjectCreationCallback (t);
+                       if (ocd != null) {
+                               iunknown = ocd (IntPtr.Zero);
+                               if (iunknown == IntPtr.Zero)
+                                       throw new COMException (string.Format("ObjectCreationDelegate for type {0} failed to return a valid COM object", t));
+                       }
+                       else {
+                               int hr = CoCreateInstance (GetCLSID (t), IntPtr.Zero, 0x1 | 0x4 | 0x10, IID_IUnknown, out iunknown);
+                               Marshal.ThrowExceptionForHR (hr);
+                       }
                }
+
+               internal __ComObject (IntPtr pItf)
+               {
+                       Guid iid = IID_IUnknown;
+                       int hr = Marshal.QueryInterface (pItf, ref iid, out iunknown);
+                       Marshal.ThrowExceptionForHR (hr);
+               }
+
+               private static Guid GetCLSID (Type t)
+               {
+                       if (t.IsImport)
+                               return t.GUID;
+
+                       // look at supertypes
+                       Type super = t.BaseType;
+                       while (super != typeof (object)) {
+                               if (super.IsImport)
+                                       return super.GUID;
+                               super = super.BaseType;
+                       }
+                       throw new COMException ("Could not find base COM type for type " + t.ToString());
+               }
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern IntPtr GetInterfaceInternal (Type t, bool throwException);
+
+               internal IntPtr GetInterface (Type t, bool throwException) {
+                       CheckIUnknown ();
+                       return GetInterfaceInternal (t, throwException);
+               }
+
+               internal IntPtr GetInterface(Type t)
+               {
+                       return GetInterface (t, true);
+               }
+
+               private void CheckIUnknown ()
+               {
+                       if (iunknown == IntPtr.Zero)
+                               throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
+               }
+
+               internal IntPtr IUnknown
+               {
+                       get
+                       {
+                               if (iunknown == IntPtr.Zero)
+                                       throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
+                               return iunknown;
+                       }
+               }
+
+               internal IntPtr IDispatch
+               {
+                       get
+                       {
+                               IntPtr pUnk = GetInterface (typeof (IDispatch));
+                               if (pUnk == IntPtr.Zero)
+                                       throw new InvalidComObjectException ("COM object that has been separated from its underlying RCW cannot be used.");
+                               return pUnk;
+                       }
+               }
+
+               internal static Guid IID_IUnknown
+               {
+                       get
+                       {
+                               return new Guid("00000000-0000-0000-C000-000000000046");
+                       }
+               }
+
+               internal static Guid IID_IDispatch
+               {
+                       get
+                       {
+                               return new Guid ("00020400-0000-0000-C000-000000000046");
+                       }
+               }
+
+               public override bool Equals (object obj)
+               {
+                       CheckIUnknown ();
+                       if (obj == null)
+                               return false;
+
+                       __ComObject co = obj as __ComObject;
+                       if ((object)co == null)
+                               return false;
+                       return (iunknown == co.IUnknown);
+               }
+
+               public override int GetHashCode ()
+               {
+                       CheckIUnknown ();
+                       // not what MS seems to do, 
+                       // but IUnknown is identity in COM
+                       return iunknown.ToInt32 ();
+               }
+
+               [DllImport ("ole32.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, PreserveSig = true)]
+               static extern int CoCreateInstance (
+                  [In, MarshalAs (UnmanagedType.LPStruct)] Guid rclsid,
+                  IntPtr pUnkOuter,
+                  uint dwClsContext,
+                 [In, MarshalAs (UnmanagedType.LPStruct)] Guid riid,
+                       out IntPtr pUnk);
        }
 }