private Expression GenerateTryBlock()
        {
            //
            // Declare variables
            //
            ParameterExpression excepInfo = Expression.Variable(typeof(ExcepInfo), "excepInfo");
            ParameterExpression argErr    = Expression.Variable(typeof(uint), "argErr");
            ParameterExpression hresult   = Expression.Variable(typeof(int), "hresult");

            List <Expression> tryStatements = new List <Expression>();

            if (_keywordArgNames.Length > 0)
            {
                string[] names = _keywordArgNames.AddFirst(_methodDesc.Name);

                tryStatements.Add(
                    Expression.Assign(
                        Expression.Field(
                            DispParamsVariable,
                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgdispidNamedArgs))
                            ),
                        Expression.Call(typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.GetIdsOfNamedParameters)),
                                        DispatchObjectVariable,
                                        Expression.Constant(names),
                                        DispIdVariable,
                                        DispIdsOfKeywordArgsPinnedVariable
                                        )
                        )
                    );
            }

            //
            // Marshal the arguments to Variants
            //
            // For a call like this:
            //   comObj.Foo(100, 101, 102, x=123, z=125)
            // DISPPARAMS needs to be setup like this:
            //   cArgs:             5
            //   cNamedArgs:        2
            //   rgArgs:            123, 125, 102, 101, 100
            //   rgdispidNamedArgs: dispid x, dispid z (the dispids of x and z respectively)

            Expression[] parameters = MakeArgumentExpressions();

            int reverseIndex   = _varEnumSelector.VariantBuilders.Length - 1;
            int positionalArgs = _varEnumSelector.VariantBuilders.Length - _keywordArgNames.Length; // args passed by position order and not by name

            for (int i = 0; i < _varEnumSelector.VariantBuilders.Length; i++, reverseIndex--)
            {
                int variantIndex;
                if (i >= positionalArgs)
                {
                    // Named arguments are in order at the start of rgArgs
                    variantIndex = i - positionalArgs;
                }
                else
                {
                    // Positial arguments are in reverse order at the tail of rgArgs
                    variantIndex = reverseIndex;
                }
                VariantBuilder variantBuilder = _varEnumSelector.VariantBuilders[i];

                Expression marshal = variantBuilder.InitializeArgumentVariant(
                    VariantArray.GetStructField(ParamVariantsVariable, variantIndex),
                    parameters[i + 1]
                    );

                if (marshal != null)
                {
                    tryStatements.Add(marshal);
                }
            }

            //
            // Call Invoke
            //

            ComTypes.INVOKEKIND invokeKind;
            if (_methodDesc.IsPropertyPut)
            {
                if (_methodDesc.IsPropertyPutRef)
                {
                    invokeKind = ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF;
                }
                else
                {
                    invokeKind = ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT;
                }
            }
            else
            {
                // INVOKE_PROPERTYGET should only be needed for COM objects without typeinfo, where we might have to treat properties as methods
                invokeKind = ComTypes.INVOKEKIND.INVOKE_FUNC | ComTypes.INVOKEKIND.INVOKE_PROPERTYGET;
            }

            MethodCallExpression invoke = Expression.Call(
                typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.IDispatchInvoke)),
                DispatchPointerVariable,
                DispIdVariable,
                Expression.Constant(invokeKind),
                DispParamsVariable,
                InvokeResultVariable,
                excepInfo,
                argErr
                );

            Expression expr = Expression.Assign(hresult, invoke);

            tryStatements.Add(expr);

            //
            // ComRuntimeHelpers.CheckThrowException(hresult, excepInfo, argErr, ThisParameter);
            //
            expr = Expression.Call(
                typeof(ComRuntimeHelpers).GetMethod(nameof(ComRuntimeHelpers.CheckThrowException)),
                hresult,
                excepInfo,
                argErr,
                Expression.Constant(_methodDesc.Name, typeof(string))
                );
            tryStatements.Add(expr);

            //
            // _returnValue = (ReturnType)_invokeResult.ToObject();
            //
            Expression invokeResultObject =
                Expression.Call(
                    InvokeResultVariable,
                    typeof(Variant).GetMethod(nameof(Variant.ToObject)));

            VariantBuilder[] variants = _varEnumSelector.VariantBuilders;

            Expression[] parametersForUpdates = MakeArgumentExpressions();
            tryStatements.Add(Expression.Assign(ReturnValueVariable, invokeResultObject));

            for (int i = 0, n = variants.Length; i < n; i++)
            {
                Expression updateFromReturn = variants[i].UpdateFromReturn(parametersForUpdates[i + 1]);
                if (updateFromReturn != null)
                {
                    tryStatements.Add(updateFromReturn);
                }
            }

            tryStatements.Add(Expression.Empty());

            return(Expression.Block(new[] { excepInfo, argErr, hresult }, tryStatements));
        }
