PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/dlr/Runtime/Microsoft.Dynamic/ComEventSink.cs

https://bitbucket.org/danipen/mono
C# | 349 lines | 223 code | 69 blank | 57 comment | 44 complexity | 741b1bc0b951f3e41fbf3c945081328d MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System; using Microsoft;
  16. #if !SILVERLIGHT // ComObject
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Globalization;
  20. using System.Reflection;
  21. using System.Runtime.InteropServices;
  22. using System.Security;
  23. using ComTypes = System.Runtime.InteropServices.ComTypes;
  24. #if CODEPLEX_40
  25. namespace System.Dynamic {
  26. #else
  27. namespace Microsoft.Scripting {
  28. #endif
  29. /// <summary>
  30. /// This class implements an event sink for a particular RCW.
  31. /// Unlike the implementation of events in TlbImp'd assemblies,
  32. /// we will create only one event sink per RCW (theoretically RCW might have
  33. /// several ComEventSink evenk sinks - but all these implement different source intefaces).
  34. /// Each ComEventSink contains a list of ComEventSinkMethod objects - which represent
  35. /// a single method on the source interface an a multicast delegate to redirect
  36. /// the calls. Notice that we are chaining multicast delegates so that same
  37. /// ComEventSinkMedhod can invoke multiple event handlers).
  38. ///
  39. /// ComEventSink implements an IDisposable pattern to Unadvise from the connection point.
  40. /// Typically, when RCW is finalized the corresponding Dispose will be triggered by
  41. /// ComEventSinksContainer finalizer. Notice that lifetime of ComEventSinksContainer
  42. /// is bound to the lifetime of the RCW.
  43. /// </summary>
  44. internal sealed class ComEventSink : MarshalByRefObject, IReflect, IDisposable {
  45. #region private fields
  46. private Guid _sourceIid;
  47. private ComTypes.IConnectionPoint _connectionPoint;
  48. private int _adviseCookie;
  49. private List<ComEventSinkMethod> _comEventSinkMethods;
  50. private object _lockObject = new object(); // We cannot lock on ComEventSink since it causes a DoNotLockOnObjectsWithWeakIdentity warning
  51. #endregion
  52. #region private classes
  53. /// <summary>
  54. /// Contains a methods DISPID (in a string formatted of "[DISPID=N]"
  55. /// and a chained list of delegates to invoke
  56. /// </summary>
  57. private class ComEventSinkMethod {
  58. public string _name;
  59. public Func<object[], object> _handlers;
  60. }
  61. #endregion
  62. #region ctor
  63. [SecurityCritical]
  64. private ComEventSink(object rcw, Guid sourceIid) {
  65. Initialize(rcw, sourceIid);
  66. }
  67. #endregion
  68. [SecurityCritical]
  69. private void Initialize(object rcw, Guid sourceIid) {
  70. _sourceIid = sourceIid;
  71. _adviseCookie = -1;
  72. Debug.Assert(_connectionPoint == null, "re-initializing event sink w/o unadvising from connection point");
  73. ComTypes.IConnectionPointContainer cpc = rcw as ComTypes.IConnectionPointContainer;
  74. if (cpc == null)
  75. throw Error.COMObjectDoesNotSupportEvents();
  76. cpc.FindConnectionPoint(ref _sourceIid, out _connectionPoint);
  77. if (_connectionPoint == null)
  78. throw Error.COMObjectDoesNotSupportSourceInterface();
  79. // Read the comments for ComEventSinkProxy about why we need it
  80. ComEventSinkProxy proxy = new ComEventSinkProxy(this, _sourceIid);
  81. _connectionPoint.Advise(proxy.GetTransparentProxy(), out _adviseCookie);
  82. }
  83. #region static methods
  84. [SecurityCritical]
  85. public static ComEventSink FromRuntimeCallableWrapper(object rcw, Guid sourceIid, bool createIfNotFound) {
  86. List<ComEventSink> comEventSinks = ComEventSinksContainer.FromRuntimeCallableWrapper(rcw, createIfNotFound);
  87. if (comEventSinks == null) {
  88. return null;
  89. }
  90. ComEventSink comEventSink = null;
  91. lock (comEventSinks) {
  92. foreach (ComEventSink sink in comEventSinks) {
  93. if (sink._sourceIid == sourceIid) {
  94. comEventSink = sink;
  95. break;
  96. } else if (sink._sourceIid == Guid.Empty) {
  97. // we found a ComEventSink object that
  98. // was previously disposed. Now we will reuse it.
  99. sink.Initialize(rcw, sourceIid);
  100. comEventSink = sink;
  101. }
  102. }
  103. if (comEventSink == null && createIfNotFound == true) {
  104. comEventSink = new ComEventSink(rcw, sourceIid);
  105. comEventSinks.Add(comEventSink);
  106. }
  107. }
  108. return comEventSink;
  109. }
  110. #endregion
  111. public void AddHandler(int dispid, object func) {
  112. string name = String.Format(CultureInfo.InvariantCulture, "[DISPID={0}]", dispid);
  113. lock (_lockObject) {
  114. ComEventSinkMethod sinkMethod;
  115. sinkMethod = FindSinkMethod(name);
  116. if (sinkMethod == null) {
  117. if (_comEventSinkMethods == null) {
  118. _comEventSinkMethods = new List<ComEventSinkMethod>();
  119. }
  120. sinkMethod = new ComEventSinkMethod();
  121. sinkMethod._name = name;
  122. _comEventSinkMethods.Add(sinkMethod);
  123. }
  124. sinkMethod._handlers += new SplatCallSite(func).Invoke;
  125. }
  126. }
  127. [SecurityCritical]
  128. public void RemoveHandler(int dispid, object func) {
  129. string name = String.Format(CultureInfo.InvariantCulture, "[DISPID={0}]", dispid);
  130. lock (_lockObject) {
  131. ComEventSinkMethod sinkEntry = FindSinkMethod(name);
  132. if (sinkEntry == null){
  133. return;
  134. }
  135. // Remove the delegate from multicast delegate chain.
  136. // We will need to find the delegate that corresponds
  137. // to the func handler we want to remove. This will be
  138. // easy since we Target property of the delegate object
  139. // is a ComEventCallContext object.
  140. Delegate[] delegates = sinkEntry._handlers.GetInvocationList();
  141. foreach (Delegate d in delegates) {
  142. SplatCallSite callContext = d.Target as SplatCallSite;
  143. if (callContext != null && callContext._callable.Equals(func)) {
  144. sinkEntry._handlers -= d as Func<object[], object>;
  145. break;
  146. }
  147. }
  148. // If the delegates chain is empty - we can remove
  149. // corresponding ComEvenSinkEntry
  150. if (sinkEntry._handlers == null)
  151. _comEventSinkMethods.Remove(sinkEntry);
  152. // We can Unadvise from the ConnectionPoint if no more sink entries
  153. // are registered for this interface
  154. //(calling Dispose will call IConnectionPoint.Unadvise).
  155. if (_comEventSinkMethods.Count == 0) {
  156. // notice that we do not remove
  157. // ComEventSinkEntry from the list, we will re-use this data structure
  158. // if a new handler needs to be attached.
  159. Dispose();
  160. }
  161. }
  162. }
  163. public object ExecuteHandler(string name, object[] args) {
  164. ComEventSinkMethod site;
  165. site = FindSinkMethod(name);
  166. if (site != null && site._handlers != null) {
  167. return site._handlers(args);
  168. }
  169. return null;
  170. }
  171. #region IReflect
  172. #region Unimplemented members
  173. public FieldInfo GetField(string name, BindingFlags bindingAttr) {
  174. return null;
  175. }
  176. public FieldInfo[] GetFields(BindingFlags bindingAttr) {
  177. return new FieldInfo[0];
  178. }
  179. public MemberInfo[] GetMember(string name, BindingFlags bindingAttr) {
  180. return new MemberInfo[0];
  181. }
  182. public MemberInfo[] GetMembers(BindingFlags bindingAttr) {
  183. return new MemberInfo[0];
  184. }
  185. public MethodInfo GetMethod(string name, BindingFlags bindingAttr) {
  186. return null;
  187. }
  188. public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) {
  189. return null;
  190. }
  191. public MethodInfo[] GetMethods(BindingFlags bindingAttr) {
  192. return new MethodInfo[0];
  193. }
  194. public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) {
  195. return null;
  196. }
  197. public PropertyInfo GetProperty(string name, BindingFlags bindingAttr) {
  198. return null;
  199. }
  200. public PropertyInfo[] GetProperties(BindingFlags bindingAttr) {
  201. return new PropertyInfo[0];
  202. }
  203. #endregion
  204. public Type UnderlyingSystemType {
  205. get {
  206. return typeof(object);
  207. }
  208. }
  209. public object InvokeMember(
  210. string name,
  211. BindingFlags invokeAttr,
  212. Binder binder,
  213. object target,
  214. object[] args,
  215. ParameterModifier[] modifiers,
  216. CultureInfo culture,
  217. string[] namedParameters) {
  218. return ExecuteHandler(name, args);
  219. }
  220. #endregion
  221. #region IDisposable
  222. #if CLR2
  223. [SecurityCritical, SecurityTreatAsSafe]
  224. #else
  225. [SecuritySafeCritical]
  226. #endif
  227. public void Dispose() {
  228. DisposeAll();
  229. GC.SuppressFinalize(this);
  230. }
  231. #endregion
  232. #if CLR2
  233. [SecurityCritical, SecurityTreatAsSafe]
  234. #else
  235. [SecuritySafeCritical]
  236. #endif
  237. ~ComEventSink() {
  238. DisposeAll();
  239. }
  240. [SecurityCritical]
  241. private void DisposeAll() {
  242. if (_connectionPoint == null) {
  243. return;
  244. }
  245. if (_adviseCookie == -1) {
  246. return;
  247. }
  248. try {
  249. _connectionPoint.Unadvise(_adviseCookie);
  250. // _connectionPoint has entered the CLR in the constructor
  251. // for this object and hence its ref counter has been increased
  252. // by us. We have not exposed it to other components and
  253. // hence it is safe to call RCO on it w/o worrying about
  254. // killing the RCW for other objects that link to it.
  255. Marshal.ReleaseComObject(_connectionPoint);
  256. } catch (Exception ex) {
  257. // if something has gone wrong, and the object is no longer attached to the CLR,
  258. // the Unadvise is going to throw. In this case, since we're going away anyway,
  259. // we'll ignore the failure and quietly go on our merry way.
  260. COMException exCOM = ex as COMException;
  261. if (exCOM != null && exCOM.ErrorCode == ComHresults.CONNECT_E_NOCONNECTION) {
  262. Debug.Assert(false, "IConnectionPoint::Unadvise returned CONNECT_E_NOCONNECTION.");
  263. throw;
  264. }
  265. } finally {
  266. _connectionPoint = null;
  267. _adviseCookie = -1;
  268. _sourceIid = Guid.Empty;
  269. }
  270. }
  271. private ComEventSinkMethod FindSinkMethod(string name) {
  272. if (_comEventSinkMethods == null)
  273. return null;
  274. ComEventSinkMethod site;
  275. site = _comEventSinkMethods.Find(element => element._name == name);
  276. return site;
  277. }
  278. }
  279. }
  280. #endif