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