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.Diagnostics; 21 #if CODEPLEX_40 22 using System.Linq.Expressions; 23 #else 24 using Microsoft.Linq.Expressions; 25 #endif 26 using System.Runtime.InteropServices; 27 28 #if CODEPLEX_40 29 namespace System.Dynamic { 30 #else 31 namespace Microsoft.Scripting { 32 #endif 33 34 /// <summary> 35 /// VariantBuilder handles packaging of arguments into a Variant for a call to IDispatch.Invoke 36 /// </summary> 37 internal class VariantBuilder { 38 39 private MemberExpression _variant; 40 private readonly ArgBuilder _argBuilder; 41 private readonly VarEnum _targetComType; 42 internal ParameterExpression TempVariable { get; private set; } 43 VariantBuilder(VarEnum targetComType, ArgBuilder builder)44 internal VariantBuilder(VarEnum targetComType, ArgBuilder builder) { 45 _targetComType = targetComType; 46 _argBuilder = builder; 47 } 48 49 internal bool IsByRef { 50 get { return (_targetComType & VarEnum.VT_BYREF) != 0; } 51 } 52 InitializeArgumentVariant(MemberExpression variant, Expression parameter)53 internal Expression InitializeArgumentVariant(MemberExpression variant, Expression parameter) { 54 //NOTE: we must remember our variant 55 //the reason is that argument order does not map exactly to the order of variants for invoke 56 //and when we are doing clean-up we must be sure we are cleaning the variant we have initialized. 57 58 _variant = variant; 59 60 if (IsByRef) { 61 // temp = argument 62 // paramVariants._elementN.SetAsByrefT(ref temp) 63 Debug.Assert(TempVariable == null); 64 var argExpr = _argBuilder.MarshalToRef(parameter); 65 66 TempVariable = Expression.Variable(argExpr.Type, null); 67 return Expression.Block( 68 Expression.Assign(TempVariable, argExpr), 69 Expression.Call( 70 variant, 71 Variant.GetByrefSetter(_targetComType & ~VarEnum.VT_BYREF), 72 TempVariable 73 ) 74 ); 75 } 76 77 Expression argument = _argBuilder.Marshal(parameter); 78 79 // we are forced to special case ConvertibleArgBuilder since it does not have 80 // a corresponding _targetComType. 81 if (_argBuilder is ConvertibleArgBuilder) { 82 return Expression.Call( 83 variant, 84 typeof(Variant).GetMethod("SetAsIConvertible"), 85 argument 86 ); 87 } 88 89 if (Variant.IsPrimitiveType(_targetComType) || 90 (_targetComType == VarEnum.VT_DISPATCH) || 91 (_targetComType == VarEnum.VT_UNKNOWN) || 92 (_targetComType == VarEnum.VT_VARIANT) || 93 (_targetComType == VarEnum.VT_RECORD) || 94 (_targetComType == VarEnum.VT_ARRAY)){ 95 // paramVariants._elementN.AsT = (cast)argN 96 return Expression.Assign( 97 Expression.Property( 98 variant, 99 Variant.GetAccessor(_targetComType) 100 ), 101 argument 102 ); 103 } 104 105 switch (_targetComType) { 106 case VarEnum.VT_EMPTY: 107 return null; 108 109 case VarEnum.VT_NULL: 110 // paramVariants._elementN.SetAsNull(); 111 return Expression.Call(variant, typeof(Variant).GetMethod("SetAsNull")); 112 113 default: 114 Debug.Assert(false, "Unexpected VarEnum"); 115 return null; 116 } 117 } 118 Release(Expression pUnk)119 private static Expression Release(Expression pUnk) { 120 return Expression.Call(typeof(UnsafeMethods).GetMethod("IUnknownReleaseNotZero"), pUnk); 121 } 122 Clear()123 internal Expression Clear() { 124 if (IsByRef) { 125 if (_argBuilder is StringArgBuilder) { 126 Debug.Assert(TempVariable != null); 127 return Expression.Call(typeof(Marshal).GetMethod("FreeBSTR"), TempVariable); 128 } else if (_argBuilder is DispatchArgBuilder) { 129 Debug.Assert(TempVariable != null); 130 return Release(TempVariable); 131 } else if (_argBuilder is UnknownArgBuilder) { 132 Debug.Assert(TempVariable != null); 133 return Release(TempVariable); 134 } else if (_argBuilder is VariantArgBuilder) { 135 Debug.Assert(TempVariable != null); 136 return Expression.Call(TempVariable, typeof(Variant).GetMethod("Clear")); 137 } 138 return null; 139 } 140 141 142 switch (_targetComType) { 143 case VarEnum.VT_EMPTY: 144 case VarEnum.VT_NULL: 145 return null; 146 147 case VarEnum.VT_BSTR: 148 case VarEnum.VT_UNKNOWN: 149 case VarEnum.VT_DISPATCH: 150 case VarEnum.VT_ARRAY: 151 case VarEnum.VT_RECORD: 152 case VarEnum.VT_VARIANT: 153 // paramVariants._elementN.Clear() 154 return Expression.Call(_variant, typeof(Variant).GetMethod("Clear")); 155 156 default: 157 Debug.Assert(Variant.IsPrimitiveType(_targetComType), "Unexpected VarEnum"); 158 return null; 159 } 160 } 161 UpdateFromReturn(Expression parameter)162 internal Expression UpdateFromReturn(Expression parameter) { 163 if (TempVariable == null) { 164 return null; 165 } 166 return Expression.Assign( 167 parameter, 168 Helpers.Convert( 169 _argBuilder.UnmarshalFromRef(TempVariable), 170 parameter.Type 171 ) 172 ); 173 } 174 } 175 } 176 177 #endif 178