using ANX.Framework.VisualStudio.Converters; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Drawing.Design; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace ANX.Framework.VisualStudio.PropertyDescriptors { public class ParameterDescriptor : PropertyDescriptor { /// /// The proxy should only run on the build appDomain, because we are wrapping the custom PropertyDescriptors of the referenced assemblies. /// private class Proxy : MarshalByRefObject, IProxy { PropertyDescriptor innerDescriptor; //Inside a proxy, we don't need to use WrappedConverters because we work on the same domain as them. IProxy converterProxy; IProxy componentConverterProxy; TypeConverter converter; TypeConverter componentConverter; MethodInfo onValueChanged; PropertyInfo nameHashCode; MethodInfo createAttributeCollection; MethodInfo fillAttributes; PropertyInfo attributeArray; MethodInfo getInvocationTarget; public void Initialize(PropertyDescriptor descriptor, IProxy converterProxy, IProxy componentConverterProxy) { this.innerDescriptor = descriptor; this.converterProxy = converterProxy; this.converter = new WrappedConverter(this.converterProxy); this.componentConverterProxy = componentConverterProxy; this.componentConverter = new WrappedConverter(this.componentConverterProxy); Type type = descriptor.GetType(); onValueChanged = type.GetMethod("OnValueChanged", BindingFlags.Instance | BindingFlags.NonPublic, null, new [] { typeof(object), typeof(EventArgs) }, null); nameHashCode = type.GetProperty("NameHashCode", BindingFlags.Instance | BindingFlags.NonPublic, null, typeof(int), Type.EmptyTypes, null); createAttributeCollection = type.GetMethod("CreateAttributeCollection", BindingFlags.Instance | BindingFlags.NonPublic); fillAttributes = type.GetMethod("FillAttributes", BindingFlags.Instance | BindingFlags.NonPublic, null, new [] { typeof(IList) }, null); attributeArray = type.GetProperty("AttributeArray", BindingFlags.Instance | BindingFlags.NonPublic, null, typeof(Attribute[]), Type.EmptyTypes, null); getInvocationTarget = type.GetMethod("GetInvocationTarget", BindingFlags.Instance | BindingFlags.NonPublic, null, new [] { typeof(Type), typeof(object) }, null); } public override object InitializeLifetimeService() { return null; } public IProxy ConverterProxy { get { return converterProxy; } } public IProxy ComponentConverterProxy { get { return componentConverterProxy; } } public bool CanResetValue(string component) { return innerDescriptor.CanResetValue(componentConverter.ConvertFromString(component)); } public string GetValue(string component) { return converter.ConvertToString(innerDescriptor.GetValue(componentConverter.ConvertFromString(component))); } public bool IsReadOnly { get { return innerDescriptor.IsReadOnly; } } public void ResetValue(string component) { innerDescriptor.ResetValue(componentConverter.ConvertFromString(component)); } public void SetValue(string component, string value) { innerDescriptor.SetValue(componentConverter.ConvertFromString(component), converter.ConvertFromString(value)); } public bool ShouldSerializeValue(string component) { return innerDescriptor.ShouldSerializeValue(componentConverter.ConvertFromString(component)); } public void AddValueChanged(string component, EventHandler handler) { innerDescriptor.AddValueChanged(componentConverter.ConvertFromString(component), handler); } public string Category { get { return innerDescriptor.Category; } } public TypeConverter Converter { get { return converter; } } public string Description { get { return innerDescriptor.Description; } } public bool DesignTimeOnly { get { return innerDescriptor.DesignTimeOnly; } } public string DisplayName { get { return innerDescriptor.DisplayName; } } public bool IsBrowsable { get { return innerDescriptor.IsBrowsable; } } public bool IsLocalizable { get { return innerDescriptor.IsLocalizable; } } public string Name { get { return innerDescriptor.Name; } } public bool SupportsChangeEvents { get { return innerDescriptor.SupportsChangeEvents; } } public void RemoveValueChanged(string component, EventHandler handler) { innerDescriptor.RemoveValueChanged(componentConverter.ConvertFromString(component), handler); } public int NameHashCode { get { return (int)nameHashCode.GetValue(innerDescriptor, null); } } public void OnValueChanged(string component) { onValueChanged.Invoke(innerDescriptor, new object[] { componentConverter.ConvertFromString(component), EventArgs.Empty }); } public string GetInvocationTarget(Type type, string instance) { return this.converter.ConvertToString(getInvocationTarget.Invoke(innerDescriptor, new object[] { type, converter.ConvertFromString(instance) })); } public object GetEditor(Type editorBaseType) { var editor = innerDescriptor.GetEditor(editorBaseType); if (editor == null || !(editor is UITypeEditor)) return null; //Create an outgoing proxy for the custom editor. return UITypeEditorWrapper.CreateProxy((UITypeEditor)editor, converterProxy); } public IProxy[] GetChildProperties(string instance, Attribute[] filter) { var descriptors = innerDescriptor.GetChildProperties(converter.ConvertFromString(instance), filter); List result = new List(); foreach (PropertyDescriptor descriptor in descriptors) { result.Add(ParameterDescriptor.CreateProxy(descriptor, WrappedConverter.CreateProxy(descriptor.Converter.GetType(), descriptor.PropertyType), this.ConverterProxy)); } return result.ToArray(); } public void FillAttributes(List attributeList) { fillAttributes.Invoke(innerDescriptor, new object[] { attributeList }); } public List CreateAttributeCollection() { return FilterAttributes((AttributeCollection)createAttributeCollection.Invoke(innerDescriptor, new object[0])); } public Attribute[] Attributes { get { return FilterAttributes(innerDescriptor.Attributes).ToArray(); } } public Attribute[] AttributeArray { get { return FilterAttributes((Attribute[])attributeArray.GetValue(innerDescriptor)).ToArray(); } set { attributeArray.SetValue(innerDescriptor, value); } } public object OriginalInstance { get { return innerDescriptor; } } public Type WrapperType { get { return typeof(ParameterDescriptor); } } } private static List FilterAttributes(IEnumerable attributes) { List result = new List(); if (attributes != null) { foreach (var attribute in attributes) { if (attribute is Attribute && attribute.GetType().IsSerializable) { result.Add((Attribute)attribute); } } } return result; } Proxy proxy; WrappedConverter converter; WrappedConverter componentConverter; //This method can be called inside the visual studi appDomain for the contentProcessorParameterDescriptor or a proxy to this wrapper or //it can be called from the build appDomain to wrap a custom PropertyDescriptor. public static IProxy CreateProxy(PropertyDescriptor descriptor, IProxy converterProxy, IProxy componentConverterProxy) { if (descriptor == null) throw new ArgumentNullException("descriptor"); if (converterProxy == null) throw new ArgumentNullException("converterProxy"); if (componentConverterProxy == null) throw new ArgumentNullException("componentConverterProxy"); Proxy proxy = new Proxy(); proxy.Initialize(descriptor, converterProxy, componentConverterProxy); return proxy; } public ParameterDescriptor(IProxy proxy) : base(((Proxy)proxy).Name, ((Proxy)proxy).Attributes) { this.proxy = (Proxy)proxy; this.converter = new WrappedConverter(this.proxy.ConverterProxy); this.componentConverter = new WrappedConverter(this.proxy.ComponentConverterProxy); } public ParameterDescriptor(PropertyDescriptor descriptor, WrappedConverter converter, WrappedConverter componentConverter) : base(descriptor) { if (converter == null) throw new ArgumentNullException("converter"); if (componentConverter == null) throw new ArgumentNullException("componentConverter"); if (AppDomain.CurrentDomain.IsBuildAppDomain()) throw new InvalidOperationException("WrappedConverter constructor with string parameters can only be called from the main appDomain."); this.converter = converter; this.componentConverter = componentConverter; this.proxy = new Proxy(); this.proxy.Initialize(descriptor, converter.ProxyInstance, componentConverter.ProxyInstance); } public override bool CanResetValue(object component) { return proxy.CanResetValue(componentConverter.ConvertToString(component)); } public override Type ComponentType { get { return typeof(string); } } public override object GetValue(object component) { return (string)proxy.GetValue((string)component); } public override bool IsReadOnly { get { return proxy.IsReadOnly; } } public override Type PropertyType { get { return typeof(string); } } public override void ResetValue(object component) { proxy.ResetValue(componentConverter.ConvertToString(component)); } public override void SetValue(object component, object value) { proxy.SetValue(componentConverter.ConvertToString(component), converter.ConvertToString(value)); } public override bool ShouldSerializeValue(object component) { return proxy.ShouldSerializeValue(componentConverter.ConvertToString(component)); } public override void AddValueChanged(object component, EventHandler handler) { base.AddValueChanged(component, handler); } public override string Category { get { return proxy.Category; } } public override TypeConverter Converter { get { return this.converter; } } public override string Description { get { return proxy.Description; } } public override bool DesignTimeOnly { get { return proxy.DesignTimeOnly; } } public override string DisplayName { get { return proxy.DisplayName; } } public override bool IsBrowsable { get { return proxy.IsBrowsable; } } public override bool IsLocalizable { get { return proxy.IsLocalizable; } } public override string Name { get { return proxy.Name; } } public override bool SupportsChangeEvents { get { return proxy.SupportsChangeEvents; } } public override void RemoveValueChanged(object component, EventHandler handler) { proxy.RemoveValueChanged(componentConverter.ConvertToString(component), handler); } protected override int NameHashCode { get { return proxy.NameHashCode; } } protected override void OnValueChanged(object component, EventArgs e) { proxy.OnValueChanged(componentConverter.ConvertToString(component)); } protected override object GetInvocationTarget(Type type, object instance) { return converter.ConvertFromString(proxy.GetInvocationTarget(type, converter.ConvertToString(instance))); } public override object GetEditor(Type editorBaseType) { if (editorBaseType != typeof(UITypeEditor)) return null; var editor = (UITypeEditor)proxy.GetEditor(editorBaseType); if (editor == null) return null; return new UITypeEditorWrapper(editor, this.converter); } public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) { //Get the names of the properties to keep the sorting. List names = new List(); List parameters = new List(); foreach (var propertyProxy in proxy.GetChildProperties(converter.ConvertToString(instance), FilterAttributes(filter).ToArray())) { var parameter = new ParameterDescriptor(propertyProxy); names.Add(parameter.Name); parameters.Add(parameter); } var result = new PropertyDescriptorCollection(parameters.ToArray()); //TODO: support using an IComparer. return result.Sort(names.ToArray()); } protected override void FillAttributes(System.Collections.IList attributeList) { proxy.FillAttributes(FilterAttributes(attributeList)); } protected override AttributeCollection CreateAttributeCollection() { return new AttributeCollection(proxy.CreateAttributeCollection().ToArray()); } public override AttributeCollection Attributes { get { return new AttributeCollection(proxy.Attributes); } } protected override Attribute[] AttributeArray { get { return proxy.AttributeArray; } set { proxy.AttributeArray = FilterAttributes(value).ToArray(); } } } }