File: winforms\Managed\System\WinForms\ComponentModel\COM2Interop\ComNativeDescriptor.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="ComNativeDescriptor.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
 
namespace System.Windows.Forms.ComponentModel.Com2Interop {
    using System.Runtime.Serialization.Formatters;
    using System.Runtime.Remoting;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System;
    using System.Collections;    
    using System.Collections.Generic;    
    using System.ComponentModel.Design;
    using Microsoft.Win32;
 
    /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor"]/*' />
    /// <devdoc>
    ///     Top level mapping layer between COM Object and TypeDescriptor.
    ///
    /// </devdoc>
    internal class ComNativeDescriptor : TypeDescriptionProvider {
      
        private static ComNativeDescriptor handler = null;
 
        private AttributeCollection staticAttrs = new AttributeCollection(new Attribute[]{BrowsableAttribute.Yes, DesignTimeVisibleAttribute.No});
 
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.nativeProps"]/*' />
        /// <devdoc>
        /// Our collection of Object managers (Com2Properties) for native properties
        /// </devdoc>
        private WeakHashtable  nativeProps = new WeakHashtable();
        
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.extendedBrowsingHandlers"]/*' />
        /// <devdoc>
        /// Our collection of browsing handlers, which are stateless and shared across objects.
        /// </devdoc>
        private Hashtable         extendedBrowsingHandlers = new Hashtable();
        
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.clearCount"]/*' />
        /// <devdoc>
        /// We increment this every time we look at an Object, at specified
        /// intervals, we run through the properies list to see if we should
        /// delete any.
        /// </devdoc>
        private int               clearCount  = 0;
        private const  int        CLEAR_INTERVAL = 25;
 
        internal static ComNativeDescriptor Instance {
            get {
                if (handler == null) {
                    handler = new ComNativeDescriptor();
                }
                return handler;
            }
        }   
 
 
        [
            System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")
        ]
        // called via reflection for AutomationExtender stuff. Don't delete!
        //
        public static object GetNativePropertyValue(object component, string propertyName, ref bool succeeded) 
        {
            return Instance.GetPropertyValue(component, propertyName, ref succeeded);
        }
 
 
        /// <devdoc>
        ///     This method returns a custom type descriptor for the given type / object.  
        ///     The objectType parameter is always valid, but the instance parameter may 
        ///     be null if no instance was passed to TypeDescriptor.  The method should 
        ///     return a custom type descriptor for the object.  If the method is not 
        ///     interested in providing type information for the object it should 
        ///     return null.
        ///
        ///     This method is prototyped as virtual, and by default returns null 
        ///     if no parent provider was passed.  If a parent provider was passed, 
        ///     this method will invoke the parent provider's GetTypeDescriptor 
        ///     method.
        /// </devdoc>
        public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
        {
            return new ComTypeDescriptor(this, instance);
        }
 
        internal string GetClassName(Object component) {
 
            string name = null;
 
            // does IVsPerPropretyBrowsing supply us a name?
            if (component is NativeMethods.IVsPerPropertyBrowsing) {
               int hr = ((NativeMethods.IVsPerPropertyBrowsing)component).GetClassName(ref name);
               if (NativeMethods.Succeeded(hr) && name != null) {
                  return name;
               }
               // otherwise fall through...
            }
 
            UnsafeNativeMethods.ITypeInfo  pTypeInfo = Com2TypeInfoProcessor.FindTypeInfo(component, true);
 
            if (pTypeInfo == null) {
                //Debug.Fail("The current component failed to return an ITypeInfo");
                return "";
            }
 
            if (pTypeInfo != null) {
                string desc = null;
                try {
                    pTypeInfo.GetDocumentation(NativeMethods.MEMBERID_NIL, ref name, ref desc, null, null);
                    
                    // strip the leading underscores
                    while (name != null && name.Length > 0 && name[0] == '_') {
                        name = name.Substring(1);
                    }
                    return name;
                }
                catch {
                }
            }
            return "";
        }
        
        internal TypeConverter GetConverter(Object component) {
            return TypeDescriptor.GetConverter(typeof(IComponent));
        }
        
