| File: Microsoft\CSharp\RuntimeBinder\ComInterop\VariantBuilder.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.CSharp\src\Microsoft.CSharp.csproj (Microsoft.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CSharp.RuntimeBinder.ComInterop { /// <summary> /// VariantBuilder handles packaging of arguments into an ComVariant for a call to IDispatch.Invoke /// </summary> internal sealed class VariantBuilder { private MemberExpression _variant; private readonly ArgBuilder _argBuilder; private readonly VarEnum _targetComType; internal ParameterExpression TempVariable { get; private set; } internal VariantBuilder(VarEnum targetComType, ArgBuilder builder) { _targetComType = targetComType; _argBuilder = builder; } internal bool IsByRef { get { return (_targetComType & VarEnum.VT_BYREF) != 0; } } [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expression InitializeArgumentVariant(MemberExpression variant, Expression parameter) { //NOTE: we must remember our variant //the reason is that argument order does not map exactly to the order of variants for invoke //and when we are doing clean-up we must be sure we are cleaning the variant we have initialized. _variant = variant; if (IsByRef) { // temp = argument // paramVariants._elementN.SetAsByrefT(ref temp) Debug.Assert(TempVariable == null); Expression argExpr = _argBuilder.MarshalToRef(parameter); TempVariable = Expression.Variable(argExpr.Type, null); return Expression.Block( Expression.Assign(TempVariable, argExpr), Expression.Call( DynamicVariantExtensions.GetByrefSetter(_targetComType & ~VarEnum.VT_BYREF), variant, TempVariable ) ); } Expression argument = _argBuilder.Marshal(parameter); // we are forced to special case ConvertibleArgBuilder since it does not have // a corresponding _targetComType. if (_argBuilder is ConvertibleArgBuilder) { return Expression.Call( typeof(DynamicVariantExtensions).GetMethod(nameof(DynamicVariantExtensions.SetAsIConvertible)), variant, argument ); } if (_targetComType.IsPrimitiveType() || (_targetComType == VarEnum.VT_DISPATCH) || (_targetComType == VarEnum.VT_UNKNOWN) || (_targetComType == VarEnum.VT_VARIANT) || (_targetComType == VarEnum.VT_RECORD) || (_targetComType == VarEnum.VT_ARRAY)) { // paramVariants._elementN.AsT = (cast)argN return Expression.Call(null, DynamicVariantExtensions.GetSetter(_targetComType), variant, argument); } switch (_targetComType) { case VarEnum.VT_EMPTY: return null; case VarEnum.VT_NULL: // paramVariants._elementN = ComVariant.Null; return Expression.Assign(variant, Expression.Property(null, typeof(ComVariant).GetProperty(nameof(ComVariant.Null), BindingFlags.Public | BindingFlags.Static))); default: Debug.Fail("Unexpected VarEnum"); return null; } } private static Expression Release(Expression pUnk) { return Expression.Call(typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.IUnknownReleaseNotZero)), pUnk); } internal Expression Clear() { if (IsByRef) { if (_argBuilder is StringArgBuilder) { Debug.Assert(TempVariable != null); return Expression.Call(typeof(Marshal).GetMethod(nameof(Marshal.FreeBSTR)), TempVariable); } if (_argBuilder is DispatchArgBuilder) { Debug.Assert(TempVariable != null); return Release(TempVariable); } if (_argBuilder is UnknownArgBuilder) { Debug.Assert(TempVariable != null); return Release(TempVariable); } if (_argBuilder is VariantArgBuilder) { Debug.Assert(TempVariable != null); return Expression.Call(TempVariable, typeof(ComVariant).GetMethod(nameof(ComVariant.Dispose))); } return null; } switch (_targetComType) { case VarEnum.VT_EMPTY: case VarEnum.VT_NULL: return null; case VarEnum.VT_BSTR: case VarEnum.VT_UNKNOWN: case VarEnum.VT_DISPATCH: case VarEnum.VT_ARRAY: case VarEnum.VT_RECORD: case VarEnum.VT_VARIANT: // paramVariants._elementN.Dispose() return Expression.Call(_variant, typeof(ComVariant).GetMethod(nameof(ComVariant.Dispose))); default: Debug.Assert(_targetComType.IsPrimitiveType(), "Unexpected VarEnum"); return null; } } internal Expression UpdateFromReturn(Expression parameter) { if (TempVariable == null) { return null; } return Expression.Assign( parameter, Helpers.Convert( _argBuilder.UnmarshalFromRef(TempVariable), parameter.Type ) ); } } }