using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; using System.Security.Permissions; namespace Python.Runtime; [Serializable] internal class EventHandlerCollection: Dictionary> { readonly EventInfo info; public EventHandlerCollection(EventInfo @event) { info = @event; } /// /// Register a new Python object event handler with the event. /// internal bool AddEventHandler(BorrowedReference target, PyObject handler) { object? obj = null; if (target != null) { var co = (CLRObject)ManagedType.GetManagedObject(target)!; obj = co.inst; } // Create a true delegate instance of the appropriate type to // wrap the Python handler. Note that wrapper delegate creation // always succeeds, though calling the wrapper may fail. Type type = info.EventHandlerType; Delegate d = PythonEngine.DelegateManager.GetDelegate(type, handler); // Now register the handler in a mapping from instance to pairs // of (handler hash, delegate) so we can lookup to remove later. object key = obj ?? info.ReflectedType; if (!TryGetValue(key, out var list)) { list = new List(); this[key] = list; } list.Add(new Handler(Runtime.PyObject_Hash(handler), d)); // Note that AddEventHandler helper only works for public events, // so we have to get the underlying add method explicitly. object[] args = { d }; MethodInfo mi = info.GetAddMethod(true); mi.Invoke(obj, BindingFlags.Default, null, args, null); return true; } /// /// Remove the given Python object event handler. /// internal bool RemoveEventHandler(BorrowedReference target, BorrowedReference handler) { object? obj = null; if (target != null) { var co = (CLRObject)ManagedType.GetManagedObject(target)!; obj = co.inst; } nint hash = Runtime.PyObject_Hash(handler); if (hash == -1 && Exceptions.ErrorOccurred()) { return false; } object key = obj ?? info.ReflectedType; if (!TryGetValue(key, out var list)) { Exceptions.SetError(Exceptions.ValueError, "unknown event handler"); return false; } object?[] args = { null }; MethodInfo mi = info.GetRemoveMethod(true); for (var i = 0; i < list.Count; i++) { var item = (Handler)list[i]; if (item.hash != hash) { continue; } args[0] = item.del; try { mi.Invoke(obj, BindingFlags.Default, null, args, null); } catch { continue; } list.RemoveAt(i); return true; } Exceptions.SetError(Exceptions.ValueError, "unknown event handler"); return false; } #region Serializable [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] protected EventHandlerCollection(SerializationInfo info, StreamingContext context) : base(info, context) { this.info = (EventInfo)info.GetValue("event", typeof(EventInfo)); } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("event", this.info); } #endregion }