        internal Object GetEditor(Object component, Type baseEditorType) {
            return TypeDescriptor.GetEditor(component.GetType(), baseEditorType);
        }
 
        internal string GetName(Object component) {
 
            if (!(component is UnsafeNativeMethods.IDispatch)) {
                return "";
            }
            
            int dispid = Com2TypeInfoProcessor.GetNameDispId((UnsafeNativeMethods.IDispatch)component);
            if (dispid != NativeMethods.MEMBERID_NIL) {
                bool success = false;
                object value = GetPropertyValue(component, dispid, ref success);
                
                if (success && value != null) {
                    return value.ToString();
                }
            }
            return "";
        }
 
        internal Object GetPropertyValue(Object component, string propertyName, ref bool succeeded) {
 
            if (!(component is UnsafeNativeMethods.IDispatch)) {
                return null;
            }
 
            UnsafeNativeMethods.IDispatch iDispatch = (UnsafeNativeMethods.IDispatch)component;
            string[] names = new string[]{propertyName};
            int[] dispid = new int[1];
            dispid[0] = NativeMethods.DISPID_UNKNOWN;
            Guid g = Guid.Empty;
            try {
               int hr = iDispatch.GetIDsOfNames(ref g, names, 1, SafeNativeMethods.GetThreadLCID(), dispid);
   
               if (dispid[0] == NativeMethods.DISPID_UNKNOWN || NativeMethods.Failed(hr)) {
                   return null;
               }
            }
            catch {
                return null;   
            }
            return GetPropertyValue(component, dispid[0], ref succeeded);
        }
 
        internal Object GetPropertyValue(Object component, int dispid, ref bool succeeded) {
            if (!(component is UnsafeNativeMethods.IDispatch)) {
                return null;
            }
            Object[] pVarResult = new Object[1];
            if (GetPropertyValue(component, dispid, pVarResult) == NativeMethods.S_OK) {
                succeeded = true;
                return pVarResult[0];
            }
            else {
                succeeded = false;
                return null;
            }
        }
 
