Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
ComInvokeBinder.cs566 linesDownload Raw Back to ComInterop
1// Licensed to the .NET Foundation under one or more agreements.2// The .NET Foundation licenses this file to you under the MIT license.3 4using System;5using System.Collections.Generic;6using System.Diagnostics;7using System.Dynamic;8using System.Linq.Expressions;9using System.Management.Automation.InteropServices;10using System.Runtime.InteropServices;11using ComTypes = System.Runtime.InteropServices.ComTypes;12 13namespace System.Management.Automation.ComInterop14{15    internal sealed class ComInvokeBinder16    {17        private readonly ComMethodDesc _methodDesc;18        private readonly Expression _method;        // ComMethodDesc to be called19        private readonly Expression _dispatch;      // IDispatch20 21        private readonly CallInfo _callInfo;22        private readonly DynamicMetaObject[] _args;23        private readonly bool[] _isByRef;24        private readonly Expression _instance;25 26        private BindingRestrictions _restrictions;27 28        private VarEnumSelector _varEnumSelector;29        private string[] _keywordArgNames;30        private int _totalExplicitArgs; // Includes the individual elements of ArgumentKind.Dictionary (if any)31 32        private ParameterExpression _dispatchObject;33        private ParameterExpression _dispatchPointer;34        private ParameterExpression _dispId;35        private ParameterExpression _dispParams;36        private ParameterExpression _paramVariants;37        private ParameterExpression _invokeResult;38        private ParameterExpression _returnValue;39        private ParameterExpression _dispIdsOfKeywordArgsPinned;40        private ParameterExpression _propertyPutDispId;41 42        internal ComInvokeBinder(43            CallInfo callInfo,44            DynamicMetaObject[] args,45            bool[] isByRef,46            BindingRestrictions restrictions,47            Expression method,48            Expression dispatch,49            ComMethodDesc methodDesc)50        {51            Debug.Assert(callInfo != null, nameof(callInfo));52            Debug.Assert(args != null, nameof(args));53            Debug.Assert(isByRef != null, nameof(isByRef));54            Debug.Assert(method != null, nameof(method));55            Debug.Assert(dispatch != null, nameof(dispatch));56 57            Debug.Assert(TypeUtils.AreReferenceAssignable(typeof(ComMethodDesc), method.Type), "method type");58            Debug.Assert(TypeUtils.AreReferenceAssignable(typeof(IDispatch), dispatch.Type), "dispatch type");59 60            _method = method;61            _dispatch = dispatch;62            _methodDesc = methodDesc;63 64            _callInfo = callInfo;65            _args = args;66            _isByRef = isByRef;67            _restrictions = restrictions;68 69            // Set Instance to some value so that CallBinderHelper has the right number of parameters to work with70            _instance = dispatch;71        }72 73        private ParameterExpression DispatchObjectVariable74        {75            get { return EnsureVariable(ref _dispatchObject, typeof(IDispatch), "dispatchObject"); }76        }77 78        private ParameterExpression DispatchPointerVariable79        {80            get { return EnsureVariable(ref _dispatchPointer, typeof(IntPtr), "dispatchPointer"); }81        }82 83        private ParameterExpression DispIdVariable84        {85            get { return EnsureVariable(ref _dispId, typeof(int), "dispId"); }86        }87 88        private ParameterExpression DispParamsVariable89        {90            get { return EnsureVariable(ref _dispParams, typeof(ComTypes.DISPPARAMS), "dispParams"); }91        }92 93        private ParameterExpression InvokeResultVariable94        {95            get { return EnsureVariable(ref _invokeResult, typeof(Variant), "invokeResult"); }96        }97 98        private ParameterExpression ReturnValueVariable99        {100            get { return EnsureVariable(ref _returnValue, typeof(object), "returnValue"); }101        }102 103        private ParameterExpression DispIdsOfKeywordArgsPinnedVariable104        {105            get { return EnsureVariable(ref _dispIdsOfKeywordArgsPinned, typeof(GCHandle), "dispIdsOfKeywordArgsPinned"); }106        }107 108        private ParameterExpression PropertyPutDispIdVariable109        {110            get { return EnsureVariable(ref _propertyPutDispId, typeof(int), "propertyPutDispId"); }111        }112 113        private ParameterExpression ParamVariantsVariable114        {115            get116            {117                _paramVariants ??= Expression.Variable(VariantArray.GetStructType(_args.Length), "paramVariants");118                return _paramVariants;119            }120        }121 122        private static ParameterExpression EnsureVariable(ref ParameterExpression var, Type type, string name)123        {124            if (var != null)125            {126                return var;127            }128            return var = Expression.Variable(type, name);129        }130 131        private static Type MarshalType(DynamicMetaObject mo, bool isByRef)132        {133            Type marshalType = (mo.Value == null && mo.HasValue && !mo.LimitType.IsValueType) ? null : mo.LimitType;134 135            // we are not checking that mo.Expression is writeable or whether evaluating it has no sideeffects136            // the assumption is that whoever matched it with ByRef arginfo took care of this.137            if (isByRef)138            {139                // Null just means that null was supplied.140                marshalType ??= mo.Expression.Type;141                marshalType = marshalType.MakeByRefType();142            }143            return marshalType;144        }145 146        internal DynamicMetaObject Invoke()147        {148            _keywordArgNames = _callInfo.ArgumentNames.ToArray();149            _totalExplicitArgs = _args.Length;150 151            Type[] marshalArgTypes = new Type[_args.Length];152 153            // We already tested the instance, so no need to test it again154            for (int i = 0; i < _args.Length; i++)155            {156                DynamicMetaObject curMo = _args[i];157                marshalArgTypes[i] = MarshalType(curMo, _isByRef[i]);158            }159 160            _varEnumSelector = new VarEnumSelector(marshalArgTypes);161 162            return new DynamicMetaObject(163                CreateScope(MakeIDispatchInvokeTarget()),164                BindingRestrictions.Combine(_args).Merge(_restrictions)165            );166        }167 168        private static void AddNotNull(List<ParameterExpression> list, ParameterExpression var)169        {170            if (var != null)171            {172                list.Add(var);173            }174        }175 176        private Expression CreateScope(Expression expression)177        {178            List<ParameterExpression> vars = new List<ParameterExpression>();179            AddNotNull(vars, _dispatchObject);180            AddNotNull(vars, _dispatchPointer);181            AddNotNull(vars, _dispId);182            AddNotNull(vars, _dispParams);183            AddNotNull(vars, _paramVariants);184            AddNotNull(vars, _invokeResult);185            AddNotNull(vars, _returnValue);186            AddNotNull(vars, _dispIdsOfKeywordArgsPinned);187            AddNotNull(vars, _propertyPutDispId);188            return vars.Count > 0 ? Expression.Block(vars, expression) : expression;189        }190 191        private Expression GenerateTryBlock()192        {193            //194            // Declare variables195            //196            ParameterExpression excepInfo = Expression.Variable(typeof(ExcepInfo), "excepInfo");197            ParameterExpression argErr = Expression.Variable(typeof(uint), "argErr");198            ParameterExpression hresult = Expression.Variable(typeof(int), "hresult");199 200            List<Expression> tryStatements = new List<Expression>();201 202            if (_keywordArgNames.Length > 0)203            {204                string[] names = _keywordArgNames.AddFirst(_methodDesc.Name);205 206                tryStatements.Add(207                    Expression.Assign(208                        Expression.Field(209                            DispParamsVariable,210                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgdispidNamedArgs))211                        ),212                        Expression.Call(typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.GetIdsOfNamedParameters)),213                            DispatchObjectVariable,214                            Expression.Constant(names),215                            DispIdVariable,216                            DispIdsOfKeywordArgsPinnedVariable217                        )218                    )219                );220            }221 222            //223            // Marshal the arguments to Variants224            //225            // For a call like this:226            //   comObj.Foo(100, 101, 102, x=123, z=125)227            // DISPPARAMS needs to be setup like this:228            //   cArgs:             5229            //   cNamedArgs:        2230            //   rgArgs:            123, 125, 102, 101, 100231            //   rgdispidNamedArgs: dispid x, dispid z (the dispids of x and z respectively)232 233            Expression[] parameters = MakeArgumentExpressions();234 235            int reverseIndex = _varEnumSelector.VariantBuilders.Length - 1;236            int positionalArgs = _varEnumSelector.VariantBuilders.Length - _keywordArgNames.Length; // args passed by position order and not by name237            for (int i = 0; i < _varEnumSelector.VariantBuilders.Length; i++, reverseIndex--)238            {239                int variantIndex;240                if (i >= positionalArgs)241                {242                    // Named arguments are in order at the start of rgArgs243                    variantIndex = i - positionalArgs;244                }245                else246                {247                    // Positional arguments are in reverse order at the tail of rgArgs248                    variantIndex = reverseIndex;249                }250                VariantBuilder variantBuilder = _varEnumSelector.VariantBuilders[i];251 252                Expression marshal = variantBuilder.InitializeArgumentVariant(253                    VariantArray.GetStructField(ParamVariantsVariable, variantIndex),254                    parameters[i + 1]255                );256 257                if (marshal != null)258                {259                    tryStatements.Add(marshal);260                }261            }262 263            //264            // Call Invoke265            //266 267            ComTypes.INVOKEKIND invokeKind;268            if (_methodDesc.IsPropertyPut)269            {270                if (_methodDesc.IsPropertyPutRef)271                {272                    invokeKind = ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF;273                }274                else275                {276                    invokeKind = ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT;277                }278            }279            else280            {281                // INVOKE_PROPERTYGET should only be needed for COM objects without typeinfo, where we might have to treat properties as methods282                invokeKind = ComTypes.INVOKEKIND.INVOKE_FUNC | ComTypes.INVOKEKIND.INVOKE_PROPERTYGET;283            }284 285            MethodCallExpression invoke = Expression.Call(286                typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.IDispatchInvoke)),287                DispatchPointerVariable,288                DispIdVariable,289                Expression.Constant(invokeKind),290                DispParamsVariable,291                InvokeResultVariable,292                excepInfo,293                argErr294            );295 296            Expression expr = Expression.Assign(hresult, invoke);297            tryStatements.Add(expr);298 299            //300            // ComRuntimeHelpers.CheckThrowException(int hresult, ref ExcepInfo excepInfo, ComMethodDesc method, object[] args, uint argErr)301            List<Expression> args = new List<Expression>();302            foreach (Expression parameter in parameters)303            {304                args.Add(Expression.TypeAs(parameter, typeof(object)));305            }306 307            expr = Expression.Call(308                typeof(ComRuntimeHelpers).GetMethod(nameof(ComRuntimeHelpers.CheckThrowException)),309                hresult,310                excepInfo,311                Expression.Constant(_methodDesc, typeof(ComMethodDesc)),312                Expression.NewArrayInit(typeof(object), args),313                argErr314            );315            tryStatements.Add(expr);316 317            //318            // _returnValue = (ReturnType)_invokeResult.ToObject();319            //320            Expression invokeResultObject =321                Expression.Call(322                    InvokeResultVariable,323                    typeof(Variant).GetMethod(nameof(Variant.ToObject)));324 325            VariantBuilder[] variants = _varEnumSelector.VariantBuilders;326 327            Expression[] parametersForUpdates = MakeArgumentExpressions();328            tryStatements.Add(Expression.Assign(ReturnValueVariable, invokeResultObject));329 330            for (int i = 0, n = variants.Length; i < n; i++)331            {332                Expression updateFromReturn = variants[i].UpdateFromReturn(parametersForUpdates[i + 1]);333                if (updateFromReturn != null)334                {335                    tryStatements.Add(updateFromReturn);336                }337            }338 339            tryStatements.Add(Expression.Empty());340 341            return Expression.Block(new[] { excepInfo, argErr, hresult }, tryStatements);342        }343 344        private Expression GenerateFinallyBlock()345        {346            List<Expression> finallyStatements = new List<Expression>347            {348                //349                // UnsafeMethods.IUnknownRelease(dispatchPointer);350                //351                Expression.Call(352                    typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.IUnknownRelease)),353                    DispatchPointerVariable354                )355            };356 357            //358            // Clear memory allocated for marshalling359            //360            for (int i = 0, n = _varEnumSelector.VariantBuilders.Length; i < n; i++)361            {362                Expression clear = _varEnumSelector.VariantBuilders[i].Clear();363                if (clear != null)364                {365                    finallyStatements.Add(clear);366                }367            }368 369            //370            // _invokeResult.Clear()371            //372 373            finallyStatements.Add(374                Expression.Call(375                    InvokeResultVariable,376                    typeof(Variant).GetMethod(nameof(Variant.Clear))377                )378            );379 380            //381            // _dispIdsOfKeywordArgsPinned.Free()382            //383            if (_dispIdsOfKeywordArgsPinned != null)384            {385                finallyStatements.Add(386                    Expression.Call(387                        DispIdsOfKeywordArgsPinnedVariable,388                        typeof(GCHandle).GetMethod(nameof(GCHandle.Free))389                    )390                );391            }392 393            finallyStatements.Add(Expression.Empty());394            return Expression.Block(finallyStatements);395        }396 397        /// <summary>398        /// Create a stub for the target of the optimized loop.399        /// </summary>400        /// <returns></returns>401        private Expression MakeIDispatchInvokeTarget()402        {403            Debug.Assert(_varEnumSelector.VariantBuilders.Length == _totalExplicitArgs);404 405            List<Expression> exprs = new List<Expression>406            {407                //408                // _dispId = ((DispCallable)this).ComMethodDesc.DispId;409                //410                Expression.Assign(411                    DispIdVariable,412                    Expression.Property(_method, typeof(ComMethodDesc).GetProperty(nameof(ComMethodDesc.DispId)))413                )414            };415 416            //417            // _dispParams.rgvararg = RuntimeHelpers.UnsafeMethods.ConvertVariantByrefToPtr(ref _paramVariants._element0)418            //419            if (_totalExplicitArgs != 0)420            {421                exprs.Add(422                    Expression.Assign(423                        Expression.Field(424                            DispParamsVariable,425                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgvarg))426                        ),427                        Expression.Call(428                            typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.ConvertVariantByrefToPtr)),429                            VariantArray.GetStructField(ParamVariantsVariable, 0)430                        )431                    )432                );433            }434 435            //436            // _dispParams.cArgs = <number_of_params>;437            //438            exprs.Add(439                Expression.Assign(440                    Expression.Field(441                        DispParamsVariable,442                        typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cArgs))443                    ),444                    Expression.Constant(_totalExplicitArgs)445                )446            );447 448            if (_methodDesc.IsPropertyPut)449            {450                //451                // dispParams.cNamedArgs = 1;452                // dispParams.rgdispidNamedArgs = RuntimeHelpers.UnsafeMethods.GetNamedArgsForPropertyPut()453                //454                exprs.Add(455                    Expression.Assign(456                        Expression.Field(457                            DispParamsVariable,458                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cNamedArgs))459                        ),460                        Expression.Constant(1)461                    )462                );463 464                exprs.Add(465                    Expression.Assign(466                        PropertyPutDispIdVariable,467                        Expression.Constant(ComDispIds.DISPID_PROPERTYPUT)468                    )469                );470 471                exprs.Add(472                    Expression.Assign(473                        Expression.Field(474                            DispParamsVariable,475                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgdispidNamedArgs))476                        ),477                        Expression.Call(478                            typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.ConvertInt32ByrefToPtr)),479                            PropertyPutDispIdVariable480                        )481                    )482                );483            }484            else485            {486                //487                // _dispParams.cNamedArgs = N;488                //489                exprs.Add(490                    Expression.Assign(491                        Expression.Field(492                            DispParamsVariable,493                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cNamedArgs))494                        ),495                        Expression.Constant(_keywordArgNames.Length)496                    )497                );498            }499 500            //501            // _dispatchObject = _dispatch502            // _dispatchPointer = Marshal.GetIDispatchForObject(_dispatchObject);503            //504 505            exprs.Add(Expression.Assign(DispatchObjectVariable, _dispatch));506 507            exprs.Add(508                Expression.Assign(509                    DispatchPointerVariable,510                    Expression.Call(511                        typeof(Marshal).GetMethod(nameof(Marshal.GetIDispatchForObject)),512                        DispatchObjectVariable513                    )514                )515            );516 517            Expression tryStatements = GenerateTryBlock();518            Expression finallyStatements = GenerateFinallyBlock();519 520            exprs.Add(Expression.TryFinally(tryStatements, finallyStatements));521 522            exprs.Add(ReturnValueVariable);523            var vars = new List<ParameterExpression>();524            foreach (VariantBuilder variant in _varEnumSelector.VariantBuilders)525            {526                if (variant.TempVariable != null)527                {528                    vars.Add(variant.TempVariable);529                }530            }531 532            // If the method returns void, return AutomationNull533            if (_methodDesc.ReturnType == typeof(void))534            {535                exprs.Add(System.Management.Automation.Language.ExpressionCache.AutomationNullConstant);536            }537 538            return Expression.Block(vars, exprs);539        }540 541        /// <summary>542        /// Gets expressions to access all the arguments. This includes the instance argument.543        /// </summary>544        private Expression[] MakeArgumentExpressions()545        {546            Expression[] res;547            int copy = 0;548            if (_instance != null)549            {550                res = new Expression[_args.Length + 1];551                res[copy++] = _instance;552            }553            else554            {555                res = new Expression[_args.Length];556            }557 558            for (int i = 0; i < _args.Length; i++)559            {560                res[copy++] = _args[i].Expression;561            }562            return res;563        }564    }565}566