COM2TypeInfoProcessor.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / WinForms / Managed / System / WinForms / ComponentModel / COM2Interop / COM2TypeInfoProcessor.cs / 1305376 / COM2TypeInfoProcessor.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Windows.Forms.ComponentModel.Com2Interop { 
    using System.Runtime.Remoting; 
    using System.Runtime.InteropServices;
    using System.ComponentModel; 
    using System.Diagnostics;
    using System;
    using System.Windows.Forms;
    using System.ComponentModel.Design; 
    using Microsoft.Win32;
    using System.Collections; 
    using Hashtable = System.Collections.Hashtable; 

    using System.Reflection.Emit; 
    using System.Reflection;
    using System.Threading;
    using System.Globalization;
 

    ///  
    ///  
    /// This is the main worker class of Com2 property interop. It takes an IDispatch Object
    /// and translates it's ITypeInfo into Com2PropertyDescriptor objects that are understandable 
    /// by managed code.
    ///
    /// This class only knows how to process things that are natively in the typeinfo.  Other property
    /// information such as IPerPropertyBrowsing is handled elsewhere. 
    /// 
    internal class Com2TypeInfoProcessor { 
 
        #if DEBUG
        private static TraceSwitch DbgTypeInfoProcessorSwitch = new TraceSwitch("DbgTypeInfoProcessor", "Com2TypeInfoProcessor: debug Com2 type info processing"); 
        #else
        private static TraceSwitch DbgTypeInfoProcessorSwitch;
        #endif
 
        private Com2TypeInfoProcessor() {
        } 
 
        private static ModuleBuilder moduleBuilder = null;
 
        private static ModuleBuilder ModuleBuilder {
            get {
               if (moduleBuilder == null) {
                  AppDomain currentDomain =  Thread.GetDomain(); 
                  AssemblyName assemblyName = new AssemblyName();
                  assemblyName.Name = "COM2InteropEmit"; 
                  AssemblyBuilder aBuilder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
                  moduleBuilder = aBuilder.DefineDynamicModule("COM2Interop.Emit");
               } 
               return moduleBuilder;
            }
        }
 
        private static Hashtable builtEnums;
        private static Hashtable processedLibraries; 
 

        ///  
        /// 
        /// Given an Object, this attempts to locate its type ifo
        /// 
        public static UnsafeNativeMethods.ITypeInfo FindTypeInfo(Object obj, bool wantCoClass) { 
            UnsafeNativeMethods.ITypeInfo  pTypeInfo = null;
 
            // this is kind of odd.  What's going on here is that 
            // if we want the CoClass (e.g. for the interface name),
            // we need to look for IProvideClassInfo first, then 
            // look for the typeinfo from the IDispatch.
            // In the case of many OleAut32 operations, the CoClass
            // doesn't have the interface members on it, although
            // in the shell it usually does, so 
            // we need to re-order the lookup if we _actually_ want
            // the CoClass if it's available. 
            // 

            for (int i = 0; pTypeInfo == null && i < 2; i++) { 

                  if (wantCoClass == (i == 0)){
                        if (obj is NativeMethods.IProvideClassInfo) {
                            NativeMethods.IProvideClassInfo pProvideClassInfo = (NativeMethods.IProvideClassInfo)obj; 
                            try {
                                pTypeInfo = pProvideClassInfo.GetClassInfo(); 
                            } 
                            catch {
                            } 
                        }
                  }
                  else {
                       if (obj is UnsafeNativeMethods.IDispatch) { 
                            UnsafeNativeMethods.IDispatch iDispatch = (UnsafeNativeMethods.IDispatch)obj;
                            try { 
                                pTypeInfo = iDispatch.GetTypeInfo(0, SafeNativeMethods.GetThreadLCID()); 
                            }
                            catch { 
                            }
                       }
                  }
 
            }
            return pTypeInfo; 
        } 

 
        /// 
        /// 
        /// Given an Object, this attempts to locate its type info. If it implementes IProvideMultipleClassInfo
        /// all available type infos will be returned, otherwise the primary one will be alled. 
        /// 
        public static UnsafeNativeMethods.ITypeInfo[] FindTypeInfos(Object obj, bool wantCoClass){ 
 
            UnsafeNativeMethods.ITypeInfo[] typeInfos = null;
            int n = 0; 
            UnsafeNativeMethods.ITypeInfo temp = null;

            if (obj is NativeMethods.IProvideMultipleClassInfo) {
               NativeMethods.IProvideMultipleClassInfo pCI = (NativeMethods.IProvideMultipleClassInfo)obj; 
               if (!NativeMethods.Succeeded(pCI.GetMultiTypeInfoCount(ref n)) || n == 0) {
                  n = 0; 
               } 

               if (n > 0){ 
                  typeInfos = new UnsafeNativeMethods.ITypeInfo[n];

                  for (int i = 0; i < n; i++){
                     if (NativeMethods.Failed(pCI.GetInfoOfIndex(i, 1 /*MULTICLASSINFO_GETTYPEINFO*/, ref temp, 0, 0, IntPtr.Zero, IntPtr.Zero))){ 
                        continue;
                     } 
                     Debug.Assert(temp != null, "IProvideMultipleClassInfo::GetInfoOfIndex returned S_OK for ITypeInfo index " + i + ", this is a issue in the object that's being browsed, NOT the property browser."); 
                     typeInfos[i] = temp;
                  } 
               }
            }

            if (typeInfos == null || typeInfos.Length == 0){ 
               temp = FindTypeInfo(obj, wantCoClass);
               if (temp != null) { 
                   typeInfos = new UnsafeNativeMethods.ITypeInfo[]{temp}; 
               }
            } 

            return typeInfos;
        }
 
        /// 
        ///  
        /// Retrieve the dispid of the property that we are to use as the name 
        /// member.  In this case, the grid will put parens around the name.
        ///  
        public static int GetNameDispId(UnsafeNativeMethods.IDispatch obj){
            int dispid = NativeMethods.DISPID_UNKNOWN;
            string[] names = null;
 
            ComNativeDescriptor cnd = ComNativeDescriptor.Instance;
            bool succeeded = false; 
 
            // first try to find one with a valid value
            cnd.GetPropertyValue(obj, "__id", ref succeeded); 

            if (succeeded) {
               names = new string[]{"__id"};
            } 
            else {
               cnd.GetPropertyValue(obj, NativeMethods.ActiveX.DISPID_Name, ref succeeded); 
               if (succeeded) { 
                  dispid = NativeMethods.ActiveX.DISPID_Name;
               } 
               else {
                  cnd.GetPropertyValue(obj, "Name", ref succeeded);
                  if (succeeded) {
                     names = new string[]{"Name"}; 
                  }
               } 
            } 

            // now get the dispid of the one that worked... 
            if (names != null) {
               int[] pDispid = new int[]{NativeMethods.DISPID_UNKNOWN};
               Guid g = Guid.Empty;
               int hr = obj.GetIDsOfNames(ref g, names, 1, SafeNativeMethods.GetThreadLCID(), pDispid); 
               if (NativeMethods.Succeeded(hr)){
 
                  dispid = pDispid[0]; 
               }
            } 

            return dispid;
        }
 

        ///  
        ///  
        /// Gets the properties for a given Com2 Object.  The returned Com2Properties
        /// Object contains the properties and relevant data about them. 
        /// 
        public static Com2Properties GetProperties(Object obj) {

            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties"); 

            if (obj == null || !Marshal.IsComObject(obj)) { 
                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties returning null: Object is not a com Object"); 
                return null;
            } 

            UnsafeNativeMethods.ITypeInfo[] typeInfos = FindTypeInfos(obj, false);

            // oops, looks like this guy doesn't surface any type info 
            // this is okay, so we just say it has no props
            if (typeInfos == null || typeInfos.Length == 0) { 
                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties :: Didn't get typeinfo"); 
                return null;
            } 


            int defaultProp = -1;
            int temp = -1; 
            ArrayList propList = new ArrayList();
            Guid[] typeGuids = new Guid[typeInfos.Length]; 
 
            for (int i = 0; i < typeInfos.Length; i++) {
               UnsafeNativeMethods.ITypeInfo ti = typeInfos[i]; 

               if (ti == null) {
                   continue;
               } 

               int[] versions = new int[2]; 
               Guid typeGuid = GetGuidForTypeInfo(ti, null, versions); 
               PropertyDescriptor[] props = null;
               bool dontProcess = typeGuid != Guid.Empty && processedLibraries != null && processedLibraries.Contains(typeGuid); 

               if (dontProcess) {
                    CachedProperties cp = (CachedProperties)processedLibraries[typeGuid];
 
                    if (versions[0] == cp.MajorVersion && versions[1] == cp.MinorVersion) {
                        props = cp.Properties; 
                        if (i == 0 && cp.DefaultIndex != -1) { 
                            defaultProp = cp.DefaultIndex;
                        } 
                    }
                    else {
                        dontProcess = false;
                    } 
               }
 
               if (!dontProcess) { 
                   props = InternalGetProperties(obj, ti, NativeMethods.MEMBERID_NIL, ref temp);
 
                   // only save the default property from the first type Info
                   if (i == 0 && temp != -1) {
                      defaultProp = temp;
                   } 

                   if (processedLibraries == null) { 
                        processedLibraries = new Hashtable(); 
                   }
 
                   if (typeGuid != Guid.Empty) {
                        processedLibraries[typeGuid] = new CachedProperties(props, i == 0 ? defaultProp : -1, versions[0], versions[1]);
                   }
               } 

               if (props != null){ 
                   propList.AddRange(props); 
               }
            } 

            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties : returning " + propList.Count.ToString(CultureInfo.InvariantCulture) + " properties");

            // done! 
            Com2PropertyDescriptor[] temp2 = new Com2PropertyDescriptor[propList.Count];
            propList.CopyTo(temp2, 0); 
 
            return new Com2Properties(obj, temp2, defaultProp);
        } 

        private static Guid GetGuidForTypeInfo(UnsafeNativeMethods.ITypeInfo typeInfo, StructCache structCache, int[] versions) {
            IntPtr pTypeAttr = IntPtr.Zero;
            int hr = typeInfo.GetTypeAttr(ref pTypeAttr); 
            if (!NativeMethods.Succeeded(hr)) {
                throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); 
            } 

            Guid g = Guid.Empty; 
            NativeMethods.tagTYPEATTR typeAttr = null;
            try {

 
                if (structCache == null) {
                    typeAttr = new NativeMethods.tagTYPEATTR(); 
                } 
                else {
                    typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR)); 
                }
                UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr);
                g = typeAttr.guid;
                if (versions != null) { 
                    versions[0] = typeAttr.wMajorVerNum;
                    versions[1] = typeAttr.wMinorVerNum; 
                } 
            }
            finally { 
                typeInfo.ReleaseTypeAttr(pTypeAttr);
                if (structCache != null && typeAttr != null) {
                    structCache.ReleaseStruct(typeAttr);
                } 
            }
 
            return g; 
        }
 

        /// 
        /// 
        /// Resolves a value type for a property from a TYPEDESC.  Value types can be 
        /// user defined, which and may be aliased into other type infos.  This function
        /// will recusively walk the ITypeInfos to resolve the type to a clr Type. 
        ///  
        private static Type GetValueTypeFromTypeDesc(NativeMethods.tagTYPEDESC typeDesc, UnsafeNativeMethods.ITypeInfo typeInfo, Object[] typeData, StructCache structCache) {
            IntPtr hreftype; 
            int hr = 0;

            switch ((NativeMethods.tagVT)typeDesc.vt) {
            default: 
                return VTToType((NativeMethods.tagVT)typeDesc.vt);
 
            case NativeMethods.tagVT.VT_UNKNOWN: 
            case NativeMethods.tagVT.VT_DISPATCH:
                // get the guid 
                typeData[0] = GetGuidForTypeInfo(typeInfo, structCache, null);

                // return the type
                return VTToType((NativeMethods.tagVT)typeDesc.vt); 

            case NativeMethods.tagVT.VT_USERDEFINED: 
                // we'll need to recurse into a user defined reference typeinfo 
                Debug.Assert(typeDesc.unionMember != IntPtr.Zero, "typeDesc doesn't contain an hreftype!");
                hreftype = typeDesc.unionMember; 
                break;

            case NativeMethods.tagVT.VT_PTR:
                // we'll need to recurse into a user defined reference typeinfo 
                Debug.Assert(typeDesc.unionMember != IntPtr.Zero, "typeDesc doesn't contain an refTypeDesc!");
                NativeMethods.tagTYPEDESC refTypeDesc = (NativeMethods.tagTYPEDESC)structCache.GetStruct(typeof(NativeMethods.tagTYPEDESC)); 
 
                try {
 
                    try {
                        //(tagTYPEDESC)Marshal.PtrToStructure(typeDesc.unionMember, typeof(tagTYPEDESC));
                        UnsafeNativeMethods.PtrToStructure(typeDesc.unionMember, refTypeDesc);
                    } 
                    catch {
                        // above is failing, why? 
                        refTypeDesc = new NativeMethods.tagTYPEDESC(); 
                        refTypeDesc.unionMember = (IntPtr)Marshal.ReadInt32(typeDesc.unionMember);
                        refTypeDesc.vt = Marshal.ReadInt16(typeDesc.unionMember, 4); 
                    }

                    if (refTypeDesc.vt == (int)NativeMethods.tagVT.VT_VARIANT) {
                        return VTToType((NativeMethods.tagVT)refTypeDesc.vt); 
                    }
                    hreftype = refTypeDesc.unionMember; 
                } 
                finally {
                    structCache.ReleaseStruct(refTypeDesc); 
                }
                break;
            }
 
            // get the reference type info
            UnsafeNativeMethods.ITypeInfo refTypeInfo = null; 
 
            hr = typeInfo.GetRefTypeInfo(hreftype, ref refTypeInfo);
            if (!NativeMethods.Succeeded(hr)) { 
                throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetRefTypeInfoFailed, hr), hr);
            }

            try { 
                // here is where we look at the next level type info.
                // if we get an enum, process it, otherwise we will recurse 
                // or get a dispatch. 
                //
                if (refTypeInfo != null) { 
                    IntPtr pRefTypeAttr = IntPtr.Zero;
                    hr = refTypeInfo.GetTypeAttr(ref pRefTypeAttr);

                    if (!NativeMethods.Succeeded(hr)) { 

                        throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); 
                    } 

                    NativeMethods.tagTYPEATTR refTypeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pRefTypeAttr, typeof(tagTYPEATTR)); 
                    UnsafeNativeMethods.PtrToStructure(pRefTypeAttr, refTypeAttr);
                    try {
                        Guid g = refTypeAttr.guid;
 
                        // save the guid if we've got one here
                        if (!Guid.Empty.Equals(g)){ 
                            typeData[0] = g; 
                        }
 
                        switch ((NativeMethods.tagTYPEKIND)refTypeAttr.typekind) {

                            case NativeMethods.tagTYPEKIND.TKIND_ENUM:
                                return ProcessTypeInfoEnum(refTypeInfo, structCache); 
                                //return VTToType(tagVT.VT_I4);
                            case NativeMethods.tagTYPEKIND.TKIND_ALIAS: 
                                // recurse here 
                                return GetValueTypeFromTypeDesc(refTypeAttr.Get_tdescAlias(), refTypeInfo, typeData, structCache);
                            case NativeMethods.tagTYPEKIND.TKIND_DISPATCH: 
                                return VTToType(NativeMethods.tagVT.VT_DISPATCH);
                                                        case NativeMethods.tagTYPEKIND.TKIND_INTERFACE:
                                                        case NativeMethods.tagTYPEKIND.TKIND_COCLASS:
                                return VTToType(NativeMethods.tagVT.VT_UNKNOWN); 
                            default:
                                return null; 
                        } 
                    }
                    finally { 
                        refTypeInfo.ReleaseTypeAttr(pRefTypeAttr);
                        structCache.ReleaseStruct(refTypeAttr);
                    }
                } 
            }
            finally { 
                refTypeInfo = null; 
            }
            return null; 
        }

        private static PropertyDescriptor[] InternalGetProperties(Object obj, UnsafeNativeMethods.ITypeInfo typeInfo, int dispidToGet, ref int defaultIndex) {
 
            if (typeInfo == null) {
                return null; 
            } 

            Hashtable propInfos = new Hashtable(); 

            int nameDispID = GetNameDispId((UnsafeNativeMethods.IDispatch)obj);
            bool addAboutBox = false;
 
            StructCache structCache = new StructCache();
 
            // properties can live as functions with get_ and put_ or 
            // as variables, so we do two steps here.
            try { 
                // DO FUNCDESC things
                ProcessFunctions(typeInfo, propInfos, dispidToGet, nameDispID, ref addAboutBox, structCache);
            }
            catch (ExternalException ex) { 
                Debug.Fail("ProcessFunctions failed with hr=" + ex.ErrorCode.ToString(CultureInfo.InvariantCulture) + ", message=" + ex.ToString());
            } 
 
            try {
                // DO VARDESC things. 
                ProcessVariables(typeInfo, propInfos, dispidToGet, nameDispID, structCache);
            }
            catch (ExternalException ex) {
                Debug.Fail("ProcessVariables failed with hr=" + ex.ErrorCode.ToString(CultureInfo.InvariantCulture) + ", message=" + ex.ToString()); 
            }
 
            typeInfo = null; 

 
            // now we take the propertyInfo structures we built up
            // and use them to create the actual descriptors.
            int cProps = propInfos.Count;
 
            if (addAboutBox) {
               cProps++; 
            } 

            PropertyDescriptor[] props = new PropertyDescriptor[cProps]; 
            int defaultProp = -1;

            int hr = NativeMethods.S_OK;
            Object[] pvar = new Object[1]; 
            ComNativeDescriptor cnd = ComNativeDescriptor.Instance;
 
            // for each item in uur list, create the descriptor an check 
            // if it's the default one.
            foreach (PropInfo pi in propInfos.Values){ 
                if (!pi.NonBrowsable) {
                    // finally, for each property, make sure we can get the value
                    // if we can't then we should mark it non-browsable
 
                    try {
                        hr = cnd.GetPropertyValue(obj, pi.DispId, pvar); 
                    } 
                    catch (ExternalException ex) {
                        hr = ex.ErrorCode; 
                        Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "IDispatch::Invoke(PROPGET, " +  pi.Name + ") threw an exception :" + ex.ToString());
                    }
                    if (!NativeMethods.Succeeded(hr)) {
                        Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Adding Browsable(false) to property '" + pi.Name + "' because Invoke(dispid=0x{0:X} ,DISPATCH_PROPERTYGET) returned hr=0x{1:X}.  Properties that do not return S_OK are hidden by default.", pi.DispId, hr)); 
                        pi.Attributes.Add(new BrowsableAttribute(false));
                        pi.NonBrowsable = true; 
                    } 
                }
                else { 
                    hr = NativeMethods.S_OK;
                }

                Attribute[] temp = new Attribute[pi.Attributes.Count]; 
                pi.Attributes.CopyTo(temp, 0);
                //Debug.Assert(pi.nonbrowsable || pi.valueType != null, "Browsable property '" + pi.name + "' has a null type"); 
                props[pi.Index] = new Com2PropertyDescriptor(pi.DispId, pi.Name, temp, pi.ReadOnly != PropInfo.ReadOnlyFalse, pi.ValueType, pi.TypeData, !NativeMethods.Succeeded(hr)); 
                if (pi.IsDefault) {
                    defaultProp = pi.Index; 
                }
            }

            if (addAboutBox) { 
               props[props.Length-1] = new Com2AboutBoxPropertyDescriptor();
            } 
            return props; 
        }
 

        private static PropInfo ProcessDataCore(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispid, int nameDispID, NativeMethods.tagTYPEDESC typeDesc, int flags, StructCache structCache) {
            string          pPropName = null;
            string          pPropDesc = null; 

 
            // get the name and the helpstring 
            int hr = typeInfo.GetDocumentation(dispid, ref pPropName, ref pPropDesc, null, null);
 
            ComNativeDescriptor cnd = ComNativeDescriptor.Instance;


            if (!NativeMethods.Succeeded(hr)) { 
                throw new COMException(SR.GetString(SR.TYPEINFOPROCESSORGetDocumentationFailed, dispid, hr, cnd.GetClassName(typeInfo)), hr);
            } 
 
            if (pPropName == null){
               Debug.Fail(String.Format(CultureInfo.CurrentCulture, "ITypeInfo::GetDocumentation didn't return a name for DISPID 0x{0:X} but returned SUCEEDED(hr),  Component=" + cnd.GetClassName(typeInfo), dispid)); 
               return null;
            }

            // now we can create our struct... make sure we don't already have one 
            PropInfo pi = (PropInfo)propInfoList[pPropName];
 
            if (pi == null) { 
                pi = new PropInfo();
                pi.Index = propInfoList.Count; 
                propInfoList[pPropName] = pi;
                pi.Name = pPropName;
                pi.DispId = dispid;
                pi.Attributes.Add(new DispIdAttribute(pi.DispId)); 
            }
 
            if (pPropDesc != null) { 
                pi.Attributes.Add(new DescriptionAttribute(pPropDesc));
            } 

            // figure out the value type
            if (pi.ValueType == null) {
                Object[] pTypeData = new Object[1]; 
                try {
                    pi.ValueType = GetValueTypeFromTypeDesc(typeDesc, typeInfo, pTypeData, structCache); 
                } 
                catch (Exception ex) {
                    Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Hiding property " + pi.Name + " because value Type could not be resolved: " + ex.ToString()); 
                }

                // if we can't resolve the type, mark the property as nonbrowsable
                // from the browser 
                //
                if (pi.ValueType == null) { 
                    pi.NonBrowsable = true; 
                }
 
                if (pi.NonBrowsable) {
                    flags |= (int)NativeMethods.tagVARFLAGS.VARFLAG_FNONBROWSABLE;
                }
 
                if (pTypeData[0] != null) {
                    pi.TypeData = pTypeData[0]; 
                } 
            }
 
            // check the flags
            if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FREADONLY) != 0) {
                pi.ReadOnly = PropInfo.ReadOnlyTrue;
            } 

            if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FHIDDEN) != 0 || 
                (flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FNONBROWSABLE) != 0 || 
                pi.Name[0] == '_' ||
                dispid == NativeMethods.ActiveX.DISPID_HWND) { 
                pi.Attributes.Add(new BrowsableAttribute(false));
                pi.NonBrowsable = true;
            }
 
            if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FUIDEFAULT) != 0) {
                pi.IsDefault = true; 
            } 

            if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FBINDABLE) != 0 && 
                (flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FDISPLAYBIND) != 0) {
                pi.Attributes.Add(new BindableAttribute(true));
            }
 
            // lastly, if it's DISPID_Name, add the ParenthesizeNameAttribute
            if (dispid == nameDispID){ 
                pi.Attributes.Add(new ParenthesizePropertyNameAttribute(true)); 

                // don't allow merges on the name 
                pi.Attributes.Add(new MergablePropertyAttribute(false));
            }

            return pi; 
        }
 
        private static void ProcessFunctions(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispidToGet, int nameDispID, ref bool addAboutBox, StructCache structCache) { 
            IntPtr pTypeAttr = IntPtr.Zero;
            int hr = typeInfo.GetTypeAttr(ref pTypeAttr); 

            if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) {
                throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr);
            } 

            NativeMethods.tagTYPEATTR         typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR)); 
            UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr); 

            try { 
                if (typeAttr == null) {
                    return;
                }
 
                NativeMethods.tagFUNCDESC       funcDesc = (NativeMethods.tagFUNCDESC)structCache.GetStruct(typeof(NativeMethods.tagFUNCDESC));
                NativeMethods.tagELEMDESC       ed = (NativeMethods.tagELEMDESC)structCache.GetStruct(typeof(NativeMethods.tagELEMDESC)); 
                bool              isPropGet; 
                PropInfo          pi;
 
                for (int i = 0; i < typeAttr.cFuncs; i++) {
                    IntPtr pFuncDesc = IntPtr.Zero;
                    hr = typeInfo.GetFuncDesc(i, ref pFuncDesc);
 
                    if (!NativeMethods.Succeeded(hr) || pFuncDesc == IntPtr.Zero) {
                        Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring function item 0x{0:X} because ITypeInfo::GetFuncDesc returned hr=0x{1:X} or NULL", i, hr)); 
                        continue; 
                    }
 
                    //funcDesc = (tagFUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(tagFUNCDESC));
                    UnsafeNativeMethods.PtrToStructure(pFuncDesc, funcDesc);
                    try {
                        if (funcDesc.invkind == (int)NativeMethods.tagINVOKEKIND.INVOKE_FUNC || 
                            (dispidToGet != NativeMethods.MEMBERID_NIL && funcDesc.memid != dispidToGet)) {
 
                            if (funcDesc.memid == NativeMethods.ActiveX.DISPID_ABOUTBOX) { 
                               addAboutBox = true;
                            } 
                            continue;
                        }

                        NativeMethods.tagTYPEDESC typeDesc; 

                        // is this a get or a put? 
                        isPropGet = (funcDesc.invkind == (int)NativeMethods.tagINVOKEKIND.INVOKE_PROPERTYGET); 

                        if (isPropGet) { 

                            if (funcDesc.cParams != 0) {

                                continue; 
                            }
 
                            typeDesc = funcDesc.elemdescFunc.tdesc; 
                        }
                        else { 
                            Debug.Assert(funcDesc.lprgelemdescParam != IntPtr.Zero, "ELEMDESC param is null!");
                            if (funcDesc.lprgelemdescParam == IntPtr.Zero || funcDesc.cParams != 1) {

                                continue; 
                            }
                            Marshal.PtrToStructure(funcDesc.lprgelemdescParam, ed); 
                            typeDesc = ed.tdesc; 
                        }
                        pi = ProcessDataCore(typeInfo, propInfoList, funcDesc.memid, nameDispID, typeDesc, funcDesc.wFuncFlags, structCache); 

                        // if we got a setmethod, it's not readonly
                        if (pi != null && !isPropGet) {
                            pi.ReadOnly = PropInfo.ReadOnlyFalse; 
                        }
                    } 
                    finally { 
                        typeInfo.ReleaseFuncDesc(pFuncDesc);
                    } 
                }
                structCache.ReleaseStruct(funcDesc);
                structCache.ReleaseStruct(ed);
            } 
            finally {
                typeInfo.ReleaseTypeAttr(pTypeAttr); 
                structCache.ReleaseStruct(typeAttr); 
            }
        } 

        /// 
        /// 
        /// This converts a type info that describes a IDL defined enum 
        /// into one we can use
        ///  
        private static Type ProcessTypeInfoEnum(UnsafeNativeMethods.ITypeInfo enumTypeInfo, StructCache structCache) { 

            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum entered"); 

            if (enumTypeInfo == null) {
                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum got a NULL enumTypeInfo");
                return null; 
            }
 
            try { 
                IntPtr pTypeAttr = IntPtr.Zero;
                int hr = enumTypeInfo.GetTypeAttr(ref pTypeAttr); 

                if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) {
                        throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr);
                } 

                NativeMethods.tagTYPEATTR typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR)); 
                UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr); 

                if (pTypeAttr == IntPtr.Zero) { 
                    Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: failed to get a typeAttr");
                    return null;
                }
 
                try {
 
                    int nItems = typeAttr.cVars; 

                    Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: processing " + nItems.ToString(CultureInfo.InvariantCulture) + " variables"); 

                    ArrayList strs = new ArrayList();
                    ArrayList vars = new ArrayList();
 
                    NativeMethods.tagVARDESC varDesc = (NativeMethods.tagVARDESC)structCache.GetStruct(typeof(NativeMethods.tagVARDESC));
                    Object varValue = null; 
                    string enumName = null; 
                    string name = null;
                    string helpstr = null; 

                    enumTypeInfo.GetDocumentation(NativeMethods.MEMBERID_NIL, ref enumName, ref helpstr, null, null);

                                                            // For each item in the enum type info, 
                    // we just need it's name and value, and helpstring if it's there.
                    // 
                    for (int i = 0; i < nItems; i++) { 
                        IntPtr pVarDesc = IntPtr.Zero;
                        hr = enumTypeInfo.GetVarDesc(i, ref pVarDesc); 

                        if (!NativeMethods.Succeeded(hr) || pVarDesc == IntPtr.Zero) {
                            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring item 0x{0:X} because ITypeInfo::GetVarDesc returned hr=0x{1:X} or NULL", hr));
                            continue; 
                        }
 
                        try { 
                            //varDesc = (tagVARDESC)Marshal.PtrToStructure(pVarDesc, typeof(tagVARDESC));
                            UnsafeNativeMethods.PtrToStructure(pVarDesc, varDesc); 

                            if (varDesc == null ||
                                varDesc.varkind != (int)NativeMethods.tagVARKIND.VAR_CONST ||
                                varDesc.unionMember == IntPtr.Zero) { 
                                continue;
                            } 
 
                            name = helpstr = null;
                            varValue = null; 

                            // get the name and the helpstring

                            hr = enumTypeInfo.GetDocumentation(varDesc.memid,  ref name,  ref helpstr, null, null); 

 
                            if (!NativeMethods.Succeeded(hr)) { 
                                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring item 0x{0:X} because ITypeInfo::GetDocumentation returned hr=0x{1:X} or NULL", hr));
                                continue; 
                            }

                            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum got name=" + (name == null ? "(null)" : name) + ", helpstring=" + (helpstr == null ? "(null)" : helpstr));
 
                            // get the value
                            try { 
                                //varValue = (VARIANT)Marshal.PtrToStructure(varDesc.unionMember, typeof(VARIANT)); 
                                varValue = Marshal.GetObjectForNativeVariant(varDesc.unionMember);
                            } 
                            catch (Exception ex) {
                                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: PtrtoStructFailed " + ex.GetType().Name + "," + ex.Message);
                            }
 
                            /*if (varValue == null) {
                                Debug.Fail("Couldn't get VARIANT from VARIANTDESC"); 
                                continue; 
                            }*/
 
                            //variant v = varValue.ToVariant();
                            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: adding variable value=" + Convert.ToString(varValue, CultureInfo.InvariantCulture));
                            vars.Add(varValue);
 
                            // if we have a helpstring, use it, otherwise use name
                            string nameString; 
                            if (helpstr != null) { 
                                nameString = helpstr;
                            } 
                            else {
                                Debug.Assert(name != null, "No name for VARDESC member, but GetDocumentation returned S_OK!");
                                nameString = name;
                            } 
                            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: adding name value=" + nameString);
                            strs.Add(nameString); 
                        } 
                        finally {
                            if (pVarDesc != IntPtr.Zero) { 
                                enumTypeInfo.ReleaseVarDesc(pVarDesc);
                            }
                        }
                    } 
                    structCache.ReleaseStruct(varDesc);
                    Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: returning enum with " + strs.Count.ToString(CultureInfo.InvariantCulture) + " items"); 
 
                    // just build our enumerator
                    if (strs.Count > 0) { 

                        // get the IUnknown value of the ITypeInfo
                        IntPtr pTypeInfoUnk = Marshal.GetIUnknownForObject(enumTypeInfo);
 
                        try {
                           enumName = pTypeInfoUnk.ToString() + "_" + enumName; 
 
                           if (builtEnums == null) {
                              builtEnums = new Hashtable(); 
                           }
                           else if (builtEnums.ContainsKey(enumName)) {
                              return (Type)builtEnums[enumName];
                           } 

                           Type enumType = typeof(int); 
 
                           if (vars.Count > 0 && vars[0] != null) {
                               enumType = vars[0].GetType(); 
                           }

                           EnumBuilder enumBuilder = ModuleBuilder.DefineEnum(enumName, TypeAttributes.Public, enumType);
                           for (int i = 0; i < strs.Count; i++) { 
                              enumBuilder.DefineLiteral((string)strs[i], vars[i]);
                           } 
                           Type t = enumBuilder.CreateType(); 
                           builtEnums[enumName] = t;
                           return t; 
                        }
                        finally {
                           if (pTypeInfoUnk != IntPtr.Zero) {
                              Marshal.Release(pTypeInfoUnk); 
                           }
                        } 
                    } 

                } 
                finally {
                    enumTypeInfo.ReleaseTypeAttr(pTypeAttr);
                    structCache.ReleaseStruct(typeAttr);
                } 
            }
            catch { 
            } 
            return null;
        } 


        private static void ProcessVariables(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispidToGet, int nameDispID, StructCache structCache) {
            IntPtr pTypeAttr = IntPtr.Zero; 
            int hr = typeInfo.GetTypeAttr(ref pTypeAttr);
 
            if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) { 
                throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr);
            } 

            NativeMethods.tagTYPEATTR typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR));
            UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr);
 
            try {
                if (typeAttr == null) { 
                    return; 
                }
                NativeMethods.tagVARDESC        varDesc = (NativeMethods.tagVARDESC)structCache.GetStruct(typeof(NativeMethods.tagVARDESC)); 

                for (int i = 0; i < typeAttr.cVars; i++) {
                    IntPtr pVarDesc = IntPtr.Zero;
 
                    hr = typeInfo.GetVarDesc(i, ref pVarDesc);
                    if (!NativeMethods.Succeeded(hr) || pVarDesc == IntPtr.Zero) { 
                        Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring variable item 0x{0:X} because ITypeInfo::GetFuncDesc returned hr=0x{1:X} or NULL", hr)); 
                        continue;
                    } 

                    //varDesc = (tagVARDESC)Marshal.PtrToStructure(pVarDesc, typeof(tagVARDESC));
                    UnsafeNativeMethods.PtrToStructure(pVarDesc, varDesc);
 
                    try {
 
                        if (varDesc.varkind == (int)NativeMethods.tagVARKIND.VAR_CONST || 
                            (dispidToGet != NativeMethods.MEMBERID_NIL && varDesc.memid != dispidToGet)) {
                            continue; 
                        }


                        PropInfo pi = ProcessDataCore(typeInfo, propInfoList, varDesc.memid, nameDispID, varDesc.elemdescVar.tdesc, varDesc.wVarFlags, structCache); 
                        if (pi.ReadOnly != PropInfo.ReadOnlyTrue) {
                            pi.ReadOnly = PropInfo.ReadOnlyFalse; 
                        } 
                    }
                    finally { 
                        if (pVarDesc != IntPtr.Zero) {
                            typeInfo.ReleaseVarDesc(pVarDesc);
                        }
                    } 
                }
                structCache.ReleaseStruct(varDesc); 
            } 
            finally {
                typeInfo.ReleaseTypeAttr(pTypeAttr); 
                structCache.ReleaseStruct(typeAttr);
            }
        }
 
        private static Type VTToType(NativeMethods.tagVT vt) {
            switch (vt) { 
            case NativeMethods.tagVT.VT_EMPTY: 
            case NativeMethods.tagVT.VT_NULL:
                return null; 
            case NativeMethods.tagVT.VT_I1:
                return typeof(SByte);
            case NativeMethods.tagVT.VT_UI1:
                return typeof(Byte); 

            case NativeMethods.tagVT.VT_I2: 
                return typeof(Int16); 
            case NativeMethods.tagVT.VT_UI2:
                return typeof(UInt16); 


            case NativeMethods.tagVT.VT_I4:
            case NativeMethods.tagVT.VT_INT: 
                return typeof(Int32);
 
            case NativeMethods.tagVT.VT_UI4: 
            case NativeMethods.tagVT.VT_UINT:
                return typeof(UInt32); 

            case NativeMethods.tagVT.VT_I8:
                return typeof(Int64);
            case NativeMethods.tagVT.VT_UI8: 
                return typeof(UInt64);
 
            case NativeMethods.tagVT.VT_R4: 
                return typeof(float);
 
            case NativeMethods.tagVT.VT_R8:
                return typeof(double);

            case NativeMethods.tagVT.VT_CY: 
                return typeof(Decimal);
            case NativeMethods.tagVT.VT_DATE: 
                return typeof(DateTime); 
            case NativeMethods.tagVT.VT_BSTR:
            case NativeMethods.tagVT.VT_LPSTR: 
            case NativeMethods.tagVT.VT_LPWSTR:
                return typeof(string);

            case NativeMethods.tagVT.VT_DISPATCH: 
                return typeof(UnsafeNativeMethods.IDispatch);
            case NativeMethods.tagVT.VT_UNKNOWN: 
                return typeof(Object); 

            case NativeMethods.tagVT.VT_ERROR: 
            case NativeMethods.tagVT.VT_HRESULT:
                return typeof(int);

            case NativeMethods.tagVT.VT_BOOL: 
                return typeof(bool);
 
            case NativeMethods.tagVT.VT_VARIANT: 
                return typeof(Com2Variant);
            case NativeMethods.tagVT.VT_CLSID: 
                return typeof(Guid);

            case NativeMethods.tagVT.VT_FILETIME:
                return typeof(NativeMethods.FILETIME); 

            case NativeMethods.tagVT.VT_USERDEFINED: 
                throw new ArgumentException(SR.GetString(SR.COM2UnhandledVT, "VT_USERDEFINED")); 

                /*case VT_ENUM: 
                    if (enumNames != null || null != pPropertyInfo.GetEnum()) {
                        return typeof(IEnum);
                    }
                    goto default;*/ 
            case NativeMethods.tagVT.VT_VOID:
            case NativeMethods.tagVT.VT_PTR: 
            case NativeMethods.tagVT.VT_SAFEARRAY: 
            case NativeMethods.tagVT.VT_CARRAY:
 
            case NativeMethods.tagVT.VT_RECORD:
            case NativeMethods.tagVT.VT_BLOB:
            case NativeMethods.tagVT.VT_STREAM:
            case NativeMethods.tagVT.VT_STORAGE: 
            case NativeMethods.tagVT.VT_STREAMED_OBJECT:
            case NativeMethods.tagVT.VT_STORED_OBJECT: 
            case NativeMethods.tagVT.VT_BLOB_OBJECT: 
            case NativeMethods.tagVT.VT_CF:
            case NativeMethods.tagVT.VT_BSTR_BLOB: 
            case NativeMethods.tagVT.VT_VECTOR:
            case NativeMethods.tagVT.VT_ARRAY:
            case NativeMethods.tagVT.VT_BYREF:
            case NativeMethods.tagVT.VT_RESERVED: 
            default:
                throw new ArgumentException(SR.GetString(SR.COM2UnhandledVT, ((int)vt).ToString(CultureInfo.InvariantCulture))); 
            } 
        }
 
        internal class CachedProperties {

            private PropertyDescriptor[] props;
 
            public readonly int MajorVersion;
            public readonly int MinorVersion; 
            private int defaultIndex; 

            internal CachedProperties(PropertyDescriptor[] props, int defIndex, int majVersion, int minVersion) { 
                this.props = ClonePropertyDescriptors(props);
                this.MajorVersion = majVersion;
                this.MinorVersion = minVersion;
                this.defaultIndex = defIndex; 
            }
 
            public PropertyDescriptor[] Properties { 
                get {
                    return ClonePropertyDescriptors(props); 
                }
            }

            public int DefaultIndex { 
                get {
                    return defaultIndex; 
                } 
            }
 
            private PropertyDescriptor[] ClonePropertyDescriptors(PropertyDescriptor[] props) {
                PropertyDescriptor[] retProps = new PropertyDescriptor[props.Length];
                for (int i = 0; i < props.Length; i++) {
                    if (props[i] is ICloneable) { 
                        retProps[i] = (PropertyDescriptor)((ICloneable)props[i]).Clone();;
                    } 
                    else { 
                        retProps[i] = props[i];
                    } 
                }
                return retProps;
            }
        } 

        ///  
        ///  
        /// This class manages a cache of structures that we can use
        /// for passing into native so we don't have to create them every time. 
        /// for many objects, these can be used thousands of times.
        /// 
        public class StructCache {
 
           private Hashtable queuedTypes = new Hashtable();
 
#if DEBUG 
           private Hashtable releaseCheck = new Hashtable();
 
           ~StructCache() {
                IEnumerator enumRelease = releaseCheck.Keys.GetEnumerator();

                while (enumRelease.MoveNext()) { 
                    Type t = (Type)enumRelease.Current;
                    if ((int)releaseCheck[t] != 0) { 
                        Debug.Assert(false, "Failed to release struct of type " + t.Name); 
                    }
                } 
           }

#endif
 
           private Queue GetQueue(Type t, bool create) {
               Object queue = queuedTypes[t]; 
 
               if (queue == null && create){
                  queue = new Queue(); 
                  queuedTypes[t] = queue;
                  #if DEBUG
                    releaseCheck[t] = 0;
                  #endif 
               }
 
               return (Queue)queue; 
           }
 
           public Object GetStruct(Type t) {
               Queue queue = GetQueue(t, true);

               Object str = null; 

               if (queue.Count == 0) { 
                  str = Activator.CreateInstance(t); 
               }
               else { 
                  str = queue.Dequeue();
               }

               #if DEBUG 
                    int count = (int)releaseCheck[t];
                    releaseCheck[t] = ++count; 
               #endif 

               return str; 
           }

           public void ReleaseStruct(Object str) {
               Type t = str.GetType(); 
               Queue queue = GetQueue(t, false);
 
               if (queue != null) { 
                  queue.Enqueue(str);
 
                  #if DEBUG
                    int count = (int)releaseCheck[t];
                    releaseCheck[t] = --count;
                  #endif 
               }
           } 
 
        }
 
        private class PropInfo {

            public const int            ReadOnlyUnknown = 0;
            public const int            ReadOnlyTrue =  1; 
            public const int            ReadOnlyFalse = 2;
 
            string               name = null; 
            int                  dispid = -1;
            Type                 valueType = null; 
            readonly ArrayList   attributes = new ArrayList();
            int                  readOnly = ReadOnlyUnknown;
            bool                 isDefault;
            Object               typeData; 
            bool                 nonbrowsable = false;
            int                  index; 
 
            public string Name {
                get { return name; } 
                set { name = value; }
            }
            public int DispId {
                get { return dispid; } 
                set { dispid = value; }
            } 
            public Type ValueType { 
                get { return valueType; }
                set { valueType = value; } 
            }
            public ArrayList Attributes {
                get { return attributes; }
            } 
            public int ReadOnly {
                get { return readOnly; } 
                set { readOnly = value; } 
            }
            public bool IsDefault { 
                get { return isDefault; }
                set { isDefault = value; }
            }
            public object TypeData { 
                get { return typeData; }
                set { typeData = value; } 
            } 
            public bool NonBrowsable {
                get { return nonbrowsable; } 
                set { nonbrowsable = value; }
            }
            public int Index{
                get {return index;} 
                set {index = value;}
            } 
 

            public override int GetHashCode() { 
                if (name != null) {
                    return name.GetHashCode();
                }
                return base.GetHashCode(); 
            }
        } 
    } 

 
    // just so we can recognize a variant properly...
    /// 
    [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")]
    [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] 
    public class Com2Variant {
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Windows.Forms.ComponentModel.Com2Interop { 
    using System.Runtime.Remoting; 
    using System.Runtime.InteropServices;
    using System.ComponentModel; 
    using System.Diagnostics;
    using System;
    using System.Windows.Forms;
    using System.ComponentModel.Design; 
    using Microsoft.Win32;
    using System.Collections; 
    using Hashtable = System.Collections.Hashtable; 

    using System.Reflection.Emit; 
    using System.Reflection;
    using System.Threading;
    using System.Globalization;
 

    ///  
    ///  
    /// This is the main worker class of Com2 property interop. It takes an IDispatch Object
    /// and translates it's ITypeInfo into Com2PropertyDescriptor objects that are understandable 
    /// by managed code.
    ///
    /// This class only knows how to process things that are natively in the typeinfo.  Other property
    /// information such as IPerPropertyBrowsing is handled elsewhere. 
    /// 
    internal class Com2TypeInfoProcessor { 
 
        #if DEBUG
        private static TraceSwitch DbgTypeInfoProcessorSwitch = new TraceSwitch("DbgTypeInfoProcessor", "Com2TypeInfoProcessor: debug Com2 type info processing"); 
        #else
        private static TraceSwitch DbgTypeInfoProcessorSwitch;
        #endif
 
        private Com2TypeInfoProcessor() {
        } 
 
        private static ModuleBuilder moduleBuilder = null;
 
        private static ModuleBuilder ModuleBuilder {
            get {
               if (moduleBuilder == null) {
                  AppDomain currentDomain =  Thread.GetDomain(); 
                  AssemblyName assemblyName = new AssemblyName();
                  assemblyName.Name = "COM2InteropEmit"; 
                  AssemblyBuilder aBuilder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
                  moduleBuilder = aBuilder.DefineDynamicModule("COM2Interop.Emit");
               } 
               return moduleBuilder;
            }
        }
 
        private static Hashtable builtEnums;
        private static Hashtable processedLibraries; 
 

        ///  
        /// 
        /// Given an Object, this attempts to locate its type ifo
        /// 
        public static UnsafeNativeMethods.ITypeInfo FindTypeInfo(Object obj, bool wantCoClass) { 
            UnsafeNativeMethods.ITypeInfo  pTypeInfo = null;
 
            // this is kind of odd.  What's going on here is that 
            // if we want the CoClass (e.g. for the interface name),
            // we need to look for IProvideClassInfo first, then 
            // look for the typeinfo from the IDispatch.
            // In the case of many OleAut32 operations, the CoClass
            // doesn't have the interface members on it, although
            // in the shell it usually does, so 
            // we need to re-order the lookup if we _actually_ want
            // the CoClass if it's available. 
            // 

            for (int i = 0; pTypeInfo == null && i < 2; i++) { 

                  if (wantCoClass == (i == 0)){
                        if (obj is NativeMethods.IProvideClassInfo) {
                            NativeMethods.IProvideClassInfo pProvideClassInfo = (NativeMethods.IProvideClassInfo)obj; 
                            try {
                                pTypeInfo = pProvideClassInfo.GetClassInfo(); 
                            } 
                            catch {
                            } 
                        }
                  }
                  else {
                       if (obj is UnsafeNativeMethods.IDispatch) { 
                            UnsafeNativeMethods.IDispatch iDispatch = (UnsafeNativeMethods.IDispatch)obj;
                            try { 
                                pTypeInfo = iDispatch.GetTypeInfo(0, SafeNativeMethods.GetThreadLCID()); 
                            }
                            catch { 
                            }
                       }
                  }
 
            }
            return pTypeInfo; 
        } 

 
        /// 
        /// 
        /// Given an Object, this attempts to locate its type info. If it implementes IProvideMultipleClassInfo
        /// all available type infos will be returned, otherwise the primary one will be alled. 
        /// 
        public static UnsafeNativeMethods.ITypeInfo[] FindTypeInfos(Object obj, bool wantCoClass){ 
 
            UnsafeNativeMethods.ITypeInfo[] typeInfos = null;
            int n = 0; 
            UnsafeNativeMethods.ITypeInfo temp = null;

            if (obj is NativeMethods.IProvideMultipleClassInfo) {
               NativeMethods.IProvideMultipleClassInfo pCI = (NativeMethods.IProvideMultipleClassInfo)obj; 
               if (!NativeMethods.Succeeded(pCI.GetMultiTypeInfoCount(ref n)) || n == 0) {
                  n = 0; 
               } 

               if (n > 0){ 
                  typeInfos = new UnsafeNativeMethods.ITypeInfo[n];

                  for (int i = 0; i < n; i++){
                     if (NativeMethods.Failed(pCI.GetInfoOfIndex(i, 1 /*MULTICLASSINFO_GETTYPEINFO*/, ref temp, 0, 0, IntPtr.Zero, IntPtr.Zero))){ 
                        continue;
                     } 
                     Debug.Assert(temp != null, "IProvideMultipleClassInfo::GetInfoOfIndex returned S_OK for ITypeInfo index " + i + ", this is a issue in the object that's being browsed, NOT the property browser."); 
                     typeInfos[i] = temp;
                  } 
               }
            }

            if (typeInfos == null || typeInfos.Length == 0){ 
               temp = FindTypeInfo(obj, wantCoClass);
               if (temp != null) { 
                   typeInfos = new UnsafeNativeMethods.ITypeInfo[]{temp}; 
               }
            } 

            return typeInfos;
        }
 
        /// 
        ///  
        /// Retrieve the dispid of the property that we are to use as the name 
        /// member.  In this case, the grid will put parens around the name.
        ///  
        public static int GetNameDispId(UnsafeNativeMethods.IDispatch obj){
            int dispid = NativeMethods.DISPID_UNKNOWN;
            string[] names = null;
 
            ComNativeDescriptor cnd = ComNativeDescriptor.Instance;
            bool succeeded = false; 
 
            // first try to find one with a valid value
            cnd.GetPropertyValue(obj, "__id", ref succeeded); 

            if (succeeded) {
               names = new string[]{"__id"};
            } 
            else {
               cnd.GetPropertyValue(obj, NativeMethods.ActiveX.DISPID_Name, ref succeeded); 
               if (succeeded) { 
                  dispid = NativeMethods.ActiveX.DISPID_Name;
               } 
               else {
                  cnd.GetPropertyValue(obj, "Name", ref succeeded);
                  if (succeeded) {
                     names = new string[]{"Name"}; 
                  }
               } 
            } 

            // now get the dispid of the one that worked... 
            if (names != null) {
               int[] pDispid = new int[]{NativeMethods.DISPID_UNKNOWN};
               Guid g = Guid.Empty;
               int hr = obj.GetIDsOfNames(ref g, names, 1, SafeNativeMethods.GetThreadLCID(), pDispid); 
               if (NativeMethods.Succeeded(hr)){
 
                  dispid = pDispid[0]; 
               }
            } 

            return dispid;
        }
 

        ///  
        ///  
        /// Gets the properties for a given Com2 Object.  The returned Com2Properties
        /// Object contains the properties and relevant data about them. 
        /// 
        public static Com2Properties GetProperties(Object obj) {

            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties"); 

            if (obj == null || !Marshal.IsComObject(obj)) { 
                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties returning null: Object is not a com Object"); 
                return null;
            } 

            UnsafeNativeMethods.ITypeInfo[] typeInfos = FindTypeInfos(obj, false);

            // oops, looks like this guy doesn't surface any type info 
            // this is okay, so we just say it has no props
            if (typeInfos == null || typeInfos.Length == 0) { 
                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties :: Didn't get typeinfo"); 
                return null;
            } 


            int defaultProp = -1;
            int temp = -1; 
            ArrayList propList = new ArrayList();
            Guid[] typeGuids = new Guid[typeInfos.Length]; 
 
            for (int i = 0; i < typeInfos.Length; i++) {
               UnsafeNativeMethods.ITypeInfo ti = typeInfos[i]; 

               if (ti == null) {
                   continue;
               } 

               int[] versions = new int[2]; 
               Guid typeGuid = GetGuidForTypeInfo(ti, null, versions); 
               PropertyDescriptor[] props = null;
               bool dontProcess = typeGuid != Guid.Empty && processedLibraries != null && processedLibraries.Contains(typeGuid); 

               if (dontProcess) {
                    CachedProperties cp = (CachedProperties)processedLibraries[typeGuid];
 
                    if (versions[0] == cp.MajorVersion && versions[1] == cp.MinorVersion) {
                        props = cp.Properties; 
                        if (i == 0 && cp.DefaultIndex != -1) { 
                            defaultProp = cp.DefaultIndex;
                        } 
                    }
                    else {
                        dontProcess = false;
                    } 
               }
 
               if (!dontProcess) { 
                   props = InternalGetProperties(obj, ti, NativeMethods.MEMBERID_NIL, ref temp);
 
                   // only save the default property from the first type Info
                   if (i == 0 && temp != -1) {
                      defaultProp = temp;
                   } 

                   if (processedLibraries == null) { 
                        processedLibraries = new Hashtable(); 
                   }
 
                   if (typeGuid != Guid.Empty) {
                        processedLibraries[typeGuid] = new CachedProperties(props, i == 0 ? defaultProp : -1, versions[0], versions[1]);
                   }
               } 

               if (props != null){ 
                   propList.AddRange(props); 
               }
            } 

            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties : returning " + propList.Count.ToString(CultureInfo.InvariantCulture) + " properties");

            // done! 
            Com2PropertyDescriptor[] temp2 = new Com2PropertyDescriptor[propList.Count];
            propList.CopyTo(temp2, 0); 
 
            return new Com2Properties(obj, temp2, defaultProp);
        } 

        private static Guid GetGuidForTypeInfo(UnsafeNativeMethods.ITypeInfo typeInfo, StructCache structCache, int[] versions) {
            IntPtr pTypeAttr = IntPtr.Zero;
            int hr = typeInfo.GetTypeAttr(ref pTypeAttr); 
            if (!NativeMethods.Succeeded(hr)) {
                throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); 
            } 

            Guid g = Guid.Empty; 
            NativeMethods.tagTYPEATTR typeAttr = null;
            try {

 
                if (structCache == null) {
                    typeAttr = new NativeMethods.tagTYPEATTR(); 
                } 
                else {
                    typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR)); 
                }
                UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr);
                g = typeAttr.guid;
                if (versions != null) { 
                    versions[0] = typeAttr.wMajorVerNum;
                    versions[1] = typeAttr.wMinorVerNum; 
                } 
            }
            finally { 
                typeInfo.ReleaseTypeAttr(pTypeAttr);
                if (structCache != null && typeAttr != null) {
                    structCache.ReleaseStruct(typeAttr);
                } 
            }
 
            return g; 
        }
 

        /// 
        /// 
        /// Resolves a value type for a property from a TYPEDESC.  Value types can be 
        /// user defined, which and may be aliased into other type infos.  This function
        /// will recusively walk the ITypeInfos to resolve the type to a clr Type. 
        ///  
        private static Type GetValueTypeFromTypeDesc(NativeMethods.tagTYPEDESC typeDesc, UnsafeNativeMethods.ITypeInfo typeInfo, Object[] typeData, StructCache structCache) {
            IntPtr hreftype; 
            int hr = 0;

            switch ((NativeMethods.tagVT)typeDesc.vt) {
            default: 
                return VTToType((NativeMethods.tagVT)typeDesc.vt);
 
            case NativeMethods.tagVT.VT_UNKNOWN: 
            case NativeMethods.tagVT.VT_DISPATCH:
                // get the guid 
                typeData[0] = GetGuidForTypeInfo(typeInfo, structCache, null);

                // return the type
                return VTToType((NativeMethods.tagVT)typeDesc.vt); 

            case NativeMethods.tagVT.VT_USERDEFINED: 
                // we'll need to recurse into a user defined reference typeinfo 
                Debug.Assert(typeDesc.unionMember != IntPtr.Zero, "typeDesc doesn't contain an hreftype!");
                hreftype = typeDesc.unionMember; 
                break;

            case NativeMethods.tagVT.VT_PTR:
                // we'll need to recurse into a user defined reference typeinfo 
                Debug.Assert(typeDesc.unionMember != IntPtr.Zero, "typeDesc doesn't contain an refTypeDesc!");
                NativeMethods.tagTYPEDESC refTypeDesc = (NativeMethods.tagTYPEDESC)structCache.GetStruct(typeof(NativeMethods.tagTYPEDESC)); 
 
                try {
 
                    try {
                        //(tagTYPEDESC)Marshal.PtrToStructure(typeDesc.unionMember, typeof(tagTYPEDESC));
                        UnsafeNativeMethods.PtrToStructure(typeDesc.unionMember, refTypeDesc);
                    } 
                    catch {
                        // above is failing, why? 
                        refTypeDesc = new NativeMethods.tagTYPEDESC(); 
                        refTypeDesc.unionMember = (IntPtr)Marshal.ReadInt32(typeDesc.unionMember);
                        refTypeDesc.vt = Marshal.ReadInt16(typeDesc.unionMember, 4); 
                    }

                    if (refTypeDesc.vt == (int)NativeMethods.tagVT.VT_VARIANT) {
                        return VTToType((NativeMethods.tagVT)refTypeDesc.vt); 
                    }
                    hreftype = refTypeDesc.unionMember; 
                } 
                finally {
                    structCache.ReleaseStruct(refTypeDesc); 
                }
                break;
            }
 
            // get the reference type info
            UnsafeNativeMethods.ITypeInfo refTypeInfo = null; 
 
            hr = typeInfo.GetRefTypeInfo(hreftype, ref refTypeInfo);
            if (!NativeMethods.Succeeded(hr)) { 
                throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetRefTypeInfoFailed, hr), hr);
            }

            try { 
                // here is where we look at the next level type info.
                // if we get an enum, process it, otherwise we will recurse 
                // or get a dispatch. 
                //
                if (refTypeInfo != null) { 
                    IntPtr pRefTypeAttr = IntPtr.Zero;
                    hr = refTypeInfo.GetTypeAttr(ref pRefTypeAttr);

                    if (!NativeMethods.Succeeded(hr)) { 

                        throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); 
                    } 

                    NativeMethods.tagTYPEATTR refTypeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pRefTypeAttr, typeof(tagTYPEATTR)); 
                    UnsafeNativeMethods.PtrToStructure(pRefTypeAttr, refTypeAttr);
                    try {
                        Guid g = refTypeAttr.guid;
 
                        // save the guid if we've got one here
                        if (!Guid.Empty.Equals(g)){ 
                            typeData[0] = g; 
                        }
 
                        switch ((NativeMethods.tagTYPEKIND)refTypeAttr.typekind) {

                            case NativeMethods.tagTYPEKIND.TKIND_ENUM:
                                return ProcessTypeInfoEnum(refTypeInfo, structCache); 
                                //return VTToType(tagVT.VT_I4);
                            case NativeMethods.tagTYPEKIND.TKIND_ALIAS: 
                                // recurse here 
                                return GetValueTypeFromTypeDesc(refTypeAttr.Get_tdescAlias(), refTypeInfo, typeData, structCache);
                            case NativeMethods.tagTYPEKIND.TKIND_DISPATCH: 
                                return VTToType(NativeMethods.tagVT.VT_DISPATCH);
                                                        case NativeMethods.tagTYPEKIND.TKIND_INTERFACE:
                                                        case NativeMethods.tagTYPEKIND.TKIND_COCLASS:
                                return VTToType(NativeMethods.tagVT.VT_UNKNOWN); 
                            default:
                                return null; 
                        } 
                    }
                    finally { 
                        refTypeInfo.ReleaseTypeAttr(pRefTypeAttr);
                        structCache.ReleaseStruct(refTypeAttr);
                    }
                } 
            }
            finally { 
                refTypeInfo = null; 
            }
            return null; 
        }

        private static PropertyDescriptor[] InternalGetProperties(Object obj, UnsafeNativeMethods.ITypeInfo typeInfo, int dispidToGet, ref int defaultIndex) {
 
            if (typeInfo == null) {
                return null; 
            } 

            Hashtable propInfos = new Hashtable(); 

            int nameDispID = GetNameDispId((UnsafeNativeMethods.IDispatch)obj);
            bool addAboutBox = false;
 
            StructCache structCache = new StructCache();
 
            // properties can live as functions with get_ and put_ or 
            // as variables, so we do two steps here.
            try { 
                // DO FUNCDESC things
                ProcessFunctions(typeInfo, propInfos, dispidToGet, nameDispID, ref addAboutBox, structCache);
            }
            catch (ExternalException ex) { 
                Debug.Fail("ProcessFunctions failed with hr=" + ex.ErrorCode.ToString(CultureInfo.InvariantCulture) + ", message=" + ex.ToString());
            } 
 
            try {
                // DO VARDESC things. 
                ProcessVariables(typeInfo, propInfos, dispidToGet, nameDispID, structCache);
            }
            catch (ExternalException ex) {
                Debug.Fail("ProcessVariables failed with hr=" + ex.ErrorCode.ToString(CultureInfo.InvariantCulture) + ", message=" + ex.ToString()); 
            }
 
            typeInfo = null; 

 
            // now we take the propertyInfo structures we built up
            // and use them to create the actual descriptors.
            int cProps = propInfos.Count;
 
            if (addAboutBox) {
               cProps++; 
            } 

            PropertyDescriptor[] props = new PropertyDescriptor[cProps]; 
            int defaultProp = -1;

            int hr = NativeMethods.S_OK;
            Object[] pvar = new Object[1]; 
            ComNativeDescriptor cnd = ComNativeDescriptor.Instance;
 
            // for each item in uur list, create the descriptor an check 
            // if it's the default one.
            foreach (PropInfo pi in propInfos.Values){ 
                if (!pi.NonBrowsable) {
                    // finally, for each property, make sure we can get the value
                    // if we can't then we should mark it non-browsable
 
                    try {
                        hr = cnd.GetPropertyValue(obj, pi.DispId, pvar); 
                    } 
                    catch (ExternalException ex) {
                        hr = ex.ErrorCode; 
                        Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "IDispatch::Invoke(PROPGET, " +  pi.Name + ") threw an exception :" + ex.ToString());
                    }
                    if (!NativeMethods.Succeeded(hr)) {
                        Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Adding Browsable(false) to property '" + pi.Name + "' because Invoke(dispid=0x{0:X} ,DISPATCH_PROPERTYGET) returned hr=0x{1:X}.  Properties that do not return S_OK are hidden by default.", pi.DispId, hr)); 
                        pi.Attributes.Add(new BrowsableAttribute(false));
                        pi.NonBrowsable = true; 
                    } 
                }
                else { 
                    hr = NativeMethods.S_OK;
                }

                Attribute[] temp = new Attribute[pi.Attributes.Count]; 
                pi.Attributes.CopyTo(temp, 0);
                //Debug.Assert(pi.nonbrowsable || pi.valueType != null, "Browsable property '" + pi.name + "' has a null type"); 
                props[pi.Index] = new Com2PropertyDescriptor(pi.DispId, pi.Name, temp, pi.ReadOnly != PropInfo.ReadOnlyFalse, pi.ValueType, pi.TypeData, !NativeMethods.Succeeded(hr)); 
                if (pi.IsDefault) {
                    defaultProp = pi.Index; 
                }
            }

            if (addAboutBox) { 
               props[props.Length-1] = new Com2AboutBoxPropertyDescriptor();
            } 
            return props; 
        }
 

        private static PropInfo ProcessDataCore(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispid, int nameDispID, NativeMethods.tagTYPEDESC typeDesc, int flags, StructCache structCache) {
            string          pPropName = null;
            string          pPropDesc = null; 

 
            // get the name and the helpstring 
            int hr = typeInfo.GetDocumentation(dispid, ref pPropName, ref pPropDesc, null, null);
 
            ComNativeDescriptor cnd = ComNativeDescriptor.Instance;


            if (!NativeMethods.Succeeded(hr)) { 
                throw new COMException(SR.GetString(SR.TYPEINFOPROCESSORGetDocumentationFailed, dispid, hr, cnd.GetClassName(typeInfo)), hr);
            } 
 
            if (pPropName == null){
               Debug.Fail(String.Format(CultureInfo.CurrentCulture, "ITypeInfo::GetDocumentation didn't return a name for DISPID 0x{0:X} but returned SUCEEDED(hr),  Component=" + cnd.GetClassName(typeInfo), dispid)); 
               return null;
            }

            // now we can create our struct... make sure we don't already have one 
            PropInfo pi = (PropInfo)propInfoList[pPropName];
 
            if (pi == null) { 
                pi = new PropInfo();
                pi.Index = propInfoList.Count; 
                propInfoList[pPropName] = pi;
                pi.Name = pPropName;
                pi.DispId = dispid;
                pi.Attributes.Add(new DispIdAttribute(pi.DispId)); 
            }
 
            if (pPropDesc != null) { 
                pi.Attributes.Add(new DescriptionAttribute(pPropDesc));
            } 

            // figure out the value type
            if (pi.ValueType == null) {
                Object[] pTypeData = new Object[1]; 
                try {
                    pi.ValueType = GetValueTypeFromTypeDesc(typeDesc, typeInfo, pTypeData, structCache); 
                } 
                catch (Exception ex) {
                    Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Hiding property " + pi.Name + " because value Type could not be resolved: " + ex.ToString()); 
                }

                // if we can't resolve the type, mark the property as nonbrowsable
                // from the browser 
                //
                if (pi.ValueType == null) { 
                    pi.NonBrowsable = true; 
                }
 
                if (pi.NonBrowsable) {
                    flags |= (int)NativeMethods.tagVARFLAGS.VARFLAG_FNONBROWSABLE;
                }
 
                if (pTypeData[0] != null) {
                    pi.TypeData = pTypeData[0]; 
                } 
            }
 
            // check the flags
            if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FREADONLY) != 0) {
                pi.ReadOnly = PropInfo.ReadOnlyTrue;
            } 

            if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FHIDDEN) != 0 || 
                (flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FNONBROWSABLE) != 0 || 
                pi.Name[0] == '_' ||
                dispid == NativeMethods.ActiveX.DISPID_HWND) { 
                pi.Attributes.Add(new BrowsableAttribute(false));
                pi.NonBrowsable = true;
            }
 
            if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FUIDEFAULT) != 0) {
                pi.IsDefault = true; 
            } 

            if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FBINDABLE) != 0 && 
                (flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FDISPLAYBIND) != 0) {
                pi.Attributes.Add(new BindableAttribute(true));
            }
 
            // lastly, if it's DISPID_Name, add the ParenthesizeNameAttribute
            if (dispid == nameDispID){ 
                pi.Attributes.Add(new ParenthesizePropertyNameAttribute(true)); 

                // don't allow merges on the name 
                pi.Attributes.Add(new MergablePropertyAttribute(false));
            }

            return pi; 
        }
 
        private static void ProcessFunctions(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispidToGet, int nameDispID, ref bool addAboutBox, StructCache structCache) { 
            IntPtr pTypeAttr = IntPtr.Zero;
            int hr = typeInfo.GetTypeAttr(ref pTypeAttr); 

            if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) {
                throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr);
            } 

            NativeMethods.tagTYPEATTR         typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR)); 
            UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr); 

            try { 
                if (typeAttr == null) {
                    return;
                }
 
                NativeMethods.tagFUNCDESC       funcDesc = (NativeMethods.tagFUNCDESC)structCache.GetStruct(typeof(NativeMethods.tagFUNCDESC));
                NativeMethods.tagELEMDESC       ed = (NativeMethods.tagELEMDESC)structCache.GetStruct(typeof(NativeMethods.tagELEMDESC)); 
                bool              isPropGet; 
                PropInfo          pi;
 
                for (int i = 0; i < typeAttr.cFuncs; i++) {
                    IntPtr pFuncDesc = IntPtr.Zero;
                    hr = typeInfo.GetFuncDesc(i, ref pFuncDesc);
 
                    if (!NativeMethods.Succeeded(hr) || pFuncDesc == IntPtr.Zero) {
                        Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring function item 0x{0:X} because ITypeInfo::GetFuncDesc returned hr=0x{1:X} or NULL", i, hr)); 
                        continue; 
                    }
 
                    //funcDesc = (tagFUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(tagFUNCDESC));
                    UnsafeNativeMethods.PtrToStructure(pFuncDesc, funcDesc);
                    try {
                        if (funcDesc.invkind == (int)NativeMethods.tagINVOKEKIND.INVOKE_FUNC || 
                            (dispidToGet != NativeMethods.MEMBERID_NIL && funcDesc.memid != dispidToGet)) {
 
                            if (funcDesc.memid == NativeMethods.ActiveX.DISPID_ABOUTBOX) { 
                               addAboutBox = true;
                            } 
                            continue;
                        }

                        NativeMethods.tagTYPEDESC typeDesc; 

                        // is this a get or a put? 
                        isPropGet = (funcDesc.invkind == (int)NativeMethods.tagINVOKEKIND.INVOKE_PROPERTYGET); 

                        if (isPropGet) { 

                            if (funcDesc.cParams != 0) {

                                continue; 
                            }
 
                            typeDesc = funcDesc.elemdescFunc.tdesc; 
                        }
                        else { 
                            Debug.Assert(funcDesc.lprgelemdescParam != IntPtr.Zero, "ELEMDESC param is null!");
                            if (funcDesc.lprgelemdescParam == IntPtr.Zero || funcDesc.cParams != 1) {

                                continue; 
                            }
                            Marshal.PtrToStructure(funcDesc.lprgelemdescParam, ed); 
                            typeDesc = ed.tdesc; 
                        }
                        pi = ProcessDataCore(typeInfo, propInfoList, funcDesc.memid, nameDispID, typeDesc, funcDesc.wFuncFlags, structCache); 

                        // if we got a setmethod, it's not readonly
                        if (pi != null && !isPropGet) {
                            pi.ReadOnly = PropInfo.ReadOnlyFalse; 
                        }
                    } 
                    finally { 
                        typeInfo.ReleaseFuncDesc(pFuncDesc);
                    } 
                }
                structCache.ReleaseStruct(funcDesc);
                structCache.ReleaseStruct(ed);
            } 
            finally {
                typeInfo.ReleaseTypeAttr(pTypeAttr); 
                structCache.ReleaseStruct(typeAttr); 
            }
        } 

        /// 
        /// 
        /// This converts a type info that describes a IDL defined enum 
        /// into one we can use
        ///  
        private static Type ProcessTypeInfoEnum(UnsafeNativeMethods.ITypeInfo enumTypeInfo, StructCache structCache) { 

            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum entered"); 

            if (enumTypeInfo == null) {
                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum got a NULL enumTypeInfo");
                return null; 
            }
 
            try { 
                IntPtr pTypeAttr = IntPtr.Zero;
                int hr = enumTypeInfo.GetTypeAttr(ref pTypeAttr); 

                if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) {
                        throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr);
                } 

                NativeMethods.tagTYPEATTR typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR)); 
                UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr); 

                if (pTypeAttr == IntPtr.Zero) { 
                    Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: failed to get a typeAttr");
                    return null;
                }
 
                try {
 
                    int nItems = typeAttr.cVars; 

                    Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: processing " + nItems.ToString(CultureInfo.InvariantCulture) + " variables"); 

                    ArrayList strs = new ArrayList();
                    ArrayList vars = new ArrayList();
 
                    NativeMethods.tagVARDESC varDesc = (NativeMethods.tagVARDESC)structCache.GetStruct(typeof(NativeMethods.tagVARDESC));
                    Object varValue = null; 
                    string enumName = null; 
                    string name = null;
                    string helpstr = null; 

                    enumTypeInfo.GetDocumentation(NativeMethods.MEMBERID_NIL, ref enumName, ref helpstr, null, null);

                                                            // For each item in the enum type info, 
                    // we just need it's name and value, and helpstring if it's there.
                    // 
                    for (int i = 0; i < nItems; i++) { 
                        IntPtr pVarDesc = IntPtr.Zero;
                        hr = enumTypeInfo.GetVarDesc(i, ref pVarDesc); 

                        if (!NativeMethods.Succeeded(hr) || pVarDesc == IntPtr.Zero) {
                            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring item 0x{0:X} because ITypeInfo::GetVarDesc returned hr=0x{1:X} or NULL", hr));
                            continue; 
                        }
 
                        try { 
                            //varDesc = (tagVARDESC)Marshal.PtrToStructure(pVarDesc, typeof(tagVARDESC));
                            UnsafeNativeMethods.PtrToStructure(pVarDesc, varDesc); 

                            if (varDesc == null ||
                                varDesc.varkind != (int)NativeMethods.tagVARKIND.VAR_CONST ||
                                varDesc.unionMember == IntPtr.Zero) { 
                                continue;
                            } 
 
                            name = helpstr = null;
                            varValue = null; 

                            // get the name and the helpstring

                            hr = enumTypeInfo.GetDocumentation(varDesc.memid,  ref name,  ref helpstr, null, null); 

 
                            if (!NativeMethods.Succeeded(hr)) { 
                                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring item 0x{0:X} because ITypeInfo::GetDocumentation returned hr=0x{1:X} or NULL", hr));
                                continue; 
                            }

                            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum got name=" + (name == null ? "(null)" : name) + ", helpstring=" + (helpstr == null ? "(null)" : helpstr));
 
                            // get the value
                            try { 
                                //varValue = (VARIANT)Marshal.PtrToStructure(varDesc.unionMember, typeof(VARIANT)); 
                                varValue = Marshal.GetObjectForNativeVariant(varDesc.unionMember);
                            } 
                            catch (Exception ex) {
                                Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: PtrtoStructFailed " + ex.GetType().Name + "," + ex.Message);
                            }
 
                            /*if (varValue == null) {
                                Debug.Fail("Couldn't get VARIANT from VARIANTDESC"); 
                                continue; 
                            }*/
 
                            //variant v = varValue.ToVariant();
                            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: adding variable value=" + Convert.ToString(varValue, CultureInfo.InvariantCulture));
                            vars.Add(varValue);
 
                            // if we have a helpstring, use it, otherwise use name
                            string nameString; 
                            if (helpstr != null) { 
                                nameString = helpstr;
                            } 
                            else {
                                Debug.Assert(name != null, "No name for VARDESC member, but GetDocumentation returned S_OK!");
                                nameString = name;
                            } 
                            Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: adding name value=" + nameString);
                            strs.Add(nameString); 
                        } 
                        finally {
                            if (pVarDesc != IntPtr.Zero) { 
                                enumTypeInfo.ReleaseVarDesc(pVarDesc);
                            }
                        }
                    } 
                    structCache.ReleaseStruct(varDesc);
                    Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: returning enum with " + strs.Count.ToString(CultureInfo.InvariantCulture) + " items"); 
 
                    // just build our enumerator
                    if (strs.Count > 0) { 

                        // get the IUnknown value of the ITypeInfo
                        IntPtr pTypeInfoUnk = Marshal.GetIUnknownForObject(enumTypeInfo);
 
                        try {
                           enumName = pTypeInfoUnk.ToString() + "_" + enumName; 
 
                           if (builtEnums == null) {
                              builtEnums = new Hashtable(); 
                           }
                           else if (builtEnums.ContainsKey(enumName)) {
                              return (Type)builtEnums[enumName];
                           } 

                           Type enumType = typeof(int); 
 
                           if (vars.Count > 0 && vars[0] != null) {
                               enumType = vars[0].GetType(); 
                           }

                           EnumBuilder enumBuilder = ModuleBuilder.DefineEnum(enumName, TypeAttributes.Public, enumType);
                           for (int i = 0; i < strs.Count; i++) { 
                              enumBuilder.DefineLiteral((string)strs[i], vars[i]);
                           } 
                           Type t = enumBuilder.CreateType(); 
                           builtEnums[enumName] = t;
                           return t; 
                        }
                        finally {
                           if (pTypeInfoUnk != IntPtr.Zero) {
                              Marshal.Release(pTypeInfoUnk); 
                           }
                        } 
                    } 

                } 
                finally {
                    enumTypeInfo.ReleaseTypeAttr(pTypeAttr);
                    structCache.ReleaseStruct(typeAttr);
                } 
            }
            catch { 
            } 
            return null;
        } 


        private static void ProcessVariables(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispidToGet, int nameDispID, StructCache structCache) {
            IntPtr pTypeAttr = IntPtr.Zero; 
            int hr = typeInfo.GetTypeAttr(ref pTypeAttr);
 
            if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) { 
                throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr);
            } 

            NativeMethods.tagTYPEATTR typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR));
            UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr);
 
            try {
                if (typeAttr == null) { 
                    return; 
                }
                NativeMethods.tagVARDESC        varDesc = (NativeMethods.tagVARDESC)structCache.GetStruct(typeof(NativeMethods.tagVARDESC)); 

                for (int i = 0; i < typeAttr.cVars; i++) {
                    IntPtr pVarDesc = IntPtr.Zero;
 
                    hr = typeInfo.GetVarDesc(i, ref pVarDesc);
                    if (!NativeMethods.Succeeded(hr) || pVarDesc == IntPtr.Zero) { 
                        Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring variable item 0x{0:X} because ITypeInfo::GetFuncDesc returned hr=0x{1:X} or NULL", hr)); 
                        continue;
                    } 

                    //varDesc = (tagVARDESC)Marshal.PtrToStructure(pVarDesc, typeof(tagVARDESC));
                    UnsafeNativeMethods.PtrToStructure(pVarDesc, varDesc);
 
                    try {
 
                        if (varDesc.varkind == (int)NativeMethods.tagVARKIND.VAR_CONST || 
                            (dispidToGet != NativeMethods.MEMBERID_NIL && varDesc.memid != dispidToGet)) {
                            continue; 
                        }


                        PropInfo pi = ProcessDataCore(typeInfo, propInfoList, varDesc.memid, nameDispID, varDesc.elemdescVar.tdesc, varDesc.wVarFlags, structCache); 
                        if (pi.ReadOnly != PropInfo.ReadOnlyTrue) {
                            pi.ReadOnly = PropInfo.ReadOnlyFalse; 
                        } 
                    }
                    finally { 
                        if (pVarDesc != IntPtr.Zero) {
                            typeInfo.ReleaseVarDesc(pVarDesc);
                        }
                    } 
                }
                structCache.ReleaseStruct(varDesc); 
            } 
            finally {
                typeInfo.ReleaseTypeAttr(pTypeAttr); 
                structCache.ReleaseStruct(typeAttr);
            }
        }
 
        private static Type VTToType(NativeMethods.tagVT vt) {
            switch (vt) { 
            case NativeMethods.tagVT.VT_EMPTY: 
            case NativeMethods.tagVT.VT_NULL:
                return null; 
            case NativeMethods.tagVT.VT_I1:
                return typeof(SByte);
            case NativeMethods.tagVT.VT_UI1:
                return typeof(Byte); 

            case NativeMethods.tagVT.VT_I2: 
                return typeof(Int16); 
            case NativeMethods.tagVT.VT_UI2:
                return typeof(UInt16); 


            case NativeMethods.tagVT.VT_I4:
            case NativeMethods.tagVT.VT_INT: 
                return typeof(Int32);
 
            case NativeMethods.tagVT.VT_UI4: 
            case NativeMethods.tagVT.VT_UINT:
                return typeof(UInt32); 

            case NativeMethods.tagVT.VT_I8:
                return typeof(Int64);
            case NativeMethods.tagVT.VT_UI8: 
                return typeof(UInt64);
 
            case NativeMethods.tagVT.VT_R4: 
                return typeof(float);
 
            case NativeMethods.tagVT.VT_R8:
                return typeof(double);

            case NativeMethods.tagVT.VT_CY: 
                return typeof(Decimal);
            case NativeMethods.tagVT.VT_DATE: 
                return typeof(DateTime); 
            case NativeMethods.tagVT.VT_BSTR:
            case NativeMethods.tagVT.VT_LPSTR: 
            case NativeMethods.tagVT.VT_LPWSTR:
                return typeof(string);

            case NativeMethods.tagVT.VT_DISPATCH: 
                return typeof(UnsafeNativeMethods.IDispatch);
            case NativeMethods.tagVT.VT_UNKNOWN: 
                return typeof(Object); 

            case NativeMethods.tagVT.VT_ERROR: 
            case NativeMethods.tagVT.VT_HRESULT:
                return typeof(int);

            case NativeMethods.tagVT.VT_BOOL: 
                return typeof(bool);
 
            case NativeMethods.tagVT.VT_VARIANT: 
                return typeof(Com2Variant);
            case NativeMethods.tagVT.VT_CLSID: 
                return typeof(Guid);

            case NativeMethods.tagVT.VT_FILETIME:
                return typeof(NativeMethods.FILETIME); 

            case NativeMethods.tagVT.VT_USERDEFINED: 
                throw new ArgumentException(SR.GetString(SR.COM2UnhandledVT, "VT_USERDEFINED")); 

                /*case VT_ENUM: 
                    if (enumNames != null || null != pPropertyInfo.GetEnum()) {
                        return typeof(IEnum);
                    }
                    goto default;*/ 
            case NativeMethods.tagVT.VT_VOID:
            case NativeMethods.tagVT.VT_PTR: 
            case NativeMethods.tagVT.VT_SAFEARRAY: 
            case NativeMethods.tagVT.VT_CARRAY:
 
            case NativeMethods.tagVT.VT_RECORD:
            case NativeMethods.tagVT.VT_BLOB:
            case NativeMethods.tagVT.VT_STREAM:
            case NativeMethods.tagVT.VT_STORAGE: 
            case NativeMethods.tagVT.VT_STREAMED_OBJECT:
            case NativeMethods.tagVT.VT_STORED_OBJECT: 
            case NativeMethods.tagVT.VT_BLOB_OBJECT: 
            case NativeMethods.tagVT.VT_CF:
            case NativeMethods.tagVT.VT_BSTR_BLOB: 
            case NativeMethods.tagVT.VT_VECTOR:
            case NativeMethods.tagVT.VT_ARRAY:
            case NativeMethods.tagVT.VT_BYREF:
            case NativeMethods.tagVT.VT_RESERVED: 
            default:
                throw new ArgumentException(SR.GetString(SR.COM2UnhandledVT, ((int)vt).ToString(CultureInfo.InvariantCulture))); 
            } 
        }
 
        internal class CachedProperties {

            private PropertyDescriptor[] props;
 
            public readonly int MajorVersion;
            public readonly int MinorVersion; 
            private int defaultIndex; 

            internal CachedProperties(PropertyDescriptor[] props, int defIndex, int majVersion, int minVersion) { 
                this.props = ClonePropertyDescriptors(props);
                this.MajorVersion = majVersion;
                this.MinorVersion = minVersion;
                this.defaultIndex = defIndex; 
            }
 
            public PropertyDescriptor[] Properties { 
                get {
                    return ClonePropertyDescriptors(props); 
                }
            }

            public int DefaultIndex { 
                get {
                    return defaultIndex; 
                } 
            }
 
            private PropertyDescriptor[] ClonePropertyDescriptors(PropertyDescriptor[] props) {
                PropertyDescriptor[] retProps = new PropertyDescriptor[props.Length];
                for (int i = 0; i < props.Length; i++) {
                    if (props[i] is ICloneable) { 
                        retProps[i] = (PropertyDescriptor)((ICloneable)props[i]).Clone();;
                    } 
                    else { 
                        retProps[i] = props[i];
                    } 
                }
                return retProps;
            }
        } 

        ///  
        ///  
        /// This class manages a cache of structures that we can use
        /// for passing into native so we don't have to create them every time. 
        /// for many objects, these can be used thousands of times.
        /// 
        public class StructCache {
 
           private Hashtable queuedTypes = new Hashtable();
 
#if DEBUG 
           private Hashtable releaseCheck = new Hashtable();
 
           ~StructCache() {
                IEnumerator enumRelease = releaseCheck.Keys.GetEnumerator();

                while (enumRelease.MoveNext()) { 
                    Type t = (Type)enumRelease.Current;
                    if ((int)releaseCheck[t] != 0) { 
                        Debug.Assert(false, "Failed to release struct of type " + t.Name); 
                    }
                } 
           }

#endif
 
           private Queue GetQueue(Type t, bool create) {
               Object queue = queuedTypes[t]; 
 
               if (queue == null && create){
                  queue = new Queue(); 
                  queuedTypes[t] = queue;
                  #if DEBUG
                    releaseCheck[t] = 0;
                  #endif 
               }
 
               return (Queue)queue; 
           }
 
           public Object GetStruct(Type t) {
               Queue queue = GetQueue(t, true);

               Object str = null; 

               if (queue.Count == 0) { 
                  str = Activator.CreateInstance(t); 
               }
               else { 
                  str = queue.Dequeue();
               }

               #if DEBUG 
                    int count = (int)releaseCheck[t];
                    releaseCheck[t] = ++count; 
               #endif 

               return str; 
           }

           public void ReleaseStruct(Object str) {
               Type t = str.GetType(); 
               Queue queue = GetQueue(t, false);
 
               if (queue != null) { 
                  queue.Enqueue(str);
 
                  #if DEBUG
                    int count = (int)releaseCheck[t];
                    releaseCheck[t] = --count;
                  #endif 
               }
           } 
 
        }
 
        private class PropInfo {

            public const int            ReadOnlyUnknown = 0;
            public const int            ReadOnlyTrue =  1; 
            public const int            ReadOnlyFalse = 2;
 
            string               name = null; 
            int                  dispid = -1;
            Type                 valueType = null; 
            readonly ArrayList   attributes = new ArrayList();
            int                  readOnly = ReadOnlyUnknown;
            bool                 isDefault;
            Object               typeData; 
            bool                 nonbrowsable = false;
            int                  index; 
 
            public string Name {
                get { return name; } 
                set { name = value; }
            }
            public int DispId {
                get { return dispid; } 
                set { dispid = value; }
            } 
            public Type ValueType { 
                get { return valueType; }
                set { valueType = value; } 
            }
            public ArrayList Attributes {
                get { return attributes; }
            } 
            public int ReadOnly {
                get { return readOnly; } 
                set { readOnly = value; } 
            }
            public bool IsDefault { 
                get { return isDefault; }
                set { isDefault = value; }
            }
            public object TypeData { 
                get { return typeData; }
                set { typeData = value; } 
            } 
            public bool NonBrowsable {
                get { return nonbrowsable; } 
                set { nonbrowsable = value; }
            }
            public int Index{
                get {return index;} 
                set {index = value;}
            } 
 

            public override int GetHashCode() { 
                if (name != null) {
                    return name.GetHashCode();
                }
                return base.GetHashCode(); 
            }
        } 
    } 

 
    // just so we can recognize a variant properly...
    /// 
    [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")]
    [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] 
    public class Com2Variant {
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK