ef583813eb
Former-commit-id: 943baa9f16a098c33e129777827f3a9d20da00d6
268 lines
7.3 KiB
C#
268 lines
7.3 KiB
C#
//
|
|
// System.__ComObject
|
|
//
|
|
// 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
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// 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
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
#if !FULL_AOT_RUNTIME && !DISABLE_REMOTING
|
|
using Mono.Interop;
|
|
using System.Collections;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
|
|
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.
|
|
|
|
[StructLayout (LayoutKind.Sequential)]
|
|
internal class __ComObject : MarshalByRefObject
|
|
{
|
|
#pragma warning disable 169
|
|
#region Sync with object-internals.h
|
|
IntPtr iunknown;
|
|
IntPtr hash_table;
|
|
SynchronizationContext synchronization_context;
|
|
#endregion
|
|
#pragma warning restore 169
|
|
|
|
// keep a reference to the proxy so it doesn't get garbage collected before the RCW
|
|
ComInteropProxy proxy;
|
|
|
|
[MethodImplAttribute (MethodImplOptions.InternalCall)]
|
|
internal static extern __ComObject CreateRCW (Type t);
|
|
|
|
[MethodImplAttribute (MethodImplOptions.InternalCall)]
|
|
private extern void ReleaseInterfaces ();
|
|
|
|
~__ComObject ()
|
|
{
|
|
if (hash_table != IntPtr.Zero) {
|
|
if (synchronization_context != null)
|
|
synchronization_context.Post ((state) => ReleaseInterfaces (), this);
|
|
else
|
|
ReleaseInterfaces ();
|
|
}
|
|
proxy = null;
|
|
}
|
|
|
|
public __ComObject ()
|
|
{
|
|
Initialize (GetType ());
|
|
}
|
|
|
|
internal __ComObject (Type t) {
|
|
Initialize (t);
|
|
}
|
|
|
|
internal __ComObject (IntPtr pItf, ComInteropProxy p)
|
|
{
|
|
proxy = p;
|
|
InitializeApartmentDetails ();
|
|
Guid iid = IID_IUnknown;
|
|
int hr = Marshal.QueryInterface (pItf, ref iid, out iunknown);
|
|
Marshal.ThrowExceptionForHR (hr);
|
|
}
|
|
|
|
internal void Initialize (IntPtr pUnk, ComInteropProxy p)
|
|
{
|
|
proxy = p;
|
|
InitializeApartmentDetails ();
|
|
iunknown = pUnk;
|
|
}
|
|
|
|
internal void Initialize (Type t)
|
|
{
|
|
InitializeApartmentDetails ();
|
|
// Guard multiple invocation.
|
|
if (iunknown != IntPtr.Zero)
|
|
return;
|
|
|
|
iunknown = CreateIUnknown (t);
|
|
}
|
|
|
|
internal static IntPtr CreateIUnknown(Type t)
|
|
{
|
|
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (t.TypeHandle);
|
|
|
|
IntPtr iunknown;
|
|
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);
|
|
}
|
|
|
|
return iunknown;
|
|
}
|
|
|
|
private void InitializeApartmentDetails ()
|
|
{
|
|
// Only synchronization_context if thread is STA.
|
|
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
|
|
return;
|
|
|
|
synchronization_context = SynchronizationContext.Current;
|
|
|
|
// Check whether the current context is a plain SynchronizationContext object
|
|
// and handle this as if no context was set at all.
|
|
if (synchronization_context != null &&
|
|
synchronization_context.GetType () == typeof(SynchronizationContext))
|
|
synchronization_context = null;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
#else
|
|
namespace System
|
|
{
|
|
// this is a shim class so we can AOT during full AOT builds without --enable-minimal=com
|
|
internal class __ComObject
|
|
{
|
|
__ComObject ()
|
|
{
|
|
throw new NotSupportedException ();
|
|
}
|
|
}
|
|
}
|
|
#endif
|