2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / ComObject.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Microsoft Public License. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Microsoft Public License, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Microsoft Public License.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 #if !SILVERLIGHT // ComObject
19
20 using System.Collections.Generic;
21 using System.Diagnostics;
22 #if CODEPLEX_40
23 using System.Linq.Expressions;
24 #else
25 using Microsoft.Linq.Expressions;
26 #endif
27 using System.Reflection;
28 using System.Runtime.InteropServices;
29 using System.Security;
30 using System.Security.Permissions;
31
32 #if CODEPLEX_40
33 namespace System.Dynamic {
34 #else
35 namespace Microsoft.Scripting {
36 #endif
37     /// <summary>
38     /// This is a helper class for runtime-callable-wrappers of COM instances. We create one instance of this type
39     /// for every generic RCW instance.
40     /// </summary>
41     internal class ComObject : IDynamicMetaObjectProvider {
42         /// <summary>
43         /// The runtime-callable wrapper
44         /// </summary>
45         private readonly object _rcw;
46
47         internal ComObject(object rcw) {
48             Debug.Assert(ComObject.IsComObject(rcw));
49             _rcw = rcw;
50         }
51
52         internal object RuntimeCallableWrapper {
53             get {
54                 return _rcw;
55             }
56         }
57
58         private readonly static object _ComObjectInfoKey = new object();
59
60         /// <summary>
61         /// This is the factory method to get the ComObject corresponding to an RCW
62         /// </summary>
63         /// <returns></returns>
64 #if MICROSOFT_DYNAMIC
65         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
66 #endif
67         [SecurityCritical]
68         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
69         public static ComObject ObjectToComObject(object rcw) {
70             Debug.Assert(ComObject.IsComObject(rcw));
71
72             // Marshal.Get/SetComObjectData has a LinkDemand for UnmanagedCode which will turn into
73             // a full demand. We could avoid this by making this method SecurityCritical
74             object data = Marshal.GetComObjectData(rcw, _ComObjectInfoKey);
75             if (data != null) {
76                 return (ComObject)data;
77             }
78
79             lock (_ComObjectInfoKey) {
80                 data = Marshal.GetComObjectData(rcw, _ComObjectInfoKey);
81                 if (data != null) {
82                     return (ComObject)data;
83                 }
84
85                 ComObject comObjectInfo = CreateComObject(rcw);
86                 if (!Marshal.SetComObjectData(rcw, _ComObjectInfoKey, comObjectInfo)) {
87                     throw Error.SetComObjectDataFailed();
88                 }
89
90                 return comObjectInfo;
91             }
92         }
93
94         // Expression that unwraps ComObject
95         internal static MemberExpression RcwFromComObject(Expression comObject) {
96             Debug.Assert(comObject != null && typeof(ComObject).IsAssignableFrom(comObject.Type), "must be ComObject");
97
98             return Expression.Property(
99                 Helpers.Convert(comObject, typeof(ComObject)),
100                 typeof(ComObject).GetProperty("RuntimeCallableWrapper", BindingFlags.NonPublic | BindingFlags.Instance)
101             );
102         }
103
104         // Expression that finds or creates a ComObject that corresponds to given Rcw
105         internal static MethodCallExpression RcwToComObject(Expression rcw) {
106             return Expression.Call(
107                 typeof(ComObject).GetMethod("ObjectToComObject"),
108                 Helpers.Convert(rcw, typeof(object))
109             );
110         }
111
112         private static ComObject CreateComObject(object rcw) {
113             IDispatch dispatchObject = rcw as IDispatch;
114             if (dispatchObject != null) {
115                 // We can do method invocations on IDispatch objects
116                 return new IDispatchComObject(dispatchObject);
117             }
118
119             // There is not much we can do in this case
120             return new ComObject(rcw);
121         }
122
123         internal virtual IList<string> GetMemberNames(bool dataOnly) {
124             return new string[0];
125         }
126
127         internal virtual IList<KeyValuePair<string, object>> GetMembers(IEnumerable<string> names) {
128             return new KeyValuePair<string, object>[0];
129         }
130
131         DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) {
132             return new ComFallbackMetaObject(parameter, BindingRestrictions.Empty, this);
133         }
134
135         private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject");
136
137         internal static bool IsComObject(object obj) {
138             // we can't use System.Runtime.InteropServices.Marshal.IsComObject(obj) since it doesn't work in partial trust
139             return obj != null && ComObjectType.IsAssignableFrom(obj.GetType());
140         }
141
142     }
143 }
144
145 #endif