        internal int GetPropertyValue(Object component, int dispid, Object[] retval) {
            if (!(component is UnsafeNativeMethods.IDispatch)) {
                return NativeMethods.E_NOINTERFACE;
            }
            UnsafeNativeMethods.IDispatch iDispatch = (UnsafeNativeMethods.IDispatch)component;
            try {
                Guid g = Guid.Empty;
                NativeMethods.tagEXCEPINFO pExcepInfo = new NativeMethods.tagEXCEPINFO();
                int hr;
 
                try{
 
                   hr = iDispatch.Invoke(dispid,
                                             ref g,
                                             SafeNativeMethods.GetThreadLCID(),
                                             NativeMethods.DISPATCH_PROPERTYGET,
                                             new NativeMethods.tagDISPPARAMS(),
                                             retval,
                                             pExcepInfo, null);
 
                   /*if (hr != NativeMethods.S_OK){
                     Com2PropertyDescriptor.PrintExceptionInfo(pExcepInfo);
 
                   } */
                   if (hr == NativeMethods.DISP_E_EXCEPTION) {
                       hr = pExcepInfo.scode;
                   }
 
                }
                catch (ExternalException ex){
                    hr = ex.ErrorCode;
                }
                return hr;
            }
            catch {
                //Debug.Fail(e.ToString() + " " + component.GetType().GUID.ToString() + " " + component.ToString());
            }
            return NativeMethods.E_FAIL;
        }
 
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.IsNameDispId"]/*' />
        /// <devdoc>
        /// Checks if the given dispid matches the dispid that the Object would like to specify
        /// as its identification proeprty (Name, ID, etc).
        /// </devdoc>
        internal bool IsNameDispId(Object obj, int dispid) {
            if (obj == null || !obj.GetType().IsCOMObject) {
                return false;
            }
            return dispid == Com2TypeInfoProcessor.GetNameDispId((UnsafeNativeMethods.IDispatch)obj);
        }
 
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.CheckClear"]/*' />
        /// <devdoc>
        /// Checks all our property manages to see if any have become invalid.
        /// </devdoc>
        private void CheckClear(Object component) {
            
            // walk the list every so many calls
            if ((++clearCount % CLEAR_INTERVAL) == 0) {
            
               lock(nativeProps) {
                   clearCount = 0;   
 
                   List<object> disposeList = null;
                   Com2Properties entry;
 
                   // first walk the list looking for items that need to be
                   // cleaned out.
                   //
                   foreach(DictionaryEntry de in nativeProps) {

                        entry = de.Value as Com2Properties;
 
                        if (entry != null && entry.TooOld) {
                            if (disposeList == null) {
                                disposeList = new List<object>(3);
                            }
                            disposeList.Add(de.Key);
                        }
                   }
 
                   // now run through the ones that are dead and dispose them.
                   // there's going to be a very small number of these.
                   //
                   if (disposeList != null) {
                       object oldKey; 
                       for (int i = disposeList.Count - 1; i >= 0; i--) {
                          oldKey = disposeList[i]; 
                          entry = nativeProps[oldKey] as Com2Properties;
                        
                          if (entry != null) {
                               entry.Disposed -= new EventHandler(OnPropsInfoDisposed);
                               entry.Dispose();
                               nativeProps.Remove(oldKey);
                          }
                       }
                   }
                }
            }
        }
 
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.GetPropsInfo"]/*' />
        /// <devdoc>
        /// Gets the properties manager for an Object.
        /// </devdoc>
        private Com2Properties GetPropsInfo(Object component) {
            // check caches if necessary
            //
            CheckClear(component);
 
            // Get the property info Object
            //
            Com2Properties propsInfo = (Com2Properties)nativeProps[component];
            
            // if we dont' have one, create one and set it up
            //
            if (propsInfo == null || !propsInfo.CheckValid()) {
                propsInfo = Com2TypeInfoProcessor.GetProperties(component);
                if (propsInfo != null) {                    
                    propsInfo.Disposed += new EventHandler(OnPropsInfoDisposed);                    
                    nativeProps.SetWeak(component, propsInfo);
                    propsInfo.AddExtendedBrowsingHandlers(extendedBrowsingHandlers);
                }
            }
            return propsInfo;
        }
 
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.GetAttributes"]/*' />
        /// <devdoc>
        /// Got attributes?
        /// </devdoc>
        internal AttributeCollection GetAttributes(Object component) {
 
            ArrayList attrs = new ArrayList();
 
            if (component is NativeMethods.IManagedPerPropertyBrowsing) {
                Object[] temp = Com2IManagedPerPropertyBrowsingHandler.GetComponentAttributes((NativeMethods.IManagedPerPropertyBrowsing)component, NativeMethods.MEMBERID_NIL);
                for (int i = 0; i < temp.Length; ++i) {
                    attrs.Add(temp[i]);
                }
            }
            
            if (Com2ComponentEditor.NeedsComponentEditor(component)) {
                EditorAttribute a = new EditorAttribute(typeof(Com2ComponentEditor), typeof(ComponentEditor));
                attrs.Add(a);
            }
 
            if (attrs == null || attrs.Count == 0) {
                return staticAttrs;
            }
            else {
                Attribute[] temp = new Attribute[attrs.Count];
                attrs.CopyTo(temp, 0);
                return new AttributeCollection(temp);
            }
        }
 
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.GetDefaultProperty"]/*' />
        /// <devdoc>
        /// Default Property, please
        /// </devdoc>
        internal PropertyDescriptor GetDefaultProperty(Object component) {
            CheckClear(component);
 
            Com2Properties propsInfo = GetPropsInfo(component);
            if (propsInfo != null) {
                return propsInfo.DefaultProperty;
            }
            return null;
        }
 
        internal EventDescriptorCollection GetEvents(Object component) {
            return new EventDescriptorCollection(null);
        }
 
        internal EventDescriptorCollection GetEvents(Object component, Attribute[] attributes) {
            return new EventDescriptorCollection(null);
        }
 
        internal EventDescriptor GetDefaultEvent(Object component) {
            return null;
        }
 