Beispiel #2
0
        private Expression MakeIDispatchInvokeTarget()
        {
            Debug.Assert(_varEnumSelector.VariantBuilders.Length == _totalExplicitArgs);

            List <Expression> exprs = new List <Expression>
            {
                //
                // _dispId = ((DispCallable)this).ComMethodDesc.DispId;
                //
                Expression.Assign(
                    DispIdVariable,
                    Expression.Property(_method, typeof(ComMethodDesc).GetProperty(nameof(ComMethodDesc.DispId)))
                    )
            };

            //
            // _dispParams.rgvararg = RuntimeHelpers.UnsafeMethods.ConvertVariantByrefToPtr(ref _paramVariants._element0)
            //
            if (_totalExplicitArgs != 0)
            {
                exprs.Add(
                    Expression.Assign(
                        Expression.Field(
                            DispParamsVariable,
                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgvarg))
                            ),
                        Expression.Call(
                            typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.ConvertVariantByrefToPtr)),
                            VariantArray.GetStructField(ParamVariantsVariable, 0)
                            )
                        )
                    );
            }

            //
            // _dispParams.cArgs = <number_of_params>;
            //
            exprs.Add(
                Expression.Assign(
                    Expression.Field(
                        DispParamsVariable,
                        typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cArgs))
                        ),
                    Expression.Constant(_totalExplicitArgs)
                    )
                );

            if (_methodDesc.IsPropertyPut)
            {
                //
                // dispParams.cNamedArgs = 1;
                // dispParams.rgdispidNamedArgs = RuntimeHelpers.UnsafeMethods.GetNamedArgsForPropertyPut()
                //
                exprs.Add(
                    Expression.Assign(
                        Expression.Field(
                            DispParamsVariable,
                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cNamedArgs))
                            ),
                        Expression.Constant(1)
                        )
                    );

                exprs.Add(
                    Expression.Assign(
                        PropertyPutDispIdVariable,
                        Expression.Constant(ComDispIds.DISPID_PROPERTYPUT)
                        )
                    );

                exprs.Add(
                    Expression.Assign(
                        Expression.Field(
                            DispParamsVariable,
                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgdispidNamedArgs))
                            ),
                        Expression.Call(
                            typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.ConvertInt32ByrefToPtr)),
                            PropertyPutDispIdVariable
                            )
                        )
                    );
            }
            else
            {
                //
                // _dispParams.cNamedArgs = N;
                //
                exprs.Add(
                    Expression.Assign(
                        Expression.Field(
                            DispParamsVariable,
                            typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cNamedArgs))
                            ),
                        Expression.Constant(_keywordArgNames.Length)
                        )
                    );
            }

            //
            // _dispatchObject = _dispatch
            // _dispatchPointer = Marshal.GetIDispatchForObject(_dispatchObject);
            //

            exprs.Add(Expression.Assign(DispatchObjectVariable, _dispatch));

            exprs.Add(
                Expression.Assign(
                    DispatchPointerVariable,
                    Expression.Call(
                        typeof(Marshal).GetMethod(nameof(Marshal.GetIDispatchForObject)),
                        DispatchObjectVariable
                        )
                    )
                );

            Expression tryStatements     = GenerateTryBlock();
            Expression finallyStatements = GenerateFinallyBlock();

            exprs.Add(Expression.TryFinally(tryStatements, finallyStatements));

            exprs.Add(ReturnValueVariable);
            var vars = new List <ParameterExpression>();

            foreach (VariantBuilder variant in _varEnumSelector.VariantBuilders)
            {
                if (variant.TempVariable != null)
                {
                    vars.Add(variant.TempVariable);
                }
            }
            return(Expression.Block(vars, exprs));
        }