//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ #if DEBUG /* This class exists in debug only. It is a complete copy of the V1.0 TypeDescriptor object and is used to validate that the behavior of the V2.0 TypeDescriptor matches 1.0 behavior. */ namespace System.ComponentModel { using System.Diagnostics; using System; using System.Reflection; using System.Collections; using Microsoft.Win32; using System.ComponentModel.Design; using System.Security.Permissions; /// /// /// /// DebugReflectEventDescriptor defines an event. Events are the main way that a user can get /// run-time notifications from a component. /// The DebugReflectEventDescriptor class takes a component class that the event lives on, /// the event name, the type of the event handling delegate, and various /// attributes for the event. /// Every event has a structure through which it passes it's information. The base /// structure, Event, is empty, and there is a default instance, Event.EMPTY, which /// is usually passed. When addOnXXX is invoked, it needs a pointer to a method /// that takes a source object (the object that fired the event) and a structure /// particular to that event. It also needs a pointer to the instance of the /// object the method should be invoked on. These two things are what composes a /// delegate. An event handler is /// a delegate, and the compiler recognizes a special delegate syntax that makes /// using delegates easy. /// For example, to listen to the click event on a button in class Foo, the /// following code will suffice: /// /// /// class Foo { /// Button button1 = new Button(); /// void button1_click(Object sender, Event e) { /// // do something on button1 click. /// } /// public Foo() { /// button1.addOnClick(this.button1_click); /// } /// } /// /// For an event named XXX, a YYYEvent structure, and a YYYEventHandler delegate, /// a component writer is required to implement two methods of the following /// form: /// /// public void addOnXXX(YYYEventHandler handler); /// public void removeOnXXX(YYYEventHandler handler); /// /// YYYEventHandler should be an event handler declared as /// /// public multicast delegate void YYYEventHandler(Object sender, YYYEvent e); /// /// Note that this event was declared as a multicast delegate. This allows multiple /// listeners on an event. This is not a requirement. /// Various attributes can be passed to the DebugReflectEventDescriptor, as are described in /// Attribute. /// ReflectEventDescriptors can be obtained by a user programmatically through the /// ComponentManager. /// [HostProtection(SharedState = true)] internal sealed class DebugReflectEventDescriptor : EventDescriptor { private static readonly Type[] argsNone = new Type[0]; private static readonly object noDefault = new object(); private Type type; // the delegate type for the event private readonly Type componentClass; // the class of the component this info is for internal MethodInfo addMethod; // the method to use when adding an event internal MethodInfo removeMethod; // the method to use when removing an event private EventInfo realEvent; // actual event info... may be null private bool filledMethods = false; // did we already call FillMethods() once? /// /// This is the main constructor for an DebugReflectEventDescriptor. /// public DebugReflectEventDescriptor(Type componentClass, string name, Type type, Attribute[] attributes) : base(name, attributes) { if (componentClass == null) { throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, "componentClass")); } if (type == null || !(typeof(Delegate)).IsAssignableFrom(type)) { throw new ArgumentException(SR.GetString(SR.ErrorInvalidEventType, name)); } Debug.Assert(type.IsSubclassOf(typeof(Delegate)), "Not a valid ReflectEvent: " + componentClass.FullName + "." + name + " " + type.FullName); this.componentClass = componentClass; this.type = type; } public DebugReflectEventDescriptor(Type componentClass, EventInfo eventInfo) : base(eventInfo.Name, new Attribute[0]) { if (componentClass == null) { throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, "componentClass")); } this.componentClass = componentClass; this.realEvent = eventInfo; } /// /// This constructor takes an existing DebugReflectEventDescriptor and modifies it by merging in the /// passed-in attributes. /// public DebugReflectEventDescriptor(Type componentType, EventDescriptor oldReflectEventDescriptor, Attribute[] attributes) : base(oldReflectEventDescriptor, attributes) { this.componentClass = componentType; this.type = oldReflectEventDescriptor.EventType; if (oldReflectEventDescriptor is DebugReflectEventDescriptor) { this.addMethod = ((DebugReflectEventDescriptor)oldReflectEventDescriptor).addMethod; this.removeMethod = ((DebugReflectEventDescriptor)oldReflectEventDescriptor).removeMethod; this.filledMethods = true; } } /// /// Retrieves the type of the component this EventDescriptor is bound to. /// public override Type ComponentType { get { return componentClass; } } /// /// Retrieves the type of the delegate for this event. /// public override Type EventType { get { FillMethods(); return type; } } /// /// Indicates whether the delegate type for this event is a multicast /// delegate. /// public override bool IsMulticast { get { return(typeof(MulticastDelegate)).IsAssignableFrom(EventType); } } /// /// This adds the delegate value as a listener to when this event is fired /// by the component, invoking the addOnXXX method. /// public override void AddEventHandler(object component, Delegate value) { FillMethods(); if (component != null) { ISite site = GetSite(component); IComponentChangeService changeService = null; // Announce that we are about to change this component // if (site != null) { changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found"); } if (changeService != null) { try { changeService.OnComponentChanging(component, this); } catch (CheckoutException coEx) { if (coEx == CheckoutException.Canceled) { return; } throw coEx; } } bool shadowed = false; if (site != null && site.DesignMode) { // Events are final, so just check the class if (EventType != value.GetType()) { throw new ArgumentException(SR.GetString(SR.ErrorInvalidEventHandler, Name)); } IDictionaryService dict = (IDictionaryService)site.GetService(typeof(IDictionaryService)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || dict != null, "IDictionaryService not found"); if (dict != null) { Delegate eventdesc = (Delegate)dict.GetValue(this); eventdesc = Delegate.Combine(eventdesc, value); dict.SetValue(this, eventdesc); shadowed = true; } } if (!shadowed) { addMethod.Invoke(component, new object[] { value}); } // Now notify the change service that the change was successful. // if (changeService != null) { changeService.OnComponentChanged(component, this, null, value); } } } // // // Adds in custom attributes found on either the AddOn or RemoveOn method... // // // protected override void FillAttributes(IList attributes) { // // The order that we fill in attributes is critical. The list of attributes will be // filtered so that matching attributes at the end of the list replace earlier matches // (last one in wins). Therefore, the two categories of attributes we add must be // added as follows: // // 1. Attributes of the event, from base class to most derived. This way // derived class attributes replace base class attributes. // // 2. Attributes from our base MemberDescriptor. While this seems opposite of what // we want, MemberDescriptor only has attributes if someone passed in a new // set in the constructor. Therefore, these attributes always // supercede existing values. // FillMethods(); Debug.Assert(componentClass != null, "Must have a component class for FilterAttributes"); if (realEvent != null) { FillEventInfoAttribute(realEvent, attributes); } else { Debug.Assert(removeMethod != null, "Null remove method for " + Name); FillSingleMethodAttribute(removeMethod, attributes); Debug.Assert(addMethod != null, "Null remove method for " + Name); FillSingleMethodAttribute(addMethod, attributes); } // Include the base attributes. These override all attributes on the actual // property, so we want to add them last. // base.FillAttributes(attributes); } private void FillEventInfoAttribute(EventInfo realEventInfo, IList attributes) { string eventName = realEventInfo.Name; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; Type currentReflectType = realEventInfo.ReflectedType; Debug.Assert(currentReflectType != null, "currentReflectType cannot be null"); int depth = 0; // First, calculate the depth of the object hierarchy. We do this so we can do a single // object create for an array of attributes. // while(currentReflectType != typeof(object)) { depth++; currentReflectType = currentReflectType.BaseType; } if (depth > 0) { // Now build up an array in reverse order // currentReflectType = realEventInfo.ReflectedType; object[][] attributeStack = new object[depth][]; while(currentReflectType != typeof(object)) { // Fill in our member info so we can get at the custom attributes. // MemberInfo memberInfo = currentReflectType.GetEvent(eventName, bindingFlags); // Get custom attributes for the member info. // if (memberInfo != null) { attributeStack[--depth] = DebugTypeDescriptor.GetCustomAttributes(memberInfo); } // Ready for the next loop iteration. // currentReflectType = currentReflectType.BaseType; } // Now trawl the attribute stack so that we add attributes // from base class to most derived. // foreach(object[] attributeArray in attributeStack) { if (attributeArray != null) { foreach(object attr in attributeArray) { if (attr is Attribute) { attributes.Add(attr); } } } } } } /// /// This fills the get and set method fields of the event info. It is shared /// by the various constructors. /// private void FillMethods() { if (filledMethods) return; if (realEvent != null) { addMethod = realEvent.GetAddMethod(); removeMethod = realEvent.GetRemoveMethod(); EventInfo defined = null; if (addMethod == null || removeMethod == null) { Type start = componentClass.BaseType; while (start != null && start != typeof(object)) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; EventInfo test = start.GetEvent(realEvent.Name, bindingFlags); if (test.GetAddMethod() != null) { defined = test; break; } } } if (defined != null) { addMethod = defined.GetAddMethod(); removeMethod = defined.GetRemoveMethod(); type = defined.EventHandlerType; } else { type = realEvent.EventHandlerType; } } else { // first, try to get the eventInfo... // realEvent = this.componentClass.GetEvent(Name); if (realEvent != null) { // if we got one, just recurse and return. // FillMethods(); return; } Type[] argsType = new Type[] {type}; addMethod = FindMethod(componentClass, "AddOn" + Name, argsType, typeof(void)); removeMethod = FindMethod(componentClass, "RemoveOn" + Name, argsType, typeof(void)); if (addMethod == null || removeMethod == null) { Debug.Fail("Missing event accessors for " + componentClass.FullName + "." + Name); throw new ArgumentException(SR.GetString(SR.ErrorMissingEventAccessors, Name)); } } filledMethods = true; } private void FillSingleMethodAttribute(MethodInfo realMethodInfo, IList attributes) { string methodName = realMethodInfo.Name; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; Type currentReflectType = realMethodInfo.ReflectedType; Debug.Assert(currentReflectType != null, "currentReflectType cannot be null"); // First, calculate the depth of the object hierarchy. We do this so we can do a single // object create for an array of attributes. // int depth = 0; while(currentReflectType != null && currentReflectType != typeof(object)) { depth++; currentReflectType = currentReflectType.BaseType; } if (depth > 0) { // Now build up an array in reverse order // currentReflectType = realMethodInfo.ReflectedType; object[][] attributeStack = new object[depth][]; while(currentReflectType != null && currentReflectType != typeof(object)) { // Fill in our member info so we can get at the custom attributes. // MemberInfo memberInfo = currentReflectType.GetMethod(methodName, bindingFlags); // Get custom attributes for the member info. // if (memberInfo != null) { attributeStack[--depth] = DebugTypeDescriptor.GetCustomAttributes(memberInfo); } // Ready for the next loop iteration. // currentReflectType = currentReflectType.BaseType; } // Now trawl the attribute stack so that we add attributes // from base class to most derived. // foreach(object[] attributeArray in attributeStack) { if (attributeArray != null) { foreach(object attr in attributeArray) { if (attr is Attribute) { attributes.Add(attr); } } } } } } /// /// This will remove the delegate value from the event chain so that /// it no longer gets events from this component. /// public override void RemoveEventHandler(object component, Delegate value) { FillMethods(); if (component != null) { ISite site = GetSite(component); IComponentChangeService changeService = null; // Announce that we are about to change this component // if (site != null) { changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found"); } if (changeService != null) { try { changeService.OnComponentChanging(component, this); } catch (CheckoutException coEx) { if (coEx == CheckoutException.Canceled) { return; } throw coEx; } } bool shadowed = false; if (site != null && site.DesignMode) { IDictionaryService dict = (IDictionaryService)site.GetService(typeof(IDictionaryService)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || dict != null, "IDictionaryService not found"); if (dict != null) { Delegate del = (Delegate)dict.GetValue(this); del = Delegate.Remove(del, value); dict.SetValue(this, del); shadowed = true; } } if (!shadowed) { removeMethod.Invoke(component, new object[] { value}); } // Now notify the change service that the change was successful. // if (changeService != null) { changeService.OnComponentChanged(component, this, null, value); } } } /* The following code has been removed to fix FXCOP violations. The code is left here incase it needs to be resurrected in the future. /// /// This is a shortcut main constructor for an DebugReflectEventDescriptor with one attribute. /// public DebugReflectEventDescriptor(Type componentClass, string name, Type type, MethodInfo addMethod, MethodInfo removeMethod) : this(componentClass, name, type, (Attribute[]) null) { this.addMethod = addMethod; this.removeMethod = removeMethod; this.filledMethods = true; } /// /// This is a shortcut main constructor for an DebugReflectEventDescriptor with one attribute. /// public DebugReflectEventDescriptor(Type componentClass, string name, Type type) : this(componentClass, name, type, (Attribute[]) null) { } /// /// This is a shortcut main constructor for an DebugReflectEventDescriptor with two attributes. /// public DebugReflectEventDescriptor(Type componentClass, string name, Type type, Attribute a1) : this(componentClass, name, type, new Attribute[] {a1}) { } /// /// This is a shortcut main constructor for an DebugReflectEventDescriptor with two attributes. /// public DebugReflectEventDescriptor(Type componentClass, string name, Type type, Attribute a1, Attribute a2) : this(componentClass, name, type, new Attribute[] {a1, a2}) { } /// /// This is a shortcut main constructor for an DebugReflectEventDescriptor with three attributes. /// public DebugReflectEventDescriptor(Type componentClass, string name, Type type, Attribute a1, Attribute a2, Attribute a3) : this(componentClass, name, type, new Attribute[] {a1, a2, a3}) { } /// /// This is a shortcut main constructor for an DebugReflectEventDescriptor with four attributes. /// public DebugReflectEventDescriptor(Type componentClass, string name, Type type, Attribute a1, Attribute a2, Attribute a3, Attribute a4) : this(componentClass, name, type, new Attribute[] {a1, a2, a3, a4}) { } /// /// This constructor takes an existing DebugReflectEventDescriptor and modifies it by merging in the /// passed-in attributes. /// public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute[] attributes) : this(oldReflectEventDescriptor.ComponentType, oldReflectEventDescriptor, attributes) { } /// /// This is a shortcut constructor that takes an existing DebugReflectEventDescriptor and one attribute to /// merge in. /// public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute a1) : this(oldReflectEventDescriptor, new Attribute[] { a1}) { } /// /// This is a shortcut constructor that takes an existing DebugReflectEventDescriptor and two attributes to /// merge in. /// public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute a1, Attribute a2) : this(oldReflectEventDescriptor, new Attribute[] { a1,a2}) { } /// /// This is a shortcut constructor that takes an existing DebugReflectEventDescriptor and three attributes to /// merge in. /// public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute a1, Attribute a2, Attribute a3) : this(oldReflectEventDescriptor, new Attribute[] { a1,a2,a3}) { } /// /// This is a shortcut constructor that takes an existing DebugReflectEventDescriptor and four attributes to /// merge in. /// public DebugReflectEventDescriptor(EventDescriptor oldReflectEventDescriptor, Attribute a1, Attribute a2, Attribute a3, Attribute a4) : this(oldReflectEventDescriptor, new Attribute[] { a1,a2,a3,a4}) { } */ } } #endif