        /// <devdoc>
        /// Props!
        /// </devdoc>
        [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")]
        internal PropertyDescriptorCollection GetProperties(Object component, Attribute[] attributes) {
            
            Com2Properties propsInfo = GetPropsInfo(component);
 
            if (propsInfo == null) {
                return PropertyDescriptorCollection.Empty;
            }
 
            try {
                propsInfo.AlwaysValid = true;
                PropertyDescriptor[] props = propsInfo.Properties;
                
                //Debug.Assert(propDescList.Count > 0, "Didn't add any properties! (propInfos=0)");
                return new PropertyDescriptorCollection(props);
            }
            finally {
                propsInfo.AlwaysValid = false;
            }
        }
 
        /// <devdoc>
        /// Fired when the property info gets disposed.
        /// </devdoc>        
        private void OnPropsInfoDisposed(object sender, EventArgs e) {

            Com2Properties propsInfo = sender as Com2Properties;
 
            if (propsInfo != null) {
                propsInfo.Disposed -= new EventHandler(OnPropsInfoDisposed);
 
                lock(nativeProps) {
 
                    // find the key
                    object key = propsInfo.TargetObject;
 
                    if (key == null && nativeProps.ContainsValue(propsInfo)) {

                        // need to find it - the target object has probably been cleaned out
                        // of the Com2Properties object already, so we run through the
                        // hashtable looking for the value, so we know what key to remove.
                        //
                        foreach (DictionaryEntry de in nativeProps) {

                            if (de.Value == propsInfo) {
                                key = de.Key;
                                break;                                    
                            }
                        }
 
                        if (key == null)  {
                            Debug.Fail("Failed to find Com2 properties key on dispose.");
                            return;
                        }
                    }
                    
                    nativeProps.Remove(key);
               }
            }
        }
 
        /// <include file='doc\ComNativeDescriptor.uex' path='docs/doc[@for="ComNativeDescriptor.ResolveVariantTypeConverterAndTypeEditor"]/*' />
        /// <devdoc>
        /// Looks at at value's type and creates an editor based on that.  We use this to decide which editor to use
        /// for a generic variant.
        /// </devdoc>
        internal static void ResolveVariantTypeConverterAndTypeEditor(Object propertyValue, ref TypeConverter currentConverter, Type editorType, ref Object currentEditor) {
 
            Object curValue = propertyValue;
            if (curValue != null && curValue != null && !Convert.IsDBNull(curValue)){
                  Type t = curValue.GetType();
                  TypeConverter subConverter = TypeDescriptor.GetConverter(t);
                  if (subConverter != null && subConverter.GetType() != typeof(TypeConverter)){
                     currentConverter = subConverter;
                  }
                  Object subEditor = TypeDescriptor.GetEditor(t, editorType);
                  if (subEditor != null) {
                     currentEditor = subEditor;
                  }
            }
        }
 
        /// <devdoc>
        ///     This type descriptor sits on top of a ComNativeDescriptor
        /// </devdoc>
        private sealed class ComTypeDescriptor : ICustomTypeDescriptor
        {
            private ComNativeDescriptor         _handler;
            private object                      _instance;
 
            /// <devdoc>
            ///     Creates a new WalkingTypeDescriptor.
            /// </devdoc>
            internal ComTypeDescriptor(ComNativeDescriptor handler, object instance)
            {
                _handler = handler;
                _instance = instance;
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            AttributeCollection ICustomTypeDescriptor.GetAttributes()
            {
                return _handler.GetAttributes(_instance);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            string ICustomTypeDescriptor.GetClassName()
            {
                return _handler.GetClassName(_instance);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            string ICustomTypeDescriptor.GetComponentName()
            {
                return _handler.GetName(_instance);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            TypeConverter ICustomTypeDescriptor.GetConverter()
            {
                return _handler.GetConverter(_instance);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
            {
                return _handler.GetDefaultEvent(_instance);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
            {
                return _handler.GetDefaultProperty(_instance);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
            {
                return _handler.GetEditor(_instance, editorBaseType);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
            {
                return _handler.GetEvents(_instance);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
            {
                return _handler.GetEvents(_instance, attributes);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
            {
                return _handler.GetProperties(_instance, null);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
            {
                return _handler.GetProperties(_instance, attributes);
            }
 
            /// <devdoc>
            ///     ICustomTypeDescriptor implementation.
            /// </devdoc>
            object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
            {
                return _instance;
            }
        }
    }
}