PageRenderTime 30ms CodeModel.GetById 21ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_Main/Runtime/Microsoft.Dynamic/ComInterop/ComEventSinkProxy.cs

#
C# | 129 lines | 64 code | 19 blank | 46 comment | 6 complexity | bf3f2e3e2956ec051728cc69c705b0ae MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. 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  Apache License, Version 2.0, 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 Apache License, Version 2.0.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16#if !SILVERLIGHT // ComObject
 17
 18using System;
 19using System.Globalization;
 20using System.Reflection;
 21using System.Runtime.InteropServices;
 22using System.Runtime.Remoting;
 23using System.Runtime.Remoting.Messaging;
 24using System.Runtime.Remoting.Proxies;
 25using System.Security;
 26using System.Security.Permissions;
 27using Microsoft.Scripting.Utils;
 28
 29namespace Microsoft.Scripting.ComInterop {
 30    /// <summary>
 31    /// ComEventSinkProxy class is responsible for handling QIs for sourceIid 
 32    /// on instances of ComEventSink.
 33    /// 
 34    /// Background: When a COM even sink advises to a connection point it is 
 35    /// supposed to hand over the dispinterface. Now, some hosts will trust
 36    /// the COM client to pass the correct pointer, but some will not.
 37    /// E.g. Excel's implementation of Connection Points will not cause a
 38    /// QI on the pointer that has been passed, however Word will QI the
 39    /// pointer to return the required interface.
 40    /// 
 41    /// ComEventSink does not, strongly speaking, implements the interface 
 42    /// that it claims to implement - it is just "faking" it by using IReflect.
 43    /// Thus, Word's QIs on the pointer passed to ICP::Advise would fail. To
 44    /// prevent this we take advangate of RealProxy's ability of
 45    /// "dressing up" like other classes and hence successfully respond to QIs 
 46    /// for interfaces that it does not really support( it is OK to say 
 47    /// "I implement this interface" for event sinks only since the common 
 48    /// practice is to use IDistpach.Invoke when calling into event sinks). 
 49    /// </summary>
 50    internal sealed class ComEventSinkProxy : RealProxy {
 51
 52        private Guid _sinkIid;
 53        private ComEventSink _sink;
 54        private static readonly MethodInfo _methodInfoInvokeMember = typeof(ComEventSink).GetMethod("InvokeMember", BindingFlags.Instance | BindingFlags.Public);
 55        
 56        #region ctors
 57
 58        private ComEventSinkProxy() {
 59        }
 60
 61        public ComEventSinkProxy(ComEventSink sink, Guid sinkIid)
 62            : base(typeof(ComEventSink)) {
 63            _sink = sink;
 64            _sinkIid = sinkIid;
 65        }
 66
 67        #endregion
 68
 69        #region Base Class Overrides
 70
 71        public override IntPtr SupportsInterface(ref Guid iid) {
 72            // if the iid is the sink iid, we ask the base class for an rcw to IDispatch
 73            if (iid == _sinkIid) {
 74                IntPtr retVal = IntPtr.Zero;
 75                retVal = Marshal.GetIDispatchForObject(_sink);
 76                return retVal;
 77            }
 78
 79            return base.SupportsInterface(ref iid);
 80        }
 81
 82        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
 83        public override IMessage Invoke(IMessage msg) {
 84            ContractUtils.RequiresNotNull(msg, "msg");
 85
 86            //Only know how to handle method calls (property and fields accessors count as methods)
 87            IMethodCallMessage methodCallMessage = msg as IMethodCallMessage;
 88            if (methodCallMessage == null)
 89                throw new NotSupportedException();
 90
 91            // ComEventSink.InvokeMember is handled specially.
 92            // The reason we need to do that is due to how namedParameters arg (7th element in the IMethodCallMessage.Args array)
 93            // is marshalled when called through RealProxy.Invoke.
 94            // In RealProxy.Invoke namedParameters is typed as object[], while InvokeMember expects it to be string[].
 95            // If we simply let this call go through (with RemotingServices.ExecuteMessage)
 96            // we get an InvalidCastException when Remoting tries to pass namedParameters (of type object[])
 97            // to InvokeMember (which expects namedParameters to be string[]).
 98            // Since we don't use namedParameters in ComEventSink.InvokeMember - we simply ignore it here
 99            // and pass-in null.
100            MethodInfo methodInfo = (MethodInfo)methodCallMessage.MethodBase;
101            if (methodInfo == _methodInfoInvokeMember) {
102                object retVal = null;
103
104                try {
105                    // InvokeMember(string name, BindingFlags bindingFlags, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
106                    retVal = ((IReflect)_sink).InvokeMember(
107                        /*name*/                methodCallMessage.Args[0] as string,
108                        /*bindingFlags*/        (BindingFlags)methodCallMessage.Args[1],
109                        /*binder*/              methodCallMessage.Args[2] as Binder,
110                        /*target*/              null,
111                        /*args*/                methodCallMessage.Args[4] as object[],
112                        /*modifiers*/           methodCallMessage.Args[5] as ParameterModifier[],
113                        /*culture*/             methodCallMessage.Args[6] as CultureInfo,
114                        /*namedParameters*/     null);
115                } catch (Exception ex) {
116                    return new ReturnMessage(ex.InnerException, methodCallMessage);
117                }
118
119                return new ReturnMessage(retVal, methodCallMessage.Args, methodCallMessage.ArgCount, null, methodCallMessage);
120            }
121
122            return RemotingServices.ExecuteMessage(_sink, methodCallMessage);
123        }
124
125        #endregion
126    }
127}
128
129#endif